【原创】切换你的Go网站http至https并免费获取证书

基于ACME协议的证书自动签发体系

作者: koangel 发表于: 2017-10-27 20:45:48

为什么要给使用Https

一般我们使用GO编写网站时,习惯于使用标准的http协议,实际上http早已被各大厂商诟病,其中不够安全是其主要诟病的因素,当然我们的项目在后台GMT以及自用系统时也未注意到该问题,直到海外阿里云被大面积扫描,并最终暴露出一些安全因素才让我们下定决心改用https防止更多安全问题发生。

关键字:为什么使用HTTPS

给你的Go网站增加自动证书体系

早期都知道证书签发其实是一门非常赚钱的生意,因为太贵了(公司早期买证书最便宜的要4000大洋,简直抢劫啊),但是由于安全问题越来越严重,所以有了免费的证书签发机构:Let's EncryptLet's Encrypt实在非常方便,可以自己使用certbot来签发证书至nginx,但是由于我们使用的是beegomartinigo语言框架,certbot使用时非常麻烦,实际上go语言自身就支持自动签发证书体系,以下将以beego以及martini为例抛砖引玉的说明一下自动证书签发的技巧。

安装ACME库

go get -u golang.org/x/crypto

没错这里要使用crypto库,实际上是要使用其中的acme库。

普通http服务器下的自动签发

在默认的https服务中可以很方便的使用代码的自动签发体系,当然你还要对http的处理做一些小手术,让他自动跳转到https的网站去。

    func ListenHttp() {
            mux := &http.ServeMux{}
            mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
            newURI := "https://" + r.Host + r.URL.String()
            http.Redirect(w, r, newURI, http.StatusFound)
            })
            go http.ListenAndServe(":80", mux) // 开启80 并自动跳转
    }

此处设置后,当你去访问该域名下任何http请求时都会跳转到https

那么接下来我们要开始监听https并实现一个最简易的自动签发体系了。

func ListenHttps(handler http.Handler) error {
    hostPolicy := func(ctx context.Context, host string) error {
        // 以grapec.me为例,检测域名是否符合规范。
        allowedHost := "grapec.me"
        if strings.Contains(host, allowedHost) {
            return nil
        }

        return fmt.Errorf("acme/autocert: only %s host is allowed", allowedHost)
    }

    // 设置存放本地证书的路径 
    dataDir := "./certs"
    autoCertm = &autocert.Manager{
        Prompt:     autocert.AcceptTOS,
        HostPolicy: hostPolicy,
        Cache:      autocert.DirCache(dataDir), // 启动当证书保存后的缓存系统。
    }

    // 开启https监听并自动签发证书
    httpsSrv = &http.Server{
        Addr:         ":https",
        ReadTimeout:  30 * time.Second,
        WriteTimeout: 30 * time.Second,
        IdleTimeout:  120 * time.Second,
        Handler:      handler,
        // 重新设置TLS的设置,当需要获取证书时首次访问会去Let's Encrypt申请证书
        TLSConfig:    &tls.Config{GetCertificate: autoCertm.GetCertificate},
    }

    // 如果你需要默认的cert.pem或key.pem请在下方设置,无需则默认为空。
    return httpsSrv.ListenAndServeTLS("", "")
}

通过以上代码会自动监听443并开启acme协议下的自动证书签发系统,至此默认情况下的自动证书签发已完成,那么接下来结合beego以及martini来改进自己的网站吧。

beego中使用自动证书签发

我们后台GMT使用的是beego框架,那么我们开始针对beego开始切换。

var (
    autoCertm *autocert.Manager = nil
)

func main() {
    // 必须关闭beego自身的http监听,否则会出现无法监听的情况。
    beego.BConfig.Listen.EnableHTTP = false

    // 开启默认的https
    beego.BConfig.Listen.EnableHTTPS = true
    beego.BConfig.Listen.HTTPSPort = 443
    beego.BConfig.Listen.HTTPSCertFile = ""
    beego.BConfig.Listen.HTTPSKeyFile = ""

    hostPolicy := func(ctx context.Context, host string) error {
        // Note: change to your real host
        allowedHost := "grapec.me"
        if strings.Contains(host, allowedHost) {
            return nil
        }

        return fmt.Errorf("acme/autocert: only %s host is allowed", allowedHost)
    }

    dataDir := "./certs"
    autoCertm = &autocert.Manager{
        Prompt:     autocert.AcceptTOS,
        HostPolicy: hostPolicy,
        Cache:      autocert.DirCache(dataDir),
    }

    beego.BeeApp.Server.TLSConfig = &tls.Config{GetCertificate: autoCertm.GetCertificate}

    // 监听一个默认的http 
    go ListenHttp() 
    // 启动443监听
    beego.Run()
}

以上完成beego的自动证书获取也就搞定了,赶紧去试试吧。

martini中使用自动签发

我们基于martini或macaron来改进自己的网站系统。

    //首先创建经典的classic
    m := martini.Classic()
    // 创建默认的证书目录
    os.Mkdir("./certs", 0644)

    // 中间省略自己设置的相关代码
    ...

    // 使用以上函数来完成监听
    go ListenHttp() 
    // 将m.Run()替换为
    log.Error(ListenHttps(m))

以上代码请勿直接复制粘贴,根据实际情况来改进代码,灵活使用。