nginx 專案搬到 GitHub 上

在「Nginx has moved to GitHub (nginx.org)」看到的,原連結是「[nginx-announce] NGINX has moved to Github!」。

這次搬移也順便將本來是 Mercurial 的專案換到 Git 上了...

Hacker News 有不少人在討論現在 nginx 目前的情況,像是之前有提到分家的事情:「nginx 分家:freenginx」。

不過基本功能都算成熟很久了,算是還好?

nginx + AWStats

AWStats 是個很老牌的分析工具,直接對 access log 分析後提供報表,本來以為是完全沒在動的專案,但從版本記錄發現 2020 年與 2023 年各有一版修安全性問題,看起來還是有在維護?

會想到要裝是因為這幾天被砍站,CPU credit balance 低到觸發我設定的 alarm:

除了處理外,也想快速看一下發生什麼事情,而這種砍站的在 JavaScript 類的分析服務上不會看到,需要直接對 server log 分析,所以就想到 AWStats 了...

Ubuntu 可以透過 sudo apt install -y awstats 安裝,裝完以後會看到 /etc/cron.d/awstats 裡面每十分鐘會自己跑一次。

預設的設定目錄在 /etc/awstats 裡面,而且有系統預設的 awstats.conf 與讓使用者蓋過去的 awstats.conf.local,可以避免升級時直接把使用者自己設定的值蓋掉。

最少有幾個要設定需要設,可以參考官方文件「AWStats configuration directives/options」裡面的說明:

LogFile="/var/log/nginx/blog.gslin.org_ssl-access.log"
LogFormat=1
SiteDomain=blog.gslin.org

接下來因為 AWStats 是 CGI script,但 nginx 不直接支援 CGI,所以需要找工具透過 FastCGICGI 程式,在這邊我是用 fcgiwrap 轉,這個軟體可以直接裝:

sudo apt install -y fcgiwrap

網路上翻文件看到也不少人是用 PHP 轉的 (應該是互相 copy & paste),也是個方法...

接下來就是去 document root 下面把 cgi-bin 以及 icon 目錄先接起來:

cd /srv/www.example.com/public
sudo ln -s /usr/share/awstats/icon awstats-icon
sudo mkdir cgi-bin
cd cgi-bin
sudo ln -s /usr/lib/cgi-bin/awstats.pl .

然後是 nginx 這邊的設定,我只放 awstats.pl 給 fcgiwrap:

    location /cgi-bin/awstats.pl {
        include fastcgi.conf;
        fastcgi_pass unix:/var/run/fcgiwrap.socket;
    }

另外在意的人可以用 nginx 的 auth_basicauth_basic_user_file 指定帳號密碼,擋外部的存取。

過個十分鐘 (或是半小時,如果 access log 比較多,第一次跑比較慢的話) 就可以直接在 https://www.example.com/cgi-bin/awstats.pl 上看到了。

這報表有種懷舊感...

幫你打理伺服器環境的 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