image

實在有點懶得用中文來寫了… 讓我用英文寫這篇吧…

Before this..

I am learning about golang on server backend side programming via martini. I want to write a RESTful server communicate via JSON. Currently my framework as follow:

Here is the note about how I add martini-secure HTTPS in martini.

Working step with martini-secure

1. Create a SSL key using generate_cert.go

You need create your localhost SSL key (or purchase one from CA) using generate_cert.go. I am not sure how to get this package, so I just download this source code and run it locally.

go run generate_cert.go --host="localhost"

You will get file “key.pem” and “cert.pem”, just put in your web side source code.

2. Apply martini-secure in martini program.

It is very easy to add SSL as a plugin in martini, here is some code. At first I would like to host two services as follow:

  • HTTP: port 8080
  • HTTPS: port 8443
m := martini.Classic()

// Make sure this enable, or it will get failed.
martini.Env = martini.Prod

    
m.Use(secure.Secure(secure.Options{
	SSLRedirect: false,
	SSLHost:     "localhost:8443",
}))

m.Get("/foo", func() string {
	return "bar"
})

go func() {
	if err := http.ListenAndServe(":8080", m); err != nil {
		fmt.Println(err)
	}
}()

// HTTPS, make sure "cert.pem" and "key.pem" files are exist.
if err := http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", m); err != nil {
	fmt.Println(err)
}

3. Write a client to verity it.

If it build with luck, let’s verify with some simple client code as follow:

  • TLSClientConfig: true : Because we use custom SSL key, not signed by CA.
package main

import (
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
)

func HttpsVerity(address string) {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}

	client := &http.Client{Transport: tr}

	response, err := client.Get(address)
	if err != nil {
		fmt.Printf("%s", err)
		os.Exit(1)
	} else {
		defer response.Body.Close()
		contents, err := ioutil.ReadAll(response.Body)
		if err != nil {
			fmt.Printf("%s", err)
			os.Exit(1)
		}
		fmt.Printf("%s\n", string(contents))
	}
}

func HttpVerity(address string) {
	client := &http.Client{}

	response, err := client.Get(address)
	if err != nil {
		fmt.Printf("%s", err)
		os.Exit(1)
	} else {
		defer response.Body.Close()
		contents, err := ioutil.ReadAll(response.Body)
		if err != nil {
			fmt.Printf("%s", err)
			os.Exit(1)
		}
		fmt.Printf("%s\n", string(contents))
	}
}

func main() {
	fmt.Println("HTTP")
	HttpVerity("http://localhost:8080/foo")
	fmt.Println("HTTPS")
	HttpsVerity("https://localhost:8443/foo")
}

Problems you might occur

tls: oversized record received with length 20527

  • Root cause:
    • Server side SSL configuration failed, might related to key files.
  • Solution:
    • You forget to add “http.ListenAndServeTLS” in your server side also check SSL key files if missing.

Cannot connect original HTTP protocol

  • Root cause:
    • martini-secure default enable SSL redirect, so original HTTP protocol will redirect to HTTPS.
  • Solution:
    • Add following code in your server side.
m.Use(secure.Secure(secure.Options{
	SSLRedirect: false,
}))               	

Show your web side is not secure when using web browser

  • Root cause:
    • We are using custom SSL key which don’t signed by CA. So it will show error when you using browser to browse your server.
  • Solution:
    • Buy a offcial SSL key.

How to deploy HTTPS on heroku using martini

It is very easy to deploy HTTPS on Heroku, using SSL Endpoint. All you need as follow:

  • Enable SSL Endpoint in Heroku ($20/per month)
  • Purchase a valid srt from a legal CA, and apply to Heroku.
  • Assign your domain name to HTTPS host (such as XXXX..herokussl.com)
  • Your application just handle HTTP request as well.
  • And.. That’s done. :)

Hope you can enjoy martini as well.


Evan

Attitude is everything