幫你打理伺服器環境的 piku

但文件寫的很糟:「The tiniest PaaS you've ever seen. Piku allows you to do git push deployments to your own servers.」。

名字來自於 Dokku,另外應該是用到了 pico 這個數量級詞:

piku, inspired by dokku, allows you do git push deployments to your own servers, no matter how small they are.

這個工具是在「Piku: Allows git push deployments to your own servers (github.com/piku)」這邊看到的,就如同說明提到的,希望透過 git push 就可以發佈軟體。

不過在討論裡面也有人抱怨文件的問題,像是在 id=40625339 這邊就有人提到。

實際到 AWS 上開了一台 EC2 instance 測,看起來就是提供一包 script 把伺服器端設定好,包括了 Let's Encrypt + nginx 以及 SSHGit 相關的設定,接著你可以直接將這台 server 設為 git remote,然後就可以用 git push 推軟體上去了。

對於只是想要跑軟體,而且容易接 CI/CD 的方案來說還算 OK,但如果想要自己客製化 nginx 或是一些工具的情境就不是那麼適用了。

nginx 分家:freenginx

Hacker News 上看到 Maxim Dounin 決定分家到 freenginx 的消息:「Freenginx: Core Nginx developer announces fork (nginx.org)」,原文在 mailing list 上:「announcing freenginx.org」,這邊提到分家的原因:

Unfortunately, some new non-technical management at F5 recently decided that they know better how to run open source projects. In particular, they decided to interfere with security policy nginx uses for years, ignoring both the policy and developers’ position.

在 freenginx 的 mailing list 上有提到更多,在 2024-February/000007.html 這篇:

The most recent "security advisory" was released despite the fact that the particular bug in the experimental HTTP/3 code is expected to be fixed as a normal bug as per the existing security policy, and all the developers, including me, agree on this.

And, while the particular action isn't exactly very bad, the approach in general is quite problematic.

這邊提到的 security advisory 是「[nginx-announce] nginx security advisory (CVE-2024-24989, CVE-2024-24990)」這個,看起來是個沒有 enabled by default 的功能:

Two security issues were identified in nginx HTTP/3 implementation,
which might allow an attacker that uses a specially crafted QUIC session
to cause a worker process crash (CVE-2024-24989, CVE-2024-24990) or
might have potential other impact (CVE-2024-24990).

The issues affect nginx compiled with the ngx_http_v3_module (not
compiled by default) if the "quic" option of the "listen" directive
is used in a configuration file.

The issue affects nginx 1.25.0 - 1.25.3.
The issue is fixed in nginx 1.25.4.

id=39373804 這邊有些目前 nginx 組成的資訊可以讀,目前 nginx 的 core devs 應該就三位 (在 Insights/Contributors 這邊看起來只有兩位,這是因為 GitHub 上面的 mirror 看起來是從 Mercurial 同步過去的,而 Sergey Kandaurov 沒有 GitHub 帳號):

Worth noting that there are only two active "core" devs, Maxim Dounin (the OP) and Roman Arutyunyan. Maxim is the biggest contributor that is still active. Maxim and Roman account for basically 99% of current development.

So this is a pretty impactful fork. It's not like one of 8 core devs or something. This is 50% of the team.

Edit: Just noticed Sergey Kandaurov isn't listed on GitHub "contributors" because he doesn't have a GitHub account (my bad). So it's more like 33% of the team. Previous releases have been tagged by Maxim, but the latest (today's 1.25.4) was tagged by Sergey

現在就是單方面的說法,可以再讓子彈多飛一點時間... 看 F5 要不要回應,以及 F5 的說法 (如果要回應的話)。

nginx 開始嘗試支援 HTTP/3

Hacker News 上看到「Nginx 1.25.0: experimental HTTP/3 support (nginx.org)」這則消息,從 nginx 1.25.0 開始可以用 HTTP/3

HTTP/2 最大的差異就是從以往的 TCP 改到 UDP 上了,這是基於 QUIC 的經驗弄出來的東西...

nginx 的支援算是等了一陣子了,不過沒有當初 HTTP/1.1SPDY 的進步這麼明顯,我自己就沒有跟的那麼緊了。

這樣以後 office firewall 預設應該會再開 443/udp?

25Gbps 下 HTTPS 的效率

作者家裡拉了 25Gbps 的 Internet 後 (可以參考先前寫的「25Gbps 的家用 Internet」這篇),然後發現 Internet 上好像拉不動 25Gbps 的量,所以自己在家裡先測試了現在 HTTPS 的極限速度:「25 Gbit/s HTTP and HTTPS download speeds」。

Client 是 AMD 的 5600X,算是目前最新的世代;Server 則是 Intel 的 9900K,目前最新應該是 12 代;測試用 35GB 的檔案來測,然後使用 TCP BBR (這邊沒有特別講,目前 kernel 內建的還是 v1)。

在單條 HTTP 的情況下 curl + nginx 與 curl + caddy 都可以直接跑滿 (23.4Gbps),Gonet/http 會卡在 20Gbps 左右。

如果是多條 HTTP 的話都可以跑滿 23.4Gbps。

但到了 HTTPS 的情況下最快的是 Go + net/http,可以跑到 12Gbps;curl + nginx 剩下 8Gbps;接下來 curl + caddy 的部份只有 7.5Gbps,而 go + caddy 只有 7.2Gbps。

上到多條 HTTPS 的情況大家都可以跑滿 23.4Gbps,除了 go + caddy 只能跑到 21.6Gbps。

另外作者試著用 kTLS 把 TLS 的工作丟進 kernel,就不需要全部在 nginx 內處理,速度基本上沒有太大變化,主要是降低了 CPU loading:

In terms of download speeds, there is no difference with or without KTLS. But, enabling KTLS noticeably reduces CPU usage, from ≈10% to a steady 2%.

算是一個有趣的發現,如果目前的 HTTPS 想要在 25Gbps 上面單線直接跑滿,還需要再 tune 不少東西...

NGINX 官方給的十個常見的設定錯誤

也是在 Hacker News 上看到的,NGINX 官方給的 10 個設定上的常見錯誤:「Avoiding the Top 10 NGINX Configuration Mistakes」,對應的討論串在「Avoiding the top Nginx configuration mistakes (nginx.com)」這邊,看了一下裡面還蠻多蠻實用的?(雖然有些算是官方自己的偏見,或是官方自己在搞事...)

第一個是 worker_connections 設定與 fd 的上限沒有對應,因為每個連線會吃掉兩個 fd,所以要給夠才有辦法讓 connection 衝上去:

The common configuration mistake is not increasing the limit on FDs to at least twice the value of worker_connections. The fix is to set that value with the worker_rlimit_nofile directive in the main configuration context.

另外就是要檢查系統的 limit 設定,像是 /etc/security/limit.conf 這類的設定。

第二個是 error_log off 這個設法,在 NGINX 裡面是有 access_log off,但沒有 error_log off 這個設法,如果你這樣設的話會寫到 error 這個檔名內。所以如果真的要丟掉的話要丟到 /dev/null,像是官方給的範例這樣:

error_log /dev/null emerg;

第三個是 NGINX 對 upstream (backend) 沒有設定 keepalive,這會導致在量很大的時候會產生不少 overhead。

第四個是違反直覺的繼承設定,官方用這樣的範例來表達:

http {
    add_header X-HTTP-LEVEL-HEADER 1;
    add_header X-ANOTHER-HTTP-LEVEL-HEADER 1;

    server {
        listen 8080;
        location / {
            return 200 "OK";
        } 
    }

    server {
        listen 8081;
        add_header X-SERVER-LEVEL-HEADER 1;

        location / {
            return 200 "OK";
        }

        location /test {
            add_header X-LOCATION-LEVEL-HEADER 1;
            return 200 "OK";
        }

        location /correct {
            add_header X-HTTP-LEVEL-HEADER 1;
            add_header X-ANOTHER-HTTP-LEVEL-HEADER 1;

            add_header X-SERVER-LEVEL-HEADER 1;
            add_header X-LOCATION-LEVEL-HEADER 1;
            return 200 "OK";
        } 
    }
}

而你會發現 add_header 會蓋掉先前繼承的項目,但同一個 block 之間會疊加而不是蓋掉...

所以打 localhost:8081/test 的時候只會有 X-LOCATION-LEVEL-HEADER: 1;打 localhost:8081/correct 會有四個 HTTP headers...

第五個算是官方自己的偏見,我們一般都會希望壓低 TTFB (Time To First Byte),把 proxy_buffering 關閉算是常態了。

第六個是官方雖然實做了 if 但很不希望你拿來用。

第七個是 health check 的正確設法,避免多個 block 都有 health check,造成無用的功夫。

第八個是保護內部的資訊,這邊給的範例是 stub_status

第九個是個地雷,ip_hash 這個演算法可以依據 client 的 IP address 來打散流量到後端的伺服器上,對於 IPv6 他會拿整個 IPv6 當作 key (128 bits),但對 IPv4 他只會拿前面三碼 (24 bits) 當作 key:

The ip_hash algorithm load balances traffic across the servers in an upstream{} block, based on a hash of the client IP address. The hashing key is the first three octets of an IPv4 address or the entire IPv6 address. The method establishes session persistence, which means that requests from a client are always passed to the same server except when the server is unavailable.

這完全就是官方自己在搞... 而 workaround 是自己指定 key:

The fix is to use the hash algorithm instead with the $binary_remote_addr variable as the hash key.

第十個是沒有使用 upstream,大多數的情況就是測試發現會動,就直接上 production 的關係 XDDD

Igor Sysoev (nginx 的作者) 離開 F5

Hacker News Daily 上看到的消息,nginx 的發明人 Igor Sysoev 離開 F5:「Do Svidaniya, Igor, and Thank You for NGINX」。

官方的理由是家庭與朋友,另外有其他的個人計畫:

With profound appreciation and gratitude, we announce today that Igor Sysoev – author of NGINX and co‑founder of NGINX, Inc. – has chosen to step back from NGINX and F5 in order to spend more time with his friends and family and to pursue personal projects.

在 Hacker News 上的討論「Do svidaniya, Igor, and thank you for Nginx (nginx.com)」也可以翻一下,不過看起來都是在歌功頌德...

2019 年三月 NGINX, Inc. 賣給了 F5,到現在快三年了。以官方的文章看起來應該是和平分手,翻了 Twitter 帳號發現也很久都沒更新了,比較好奇的是後續的計畫會是什麼...

關閉 GitLab 的 nginx,使用自己裝的 nginx

我自己架設的 GitLab 是透過「Install self-managed GitLab」這邊的方法裝進 Ubuntu 系統內的 (我在自己的 wiki 上也有整理:「GitLab」),他會自己下載所有對應的套件,包括了 nginx

但這樣就直接把 TCP port 80/443 都吃掉了,同一台機器要放其他的 virtual host 就比較麻煩,所以找了些方法讓 GitLab 不要佔用 TCP port 80/443。

首先是找到這篇,資料有點舊,但裡面關掉 nginx 的方法還算是有用:「How to setup gitlab without embedded nginx」。

現在只要把 /etc/gitlab/gitlab.rb 裡面的:

  • nginx['enable'] 改成 false
  • web_server['external_users'] 改成 ['www-data']

然後跑 gitlab-ctl reconfigure 讓他更新設定檔,接下來停掉整個 GitLab 再打開 (或是重開機) 讓 nginx 完全失效就可以了。

接下來弄好自己的 nginx 以及 HTTPS 設定,這個部份我自己偏好用 dehydrated,其他人會有不同的偏好設法。

在弄完 nginx 後再來是 proxy_pass 類的資訊要帶進去,這個部份可以參考本來 GitLab 的 nginx 設定檔 (在 /var/opt/gitlab/nginx/conf/ 這下面),其中最重要的就是 GitLab 本身,我們會在 /etc/nginx/conf.d/upstream.conf 裡面寫入對應的 upstream 資訊 (沒這個檔案就自己生一個):

upstream gitlab-workhorse {
    server unix:/var/opt/gitlab/gitlab-workhorse/sockets/socket;
}

接下來是在對應的 virtual host 下設定 proxy_pass

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_pass http://gitlab-workhorse;
    }

另外我有啟用 GitLab 提供的 Mattermost,所以也要翻設定導進去:

#
upstream gitlab_mattermost {
    server 127.0.0.1:8065;
}

與:

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_pass http://gitlab_mattermost;
    }

都弄好後叫 nginx 重讀設定,接下來應該就會動了... 然後 TCP port 80/443 也算自由了,要掛其他的網域上去應該都 OK。

這幾天 blog 被掃,用 nginx 的 limit_req_zone 擋...

Update:這個方法問題好像還是不少,目前先拿掉了...

這幾天 blog 被掃中單一頁面負載會比較重的頁面,結果 CPU loading 變超高,從後台可以看到常常滿載:

看了一下是都是從 Azure 上面打過來的,有好幾組都在打,IP address 每隔一段時間就會變,所以單純用 firewall 擋 IP address 的方法看起來沒用...

印象中 nginx 本身可以 rate limit,搜了一下文件可以翻到應該就是「Module ngx_http_limit_req_module」這個,就設起來暫時用這個方式擋著,大概是這樣:

limit_conn_status 429;
limit_req_status 429;
limit_req_zone $binary_remote_addr zone=myzone:10m rate=10r/m;

其中預設是傳回 5xx 系列的 service unavailable,但這邊用 429 應該更正確,從維基百科的「List of HTTP status codes」這邊可以看到不錯的說明:

429 Too Many Requests (RFC 6585)
The user has sent too many requests in a given amount of time. Intended for use with rate-limiting schemes.

然後 virtual host 的設定檔內把某個 path 放進這個 zone 保護起來,目前比較困擾的是需要 copy & paste try_filesFastCGI 相關的設定:

    location /path/subpath {
        limit_req zone=myzone;
        try_files $uri $uri/ /index.php?$args;

        include fastcgi.conf;
        fastcgi_intercept_errors on;
        fastcgi_pass php74;
    }

這樣一來就可以自動擋下這些狂抽猛送的 bot,至少在現階段應該還是有用的...

如果之後有遇到其他手法的話,再見招拆招看看要怎麼再加強 :o

把 TLS 1.0 關掉...

突然想到所以到 SSL Report 上測試 blog.gslin.org,發現如果還支援 TLS 1.0,在 Overall Rating 的部份會直接被降為 B。

翻了一下 access log (我在 log 裡有多記錄連線的 TLS Protocol),看起來用 TLS 1.0 連的主要都是 bot,關掉應該是還好...

另外看了一下報告裡的 Cipher Suites 部份,發現不少 cipher (像是 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028)) 被列為 weak,看起來像是把使用 CBC 的 cipher 都認為是 weak,是個推廣 AEAD 的概念。

影響應該不大,但還是記錄一下... 另外等 Ubuntu 20.04 出了以後把整台重灌好了,目前還在用 Ubuntu 16.04,系統內建的 nginx 不支援 TLS 1.3。

nginx + fcgiwrap (spawn-fcgi) + Mailgraph

手上還是有固定一台機器是自己架設 Postfix 管理郵件系統,所以還是想跑個 Mailgraph 看一下有多少量在上面跑...

不過因為 Mailgraph 的 web interface 只有 CGI 界面,但 nginx 不支援 CGI,所以需要找個工具透過 nginx 支援的 FastCGI 轉換進去。

概念與設定都不算太難,但是得把工具找齊才會動 (這段花了不少時間),所以記錄一下怎麼做,以後找比較好找資料。

首先先裝 Mailgraph 與 fcgiwrap:

sudo apt install -y fcgiwrap mailgraph

這兩個程式預設都會跑起來。如果沒有的話自己用 sudo service fcgiwrap statussudo service mailgraph status。接下來在 nginx 的找個 virtual host 裡面這樣設:

    location /mailgraph/ {
        index mailgraph.cgi;

        location ~ \.cgi$ {
            include fastcgi.conf;

            fastcgi_pass unix:/var/run/fcgiwrap.socket;
        }
    }

然後把 Mailgraph 的 CGI 與 css 透過 symbolic link 建到 document root 的 mailgraph/ 下:

export DOCUMENT_ROOT="/srv/home.gslin.org"
cd "${DOCUMENT_ROOT}"
sudo mkdir mailgraph
sudo ln -s /usr/lib/cgi-bin/mailgraph.* .

接著重讀設定檔,或是重跑 nginx,就應該可以在 https://virtualhost.com/mailgraph/ 下看到了。