Server 翻新日誌 (1) ─ NginxProxyManager 與 Wildcard Certificate

回想起來我的自架歷程應該就是從今年年初個人網站開始的吧,到現在也快要整整一年了。在前幾天發現了 NginxProxyManager 決定把 server 上亂糟糟的服務們整理一下。剛好最近沒有忙到不可開交的地步,就在這幾天把整個 server 翻新了一遍,也把個人網站搬到 server 上了。趁著還有這個興致,決定把整個過程記錄一下。

翻新前的 Server 概況

我的個人網站之前一直都是使用 Hugo 生成靜態網頁後直接丟到 GitHub Pages 上的,server 的部分是直到暑假大家想一起玩 minecraft 的時候才把家裡快 10 歲的桌機翻了出來,申請固定 IP 然後上了 Ubuntu 才成了現在這個樣子。而在這之後又陸陸續續放了許多服務,像是 code-server、課表網站、短網址等。

之前一直都是自己掛一個 Nginx Reverse Proxy,有新的服務就直接走一遍下面的流程:

  1. 跑去 Domain Registrar 加一個 subdomain A record 指到同一台 server
  2. 用 LetsEncrypt 申請 SSL
  3. 在 Nginx conf 開個新的 server block 然後指到特定的 port

其實說繁瑣也沒到那麼繁瑣,但有時候 conf 不小心寫錯或忘了哪個步驟又會讓我吃 502 server error 跟白花許多時間 debug。而這邊用 NginxProxyManager 就能幫我們解決這些問題。

NginxProxyManager

前幾天剛好在剛好在 r/selfhosted 看到有人分享,其實真的挺讚的,可以讓你用圖形化介面管理所有 Proxy、SSL 憑證,也可以設定 404 自動重新導向跟 HTTP simple auth,總之就能省去許多上面要手動處理的東西。

安裝基本上挺簡單的,先把 Host 的 Nginx 關了:

sudo systemctl stop nginx

接著用他提供的 docker-compose.yml 直接 docker-compose up -d,就能直接訪問 http://[ip]:81 進到管理介面。

看到網址是 http 就不舒服。要用 SSL 就先登入管理介面,然後新增一個 proxy host:

Save 完之後把 docker-compose.yml 裡的 port forwarding 拿掉重開後到 my-admin-url.yagami.dev 就會發現有 SSL 了。

接著就可以用同樣的方式新增你所有服務的 reverse proxy。

值得注意的是,像我之前是 proxy_passlocalhost:[port],但由於 Nginx Reverse Proxy 是開在 container 裡面的,所以在 Forward Hostname / IP 填 localhost 會是 container 的 localhost 而不是 host machine 的。這裡有兩個解決方法:

  1. 把 host.docker.internal 加到 extra_hosts 當作 hostname
  2. 用 external docker network

第一種方法我嘗試了好一陣子,但實在是弄不起來就放棄了,改用官方建議的第二種作法(詳細可以自己去看 官方文件 )。然後不要跟我一樣把 hostname 填成 network 名字,找超久有夠白痴的。

Proxy host 加完基本上就大致完成了,不會再有一堆奇奇怪怪的 port forwarding,全部在 docker network 內完成,對外就開放 80 跟 443 兩個 port。

Wildcard Domain

後來又持續玩了玩他的設定,發現他有個功能是如果有人訪問了沒有對應 proxy 的 subdomain 就給他一個 404,就設起來玩玩看。這才想到我其實可以用 wildcard subdomain 啊,之前怎麼沒想到...

於是就跑去我的 registrar 那邊加了一個 wildcard A record 指到伺服器,卻發現出來的不是 404,而是「您的連線是不安全的......」,告訴我沒有 SSL。查了一會兒才發現 LetsEncrypt 居然有發 wildcard subdomain 的 certificate!然後就遇到了另一個大難題:DNS Challenge。

用 LetsEncrypt 簽 SSL 的時候,基本上只要 domain 的 IP 有指到對應的機器他就會發給你。但 wildcard certificate 比較嚴格,需要做 DNS Challenge 才給發。DNS Challenge 簡單來說就是 certbot 會用你給他的 credentials 在你的 DNS records 裡面加一些有的沒的,確認這 domain 真的是你才會發給你。

這真的超頭痛,因為我 Domain 是在 Namecheap 註冊的,但 Namecheap 的 certbot plugin 好像沒人在維護。於是我還特地把 domain name 搬到 Google Domains..... 然後我就發現我是白痴。因為其實也不用搬,只要把 nameserver 改成 Google Cloud DNS 然後用他管理就好,不需要換 registrar。我是智障。

總之要能做 DNS Challenge 的步驟就是:

  1. 把 nameserver 改成某個支援的 DNS 代管,在此我選用 Google Cloud DNS
  2. 創一個 Service Account 然後拿到 credentials
  3. 申請 Certificate

總之,經過百般折騰,終於被我弄出了 wildcard certificate,同樣掛在 NginxProxyManager 上。因此現在你在網址列打 [anything].yagami.dev 應該都會出現有 https 的 404 頁面 :)

總結

架服務真的會上癮,雖然 debug 很麻煩,但東西架起來之後真的超爽的啦。如果下禮拜有空再接著寫下一篇吧。