Começando a Ver o Nginx
Olá, o título é uma paródia de algo. É apenas uma música que me veio à mente de repente.
A razão para as atualizações de artigos serem anormalmente frequentes ultimamente é que, uma vez que algo me interessa, não consigo parar, então é apenas essa fase.
Nginx
Até agora, eu o havia configurado provisoriamente como um proxy reverso e servidor de cache, mas com o suporte a IPv6, comecei a me preocupar com isso, então revisei as configurações, incluindo um refatoramento.
Estrutura de arquivos
Algumas partes estão ocultas, mas está assim:
├── http.d
│ ├── bot_rate_limit.conf
│ ├── gzip.conf
│ ├── proxy_cache_zones.conf
│ └── proxy_common.conf
├── mime.types
├── mime.types-dist
├── nginx.conf
├── scgi_params
├── selfsigned.crt
├── selfsigned.key
├── sites-enabled
│ ├── 1btc.love.conf
│ ├── btclol.xyz.conf
│ ├── damepo.jp.conf
│ ├── git.soulminingrig.com.conf
│ ├── soulminingrig.com.conf
│ ├── starlink.soulminingrig.com.conf
│ ├── stg.api.1btc.love.conf
├── snippets
│ ├── common_error_pages.conf
│ ├── proxy_headers.conf
│ └── ssl_common.conf
├── uwsgi_params
└── win-utf
Eu estava um pouco indeciso sobre qual nome de pasta usar para gerenciar os arquivos conf que são carregados por diretiva location, mas depois de consultar o ChatGPT, ficou assim. http.d são os arquivos incluídos da diretiva http em nginx.conf, então estou bastante satisfeito com isso.
No entanto, achei que se tornar snippets foi um pouco peculiar, mas tudo bem.
Vamos dar uma olhada em cada item de configuração.
http.d/bot_rate_limit.conf
Os crawlers do Meta estavam muito agressivos, então decidi impor limites não apenas com fail2ban, mas também por User-Agent.
Além disso, em relação a feed/RSS, recebi um contato muito gentil, mas não faz sentido aplicar a restrição a eles em primeiro lugar, e eles são quase sempre respondidos pelo cache, então abri uma exceção, considerando que não representam uma carga significativa para o servidor de origem.
# Aplicar limite de taxa apenas a bots e crawlers de expansão de links
map $http_user_agent $is_bot {
default 0;
~*bot 1;
~*crawler 1;
~*spider 1;
~*facebookexternalhit 1;
~*slackbot 1;
~*discordbot 1;
~*twitterbot 1;
~*linkedinbot 1;
~*embedly 1;
~*quora 1;
~*skypeuripreview 1;
~*whatsapp 1;
~*telegrambot 1;
~*applebot 1;
~*pingdom 1;
~*uptimerobot 1;
}
# stg.api.1btc.love é para fins de validação, então bots também são excluídos do limite de taxa
# Se a chave for uma string vazia, não será contada por limit_req_zone
map $server_name $bot_limit_host_key {
stg.api.1btc.love "";
default $binary_remote_addr;
}
# feed.xml / feed.json são excluídos do limite de taxa, mesmo para bots
map $uri $is_feed_path {
default 0;
~*feed\.(xml|json)$ 1;
}
# Usar a chave por IP apenas quando for um bot E não for feed.xml / feed.json
map "$is_bot:$is_feed_path" $bot_limit_key {
default "";
"1:0" $bot_limit_host_key;
}
limit_req_zone $bot_limit_key zone=bot:10m rate=1r/s;
limit_req_status 429;
limit_req zone=bot burst=5 nodelay;
Claro, como isso é aplicado à diretiva http, é basicamente aplicado a tudo, mas fiz o possível para permitir exceções mínimas. Originalmente, julguei que seria melhor que estivesse ativado por padrão.
Se houver um ataque DoS óbvio com falsificação de UA ou falsificação de navegador, ele será bloqueado por fail2ban e tratado como drop, não podendo enviar requisições por um tempo, então é uma defesa em duas camadas.
http.d/gzip.conf
No passado, eu tinha suporte a brotli, mas parei porque era problemático ao atualizar versões se eu tivesse que construir separadamente. A vantagem de poder atualizar com pkg/apt é grande.
gzip on;
gzip_vary off;
gzip_proxied any;
gzip_min_length 1024;
gzip_comp_level 7;
gzip_http_version 1.1;
gzip_types text/plain
text/xml
text/css
text/javascript
image/gif
image/png
image/svg+xml
application/javascript
application/json
application/xml
application/x-javascript
application/font-woff
application/font-woff2
application/font-ttf
application/octet-stream;
Não há muito a dizer, eu não o incluía antes, mas comecei a incluir gzip_min_length há alguns anos. Foi quando pensei que seria um desperdício ter compressão ineficiente e decidi incluí-lo corretamente.
http.d/proxy_cache_zones.conf
Pode parecer que o recuo está sutilmente quebrado, mas é uma restrição do formatador.
Eu removi o que não era necessário, e as zones começam do 4 ou estão espalhadas, mas bem...
Em relação a inactive, se não houver hits por 7 dias, ele é excluído, e em relação a use_temp_path, estou configurando para armazenar em cache diretamente no caminho do cache. Mesmo que você defina algo como proxy_temp_path /tmp/nginx;, parece que ele armazena em cache sem passar por isso, tornando-o mais rápido.
proxy_cache_path /tmp/nginx/zone4 levels=1:2 keys_zone=zone4:10m
inactive=7d
max_size=3g
use_temp_path=off;proxy_cache_path /tmp/nginx/posts levels=1:2 keys_zone=posts:10m
inactive=7d
max_size=2g
use_temp_path=off;
proxy_cache_path /tmp/nginx/git levels=1:2 keys_zone=git:10m
inactive=7d
max_size=2g
use_temp_path=off;
proxy_cache_path /tmp/nginx/static levels=1:2 keys_zone=static_cache:10m
inactive=7d
max_size=1g
use_temp_path=off;
proxy_cache_path /tmp/nginx/1btc_cache levels=1:2 keys_zone=1btc_cache:10m
inactive=7d
max_size=512m
use_temp_path=off;
http.d/proxy_common.conf
proxy_cache_valid é definido como uma regra comum, e o restante é gerenciado para ser sobrescrito no lado da diretiva location.
Dessa forma, o cache pode ser operado mesmo sem configurações de cache.
Ao usar proxy_cache_bypass $http_cookie, evita-se que requisições com cookies, por exemplo, após o login, resultem na exibição da tela anterior ao login.
Além disso, permitir que o cache responda a redirecionamentos, erros e any é uma medida contra ataques. Ao fazer isso, o cache responderá no mínimo, evitando acessos anormais ao Origin.
proxy_temp_path seria preferível ter um caminho que possa ser persistido, mas como basicamente não há tal cenário para o meu uso, eu o defino para /tmp. Se você fizer isso em um site com tráfego considerável, embora houvesse aquela opção não utilizada mencionada anteriormente, provavelmente todos os caches serão apagados na reinicialização do servidor, aumentando a carga no Origin e o risco de falhas.
proxy_buffering on;
proxy_cache_bypass $http_cookie;
proxy_cache_background_update on;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_revalidate on;
proxy_cache_use_stale updating;
proxy_connect_timeout 60;
proxy_no_cache $http_cookie;
proxy_read_timeout 90;
proxy_send_timeout 60;
proxy_temp_path /tmp/nginx;
proxy_cache_valid 200 201 60s;
proxy_cache_valid 301 1d;
proxy_cache_valid 302 3h;
proxy_cache_valid 304 1d;
proxy_cache_valid 404 1m;
proxy_cache_valid any 5s;
proxy_cache_lock on;
nginx.conf
Não há muito a dizer, exceto que ele está configurado para retornar um erro específico caso o IP do registro A seja acessado diretamente.
multi_accept está ativado porque é uma instância fraca que não é de alta especificação, operando como meu servidor de proxy reverso e cache.
As configurações que antes eram escritas diretamente na diretiva http agora são incluídas por finalidade, o que melhorou bastante a clareza.
worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 65535;
events {
multi_accept on;
worker_connections 65535;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
server_tokens off;
types_hash_max_size 4096;
client_max_body_size 16M;
# MINE
include mime.types;
default_type application/octet-stream;
include ./http.d/bot_rate_limit.conf;
include ./http.d/proxy_common.conf;
include ./http.d/proxy_cache_zones.conf;
include ./http.d/gzip.conf;
server {
listen 80;
listen [::]:80;
server_name 163.44.113.145 91.98.169.80 2400:8500:2002:3317:163:44:113:145;
include snippets/common_error_pages.conf;
return 444;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name 163.44.113.145 91.98.169.80 2400:8500:2002:3317:163:44:113:145;
ssl_certificate ./selfsigned.crt;
ssl_certificate_key ./selfsigned.key;
include snippets/common_error_pages.conf;
# IPアドレスへのアクセスを拒否
return 444;
}
### Damepo.jp
include ./sites-enabled/damepo.jp.conf;
### Soulminingrig My Blog
include ./sites-enabled/soulminingrig.com.conf;
# ~~~略~~~~
}
sites-enabled/soulminingrig.com.conf
Abaixo de site-enabled, apresentarei apenas um exemplo. Esta é a configuração deste site.
Além disso, o motivo pelo qual não estou usando links simbólicos é simplesmente para poder excluir rapidamente o que não é necessário.
Na verdade, existem algumas partes que ainda estão codificadas, mas por favor, entenda que ainda estou trabalhando nisso.
Recentemente, mudei para www.soulminingrig.com, mas antes disso, eu estava entregando no domínio raiz sem www, então, atualmente, ainda estou aceitando o domínio raiz sem redirecionamento.
Para o período de verificação, estou configurando as respostas de cabeçalho de forma que seja fácil entender se o cache do servidor ou o cache do cliente respondeu.
E, enquanto penso se as regras de cache para imagens, fontes, CSS, etc., poderiam ser um pouco melhores, fico pensando... Não há como resolver isso...?
upstream está configurado para que eu possa responder rapidamente se o número de backends aumentar. Bem, atualmente só há um, mas tê-lo no topo facilita a compreensão de qual backend a configuração está apontando.
upstream backend_sm {
server 10.1.0.228:8888 max_fails=3 fail_timeout=3s;
keepalive 16;
keepalive_timeout 30s;
}
map $uri $static_cache {
~\.(jpg|jpeg|png|webp|gif|mp4|css|js|ico|woff2)(\?.*)?$ "public, max-age=604800";
~\.html$ "public, max-age=600";
default "public, max-age=600";
}
map $upstream_cache_status $server_cache_status {
default $upstream_cache_status;
"" "NONE";
}
map "$http_if_none_match:$http_if_modified_since" $client_cache_request {
default "MISS";
"~.+:.+" "REVALIDATE";
"~.+:" "REVALIDATE";
"~:.+" "REVALIDATE";
}
server {
listen 80;
listen [::]:80;
server_name soulminingrig.com www.soulminingrig.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl reuseport backlog=65535 rcvbuf=256k sndbuf=256k fastopen=256 so_keepalive=on;
listen [::]:443 ssl reuseport backlog=65535 rcvbuf=256k sndbuf=256k fastopen=256 so_keepalive=on ipv6only=on;
listen 443 quic reuseport;
listen [::]:443 quic reuseport;
http2 on;
http3 on;
server_name soulminingrig.com www.soulminingrig.com;
client_max_body_size 50M;
location ~* \.(jpg|jpeg|png|webp|gif|ico|mp4|js|css|woff2)(\?.*)?$ {
proxy_pass http://backend_sm;
include snippets/proxy_headers.conf;
proxy_http_version 1.1;
proxy_redirect off;
proxy_cache static_cache;
proxy_cache_valid 200 301 302 7d;
proxy_cache_valid 404 1m;
proxy_cache_revalidate on;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_lock on;
proxy_connect_timeout 3s;
proxy_read_timeout 15s;
expires 7d;
add_header X-Cache-Status $upstream_cache_status always;
add_header X-Server-Cache-Status $server_cache_status always;
add_header X-Client-Cache-Request $client_cache_request always;
add_header X-Client-Cache-Policy "public, max-age=604800" always;
add_header Cache-Control "public, max-age=604800" always;
}
location / {
proxy_pass http://backend_sm/;
include snippets/proxy_headers.conf;
proxy_http_version 1.1;
proxy_redirect off;
proxy_cache posts;
proxy_cache_key $scheme$host$request_uri;
proxy_cache_valid 200 10m;
proxy_cache_valid 301 1h;
proxy_cache_valid 404 1m;
proxy_cache_revalidate on;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504 http_403 http_404;
proxy_cache_background_update on;
proxy_cache_lock on;
proxy_connect_timeout 3s;
proxy_read_timeout 15s;
expires $static_cache;
add_header X-Cache-Status $upstream_cache_status always;
add_header X-Server-Cache-Status $server_cache_status always;
add_header X-Client-Cache-Request $client_cache_request always;
add_header X-Client-Cache-Policy $static_cache always;
add_header Cache-Control $static_cache always;
}
include snippets/common_error_pages.conf;
include snippets/ssl_common.conf;
ssl_certificate /hoge/fullchain.pem; # managed by Certbot
ssl_certificate_key /hoge/oulminingrig.com/privkey.pem;
# managed by Certbot
}
Bônus: nginxfmt.py
Isso é muito bom.
É um formatador, e sinto que a qualidade é a mais alta.
Está disponível no AUR.
yay -S nginx-config-formatter
Para usar, ele formata com nginxfmt.py example.conf,
find . -name "*conf" | xargs -I{} nginxfmt.py {}
e ele formata em massa.
Até pouco tempo atrás, eu usava nginxbeautifier, mas a sintaxe de redirecionamento ficava corrompida, então mudei.