Quantcast
Channel: 小惡魔 – 電腦技術 – 工作筆記 – AppleBOY
Viewing all articles
Browse latest Browse all 325

在 Go 語言用一行程式碼自動化安裝且更新 Let’s Encrypt 憑證

$
0
0
Go-brown-side.sh 在去年寫了一篇『申請 Let’s Encrypt 免費憑證讓網站支援 HTTP2』教學,如果您是用 Nginx,就可以參考該篇教學讓您的伺服器支援 HTTPS,而 Google Security Blog 也宣布在 56 版本以後將會提示 non-secure 網站,讓使用者可以選擇性瀏覽網站。Let’s Encrypt 官方也公布去年 2016 發了多少張憑證,相當驚人,想必大家對 HTTPS 已經有相當程度的瞭解。底下這張圖說明 2016 年 Let’s Encrypt 發憑證總量的狀況 Screen Shot 2017-04-07 at 9.52.40 AM 此篇會介紹在 Go 語言如何跟 Let’s Encrypt 串接,底下有兩種方式。

使用 Caddy 伺服器

大家可以把 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 語言 autocert package

相信很多 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 就可以綁定憑證及自動更新 (不必擔心三個月會過期),這邊你會問,那憑證是放在哪裡呢?
  • MacOS 放在 /Users/xxxx/Library/Caches/golang-autocert
  • Windows ${HOMEDRIVE}/${HOMEPATH} 加上依序先找 APPDATA, CSIDL_APPDATA, TEMP, TMP 全域變數的目錄
  • Linux 放在家目錄內 .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 的好處。

Viewing all articles
Browse latest Browse all 325