The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

如何在 Ubuntu 22.04.5 LTS 上安裝和配置 Cloudflare Tunnel 服務

最近幫同事建立了一台測試機,我使用 Cloudflare Tunnel 提供客戶測試網址,這樣就不需要處理防火牆的種種瑣事,不但減少了許多管理上的麻煩,透過 Cloudflare 的平臺檔在前面也相對安全許多。這篇文章會記錄一下我在 Ubuntu 22.04.5 LTS 上安裝和配置 Cloudflare Tunnel 的過程。

Cloudflare Tunnel and Ubuntu

安裝設定 Cloudflare Tunnel 步驟

  1. 安裝 Cloudflare Tunnel 命令列工具

    wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
    sudo dpkg -i cloudflared-linux-amd64.deb
    
  2. 登入 Cloudflare 帳號

    這邊我假設我的 Ubuntu 登入帳號為 test1,並且已經在 Cloudflare 上建立了一個帳號(account),如果你還沒有 Cloudflare 帳號,可以到這裡建立一個帳號。

    cloudflared tunnel login
    

    接著出現以下的提示,請點擊網址開啟瀏覽器並使用你的 Cloudflare 帳號登入:

    Please open the following URL and log in with your Cloudflare account:
    
    https://dash.cloudflare.com/argotunnel?aud=&callback=https%3A%2F%2Flogin.cloudflareaccess.org%2FxxxxGASfxuYgpAxxxxGA3y4Y3QHKATnxxxxxKjQeUE%3D
    
    Leave cloudflared running to download the cert automatically.
    

    登入後會出現 Authorize Cloudflare Tunnel 的網頁,並顯示所有你有權限管理的 Domain Name 清單,請選擇你要使用的網域,然後點擊 Authorize 按鈕。

    登入並授權之後,它會建立一把代表你 Cloudflare 帳號下某一個「域名」的憑證檔: /home/test1/.cloudflared/cert.pem

  3. 建立 Cloudflare Tunnel

    tunnel_name="dev-test1"
    
    # 建立通道
    cloudflared tunnel create $tunnel_name
    
    # 建立 DNS 記錄
    cloudflared tunnel route dns $tunnel_name $tunnel_name-80
    cloudflared tunnel route dns $tunnel_name $tunnel_name-443
    cloudflared tunnel route dns $tunnel_name $tunnel_name-3000
    
    # 取得通道 ID
    tunnel_id=$(cloudflared tunnel info $tunnel_name | grep -oP 'Your tunnel \K([a-z0-9-]+)')
    
    # 建立 cloudflared 設定檔
    cat <<EOF > ~/.cloudflared/config.yml
    tunnel: $tunnel_name
    credentials-file: /home/test1/.cloudflared/$tunnel_id.json
    
    ingress:
      - hostname: $tunnel_name-80.miniasp.com
        service: http://localhost:80
      - hostname: $tunnel_name-443.miniasp.com
        service: http://localhost:443
      - hostname: $tunnel_name-3000.miniasp.com
        service: http://localhost:3000
      - service: http_status:404
    EOF
    

    上述命令可以直接建立一個通道、三個 DNS 記錄 (CNAME),並且用三個域名對應到本機的三個 Ports

    Tunnel credentials written to /home/test1/.cloudflared/a010e7d5-c830-4baa-ac8a-9e67843586c7.json. cloudflared chose this file based on where your origin certificate was found. Keep this file secret. To revoke these credentials, delete the tunnel.
    
    Created tunnel dev-test1 with id a010e7d5-c830-4baa-ac8a-9e67843586c7
    2025-04-09T06:12:18Z INF Added CNAME dev-test1-80.miniasp.com which will route to this tunnel tunnelID=a010e7d5-c830-4baa-ac8a-9e67843586c7
    2025-04-09T06:12:21Z INF Added CNAME dev-test1-443.miniasp.com which will route to this tunnel tunnelID=a010e7d5-c830-4baa-ac8a-9e67843586c7
    2025-04-09T06:12:23Z INF Added CNAME dev-test1-3000.miniasp.com which will route to this tunnel tunnelID=a010e7d5-c830-4baa-ac8a-9e67843586c7
    
  4. 啟動 Cloudflare Tunnel

    由於我是用一般使用者身分執行,若要持續運行 Cloudflare Tunnel,要使用 nohup 指令來執行,這樣就不會因為關閉終端機而中斷連線。

    nohup cloudflared tunnel run &
    

    如果你想跑在背景服務,請先 sudoroot 身分,然後執行以下命令:

    cat << EOF > /etc/systemd/system/cloudflared.service
    [Unit]
    Description=Cloudflared Tunnel Service
    After=network.target
    
    [Service]
    ExecStart=/usr/local/bin/cloudflared tunnel run
    User=test1
    Group=test1
    Restart=always
    RestartSec=5
    
    [Install]
    WantedBy=multi-user.target
    EOF
    
    # 設定服務文件的權限
    chmod 644 /etc/systemd/system/cloudflared.service
    
    # 重新載入 systemd 配置
    systemctl daemon-reload
    
    # 啟動服務
    systemctl start cloudflared
    
    # 啟用服務(開機自動啟動)
    systemctl enable cloudflared
    
    # 查看服務狀態
    systemctl status cloudflared
    
  5. 如果你還沒有網頁可以連線,我會用以下方法快速提供一個網頁供測試之用!

    sudo npm i -g rimraf serve
    
    # 建立一個測試用的網頁
    cat <<EOF > index.html
    <h1>Hi</h1>
    EOF
    
    # 建立本機 HTTP 伺服器
    serve -l 3000 &> /dev/null &
    curl -v http://localhost:30016
    
    # 查看正在運行的 cloudflared
    ps -ef | grep cloudflared
    
    # 從 Cloudflare 網路測試連線
    curl -v https://dev-test1-3000.miniasp.com/
    

刪除所有 Cloudflare Tunnel 的步驟

  1. 列出所有已經建立的 Tunnel 清單

    cloudflared tunnel list
    

    假設你的 Tunnel 名稱為 dev-test1 的話,可以用以下命令查看目前是否有任何既有的通道連線

    cloudflared tunnel info dev-test1
    

    只要有顯示資料,就代表目前的 Tunnel 正在運作中,顯示結果大概會長這樣:

    test1@myvm:~$ cloudflared tunnel info dev-test1
    NAME:     dev-test1
    ID:       a010e7d5-c830-4baa-ac8a-9e67843586c7
    CREATED:  2025-04-08 18:52:40.152082 +0000 UTC
    
    CONNECTOR ID                         CREATED              ARCHITECTURE VERSION  ORIGIN IP     EDGE
    788009df-fae0-49d0-b43b-be58a968df29 2025-04-09T01:32:57Z linux_amd64  2025.4.0 xxx.xxx.x.xxx 2xkhh01, 2xtpe01
    

    我寫了一支 Bash Script 來顯示所有 Tunnel 的狀態,這樣就可以很方便的查看目前所有 Tunnel 的狀態:

    #!/bin/bash
    
    # 取得所有 tunnel ID 和名稱
    tunnels=$(cloudflared tunnel list | tail -n +3 | awk '{print $1 "," $2}')
    
    echo "===== 所有 Cloudflared Tunnel 的連線資訊 ====="
    echo ""
    
    # 對每個 tunnel 執行 info 命令
    while IFS= read -r tunnel_info; do
        tunnel_id=$(echo $tunnel_info | cut -d',' -f1)
        tunnel_name=$(echo $tunnel_info | cut -d',' -f2)
    
        echo "===== 通道名稱: $tunnel_name (ID: $tunnel_id) ====="
        cloudflared tunnel info $tunnel_name
        echo ""
        echo "------------------------------------------------"
        echo ""
    done <<< "$tunnels"
    
  2. 刪除 Tunnel

    假設你的 Tunnel 名稱為 dev-test1 的話,可以用以下命令刪除:

    cloudflared tunnel cleanup dev-test1
    cloudflared tunnel delete dev-test1
    
  3. 刪除之前建立的路由(域名)

    目前 cloudflared 並沒有支援透過 CLI 刪除路由 (DNS),建議自己連到 https://dash.cloudflare.com/ 並手動刪除所有之前自動建立的 CNAME 記錄。

    不過,我在 Cloudflare 的 DNS 管理很多子域名,要找出哪些 CNAME 是 Cloudflare Tunnel 自動建立的,可以透過以下方式檢查。

    第一種方法,是直接在 Cloudflare 的 DNS 管理頁面中,拿 .cfargotunnel.com 這個關鍵字去搜尋,可以找出過往曾經透過 Cloudflare Tunnel 建立的 CNAME 記錄。

    第二種方法,則是用 cloudflared tunnel list 找出 Tunnel 的 ID,再拿 ID 去搜尋也可以找到。

關於 Cloudflare Tunnel 的觀念整理

  • 瞭解「域名憑證」的位置

    每次透過 cloudflared tunnel login 登入會取得「域名憑證」,預設檔案會置於 ~/.cloudflared/cert.pem

    如果在執行 cloudflared tunnel list 命令時,要指定「域名帳戶」來列出所有已建立的 Tunnel 可以執行以下命令:

    cloudflare tunnel --origincert '~/.cloudflared/cert.pem' list
    

    將憑證檔更名後,就可以再登入一次,以取得另一個「域名」的憑證,這樣就可以用多個域名在同一台主機操作:

    cloudflare tunnel --origincert '~/.cloudflared/cert-domain1.pem' list
    cloudflare tunnel --origincert '~/.cloudflared/cert-domain2.pem' list
    
  • 瞭解「通道」的概念

    建立通道時,會產生一個 GUID 格式的 ID,這個 ID 會用來識別這個通道,並且會產生一個以 Tunnel ID 為名的 JSON 檔案,這是代表一個通道的密鑰。預設檔名與路徑如下:

    ~/.cloudflared/{ID}.json
    

    一個 Cloudflare 域名 (domain) 有多個通道 (tunnel),每個通道可以有最多 100連線(connection)。

    Tunnel availability and failover

    Cloudflare Tunnel 可已建立多個副本 (cloudflared replicas),主要用來作為可用性故障轉移等情境,不是用來作為負載平衡之用。如有負載平衡需求,需改用 Cloudflare Load Balancers

  • 關於 Cloudflare Tunnel 的設定檔

    Cloudflare Tunnel 的設定檔預設路徑為 ~/.cloudflared/config.yml,這個檔案在建立 Tunnel 時不會自動產生。

    如果沒有這個設定檔,就無法直接用 cloudflared tunnel run 啟動通道執行,必須指定通道名稱或通道 ID。

    cloudflared tunnel run {tunnel_name}
    

    如果手動建立該檔案,可以定義 tunnel: 為預設 Tunnel ID,例如:

    tunnel: {tunnel_id}
    

    這樣就可以直接建立連線:

    cloudflared tunnel run
    

    不過,建立了通道,依然外面是無法連線的,因為你並沒有設定路由 (route) (ingress rules)。

    通道通常需要一個路由 (route) 來讓網路建立連線,而每個路由通常會綁定一個域名 (dns)。

    cloudflared tunnel route dns {tunnel_name} {hostname}
    

    這裡的 {hostname} 指的是你的子域名名稱,如果你授權登入的域名是 miniasp.com 的話,這裡 hostname 設定為 test-1,那麼你就可以透過 test-1.miniasp.com 來連線。

    直接透過命令列工具建立連線的方式是:

    cloudflared tunnel run --url http://localhost:3000
    

    建立好通道連線後,就可以用 {hostname}.miniasp.com 來連到本機的 http://localhost:3000 位址。

    如果想要連接多個本地網路服務,可以在 ~/.cloudflared/config.yml 設定檔中,加入多個 ingress 規則,這樣就可以透過不同的子域名來連接不同的本地網路服務。

    tunnel: {tunnel_id}
    
    ingress:
      - hostname: test-1.miniasp.com
        service: http://localhost:80
      - hostname: test-2.miniasp.com
        service: http://localhost:443
      - hostname: test-3.miniasp.com
        service: http://localhost:3000
      - service: http_status:404
    

    這樣就可以直接用以下命令使用預設的通道建立連線:

    cloudflared tunnel run
    

    這幾個域名記得都要先透過 cloudflared tunnel route dns {tunnel_name} {hostname} 註冊域名,否則一樣連不上

  • 我覺得 cloudflared 有幾個設計不良的地方

    首先,執行 cloudflared tunnel run 時所呈現的 Log 完全看不出路由資訊,對於不熟悉 Cloudflare Tunnel 的人來說,會不知道應該給別人什麼網址連線!然後,目前的 cloudflared 也完全沒有方法可以取得之前建立的路由資訊,真的要「回憶」起你當初到底建立哪一個,必須要透過以下步驟查看。

    1. 透過 cloudflared tunnel list 取得 Tunnel ID (你總該先知道正在用哪個 Tunnel 吧)

    2. 然後用瀏覽器登入 Cloudflare 帳號,進入到 DNS 頁面,然後搜尋 Tunnel ID 才能找到之前替這個 Tunnel 建立的 CNAME 記錄,這才是你可以連上這個 Tunnel 的域名。

    再者,另一個不方便的地方,就是你完全找不到你正準備連線的 Tunnel 到底是綁定哪個域名。

    我們需透過 cloudflared tunnel login 登入會取得「域名憑證」,這個憑證其實是綁定一個「域名」的,預設檔案會置於 ~/.cloudflared/cert.pem。但是,該檔案看不出到底當初授權的是哪個域名,這真的很討厭啊,我域名很多,換來換去的,我自己都會忘記當初到底是建立哪一個域名的憑證。

    解決方法,就是不要用「預設憑證」路徑,登入後就改名,這樣可能比較不會忘記。

    例如: ~/.cloudflared/cert.pem 產生後,就立刻複製一份,叫做 ~/.cloudflared/cert-miniasp.com.pem,這樣下次看檔名就知道是什麼域名了!

使用心得

我覺得雖然 Cloudflare Tunnel 很強大,功能也非常多,但是使用體驗真的不太好,心理負擔太多,特別是在設定和管理上,讓人感到困惑。

我目前為止,還是覺得 ngrok 最為簡單易用,使用上完全無負擔,一個命令就可以開通連線!

ngrok http 3000

或是

ngrok http http://localhost:8080

雖然 ngrok 的免費版有一些限制,但是它的使用體驗真的比 Cloudflare Tunnel 好太多了。

相關連結

留言評論