在去年寫了一篇『申請 Let’s Encrypt 免費憑證讓網站支援 HTTP2』教學,如果您是用 Nginx,就可以參考該篇教學讓您的伺服器支援 HTTPS,而 Google Security Blog 也宣布在 56 版本以後將會提示 non-secure 網站,讓使用者可以選擇性瀏覽網站。Let’s Encrypt 官方也公布去年 2016 發了多少張憑證,相當驚人,想必大家對 HTTPS 已經有相當程度的瞭解。底下這張圖說明 2016 年 Let’s Encrypt 發憑證總量的狀況
此篇會介紹在 Go 語言如何跟 Let’s Encrypt 串接,底下有兩種方式。
大家可以把 Caddy 想成跟 Nginx 同等關係,不同的是 Caddy 是一套完全用 Go 語言打造的伺服器,這邊就會介紹 Caddy 怎麼設定 HTTPS 憑證。先假設 domain 為 example.com,底下就是讓此 domain 自動掛上憑證的 Caddyfile
設定檔
example.com {
proxy / localhost:3000
}
上面設定會自動將 http
轉換成 https
,也就是在瀏覽器鍵入 http://example.com Caddy 會自動換成 https://example.com。如果是 Nginx 呢?看看底下設定
server {
listen 0.0.0.0:80;
server_name example.com;
location /.well-known/acme-challenge/ {
alias /var/www/dehydrated/;
}
return 301 https://example.com$request_uri;
}
server {
# listen 80 deferred; # for Linux
# listen 80 accept_filter=httpready; # for FreeBSD
listen 0.0.0.0:443 ssl http2;
ssl_certificate /etc/dehydrated/certs/example.com/fullchain.pem;
ssl_certificate_key /etc/dehydrated/certs/example.com/privkey.pem;
# The host name to respond to
server_name codeigniter.org.tw;
}
很明顯可以看出,用 Caddy 大勝 Nginx 設定檔簡易程度。另外 Let’s Encrypt 憑證會在三個月後過期,如果是使用 Caddy,可以不用擔心過期問題,Caddy 會自動在三個月內幫忙更新憑證有效日期,如果是 Nginx,請寫 Script 並且放到 Crontab 內。結論就是 Caddy 自動幫忙處理申請 + 更新憑證,而 Nginx 都必須手動打造。
相信很多 Go 開發者不希望前面有 Proxy Server 像是 Caddy 或 Nginx,而是希望 Go Binary 可以直接 Listen 443 port,這樣好處就是 Performance 會是最好,缺點就是一台機器只能使用一個 application。而 Go 語言要實現整合 Let’s Encrypt 相當容易,只需要引入 autocert 套件,底下是範例程式碼: (使用 Gin framework)
package main
import (
"crypto/tls"
"net/http"
"github.com/gin-gonic/gin"
"golang.org/x/crypto/acme/autocert"
)
func main() {
r := gin.Default()
// Ping handler
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
m := autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"),
Cache: autocert.DirCache("/var/www/.cache"),
}
s := &http.Server{
Addr: ":https",
TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
Handler: r,
}
s.ListenAndServeTLS("", "")
}
程式碼內有兩個地方需要注意,一個是 HostWhitelist
這是綁定特定 Domain,請務必填寫,當然你也可以填空,但是這樣任意 Domain 指向你的機器,就可以直接對 Let’s Encrypt 請求憑證,所以請務必填上自己的 Domain,另一個是 DirCache
這是憑證存放的目錄,你可以任意指定到其他目錄,第一次請求會比較久,原因是憑證還沒下來。上面範例你會發現,哪是一行,看起來就是好幾行才完成此功能,在不久之前 (本週) @bradfitz (Go 語言 HTTP 核心開發者) 開發了 Listener
函示 (相關 Commit),讓開發者可以用一行取代上面冗長的程式碼:
package main
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
"golang.org/x/crypto/acme/autocert"
)
func main() {
r := gin.Default()
// Ping handler
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
log.Fatal(http.Serve(autocert.NewListener("example1.com", "example2.com"), r))
}
現在只要填入您的 Domain 就可以綁定憑證及自動更新 (不必擔心三個月會過期),這邊你會問,那憑證是放在哪裡呢?
/Users/xxxx/Library/Caches/golang-autocert
${HOMEDRIVE}/${HOMEPATH}
加上依序先找 APPDATA
, CSIDL_APPDATA
, TEMP
, TMP
全域變數的目錄.cache/golang-autocert
目錄如果是搭配 Docker,你可以指定 XDG_CACHE_HOME
變數來轉換您想要的目錄。請在 Dockerfile 內放入
ENV XDG_CACHE_HOME /var/lib/.cache
這樣 docker 會把憑證放在 /var/lib/.cache/golang-autocert
內,使用者不用管憑證放哪裡,因為就算消失了,下次重新啟動,自然會在產生一次。最後要講的是,如何用 Go 語言實現 http
轉到 https
呢?非常簡單,請參考底下程式碼
var g errgroup.Group
g.Go(func() error {
return http.ListenAndServe(":http", http.RedirectHandler("https://example.com", 303))
})
g.Go(func() error {
return http.Serve(autocert.NewListener("example.com"), handler)
})
if err := g.Wait(); err != nil {
log.Fatal(err)
}
不管是用 Caddy 或者是 Go 語言 autocert 套件都是非常簡單,如果寫 Open Source 專案,基本上就是用 Go 語言內建的 autocert 來實現 Let’s Encrypt 串接,方便大家下載 Binary 直接 Listen 443 Port。不熟 Go 語言,就試試看 Caddy 伺服器吧。相信跑過一陣子你會發現 Caddy 的好處。