Links#
https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html
https://nginx.org/en/docs/http/ngx_http_proxy_module.html1. Important Points#
Nginx auth_basic 可以给接口、内部 dashboard、调试页面加一层用户名密码认证。它适合保护低频内部入口,不适合替代应用自己的登录、权限、审计和 token 体系。
auth_basic 是 Nginx 自带的 HTTP Basic Auth 模块指令,模块名是 ngx_http_auth_basic_module。通常系统包安装的 Nginx 都支持,不需要额外安装 Nginx 插件;额外需要的只是生成密码文件的 htpasswd 工具。
good for:
internal API
admin dashboard
temporary debug endpoint
monitoring UI behind VPN / private network
not enough for:
public production API
fine-grained user permission
multi-tenant authorization
audit-grade identity management建议:
security:
Basic Auth 必须配合 HTTPS
password file 权限限制为 root / nginx 可读
不要把 .htpasswd 提交到 Git
对公网入口再配合 allow / deny 或安全组限制2. Create Password File#
先确认 Nginx 识别 auth_basic 配置。最直接的方式是写完配置后运行:
sudo nginx -t如果不支持,会报 unknown directive "auth_basic"。
Debian / Ubuntu:
sudo apt-get update
sudo apt-get install -y apache2-utilsRHEL / CentOS / Amazon Linux:
sudo yum install -y httpd-tools创建第一个用户:
sudo htpasswd -c /etc/nginx/.htpasswd admin追加第二个用户时不要加 -c,否则会覆盖原文件:
sudo htpasswd /etc/nginx/.htpasswd ops检查文件:
sudo ls -l /etc/nginx/.htpasswd
sudo cat /etc/nginx/.htpasswd3. Alternative Password Generators#
.htpasswd 不只能用 htpasswd 生成;Nginx 读取的是兼容 htpasswd 格式的用户名和密码 hash。优先用 htpasswd,没有这个工具时可以用 openssl passwd -apr1。
# htpasswd: recommended
sudo htpasswd -c /etc/nginx/.htpasswd admin
sudo htpasswd /etc/nginx/.htpasswd ops
# openssl: when htpasswd is unavailable
printf "admin:$(openssl passwd -apr1 'your-password')\n" | sudo tee /etc/nginx/.htpasswd
printf "ops:$(openssl passwd -apr1 'ops-password')\n" | sudo tee -a /etc/nginx/.htpasswd权限建议:
sudo chown root:www-data /etc/nginx/.htpasswd
sudo chmod 0640 /etc/nginx/.htpasswd如果系统的 Nginx group 不是 www-data,先确认:
ps -eo user,group,comm | grep nginx然后把 group 换成实际的 Nginx worker group,例如 nginx:
sudo chown root:nginx /etc/nginx/.htpasswd
sudo chmod 0640 /etc/nginx/.htpasswd4. Protect A Proxy API#
这个例子把外部 :81/api/ 反代到本机后端 127.0.0.1:8080,并要求 Basic Auth。
server {
listen 81;
server_name _;
location /api/ {
auth_basic "internal api";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://127.0.0.1:8080/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}保存为:
/etc/nginx/conf.d/internal-api-auth.conf验证并 reload:
sudo nginx -t
sudo systemctl reload nginx5. Protect Whole Site#
如果整个 vhost 都要认证,可以把 auth_basic 放在 server 级别。
server {
listen 81;
server_name _;
auth_basic "internal site";
auth_basic_user_file /etc/nginx/.htpasswd;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}如果只想保护某个接口,就把 auth_basic 放到对应 location。
6. Path Prefix Example#
这个例子适合把内部工具挂到一个路径下:
http://example.com/alertmanager/
-> Nginx Basic Auth
-> http://127.0.0.1:9093/alertmanager/Nginx:
server {
listen 82;
server_name _;
location = / {
return 302 /alertmanager/;
}
location /alertmanager/ {
auth_basic "alertmanager";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://127.0.0.1:9093/alertmanager/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Alertmanager 启动参数需要和路径一致:
alertmanager \
--config.file=/etc/alertmanager/alertmanager.yml \
--web.route-prefix=/alertmanager \
--web.external-url=http://example.com:82/alertmanager7. Verify#
未带认证时应该返回 401:
curl -i http://127.0.0.1:81/api/带认证访问:
curl -i -u admin:'your-password' http://127.0.0.1:81/api/只看状态码:
curl -o /dev/null -s -w '%{http_code}\n' http://127.0.0.1:81/api/
curl -o /dev/null -s -w '%{http_code}\n' -u admin:'your-password' http://127.0.0.1:81/api/预期:
without auth:
401
with valid auth:
200 / 301 / 302 / backend status
with wrong password:
401查看 Nginx 是否监听端口:
ss -lntp | grep ':81'查看日志:
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log8. Allowlist Plus Password#
对公网机器建议同时限制来源 IP:
location /api/ {
allow 10.0.0.0/8;
allow 192.168.0.0/16;
deny all;
auth_basic "internal api";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://127.0.0.1:8080/;
}注意:
order:
allow / deny 和 auth_basic 都会生效
来源 IP 不允许时直接 403
来源 IP 允许但没密码时返回 4019. Common Problems#
| Symptom | Check |
|---|---|
401 Unauthorized |
用户名密码是否正确,.htpasswd 是否是当前文件 |
403 Forbidden |
是否配置了 deny all,是否命中了错误的 server / location |
500 Internal Server Error |
Nginx worker 是否能读取 .htpasswd |
502 Bad Gateway |
后端地址是否可达,proxy_pass 是否正确 |
访问 /api/foo 后端路径不对 |
检查 proxy_pass 末尾是否需要 / |
| reload 后不生效 | 先 nginx -t,再 systemctl reload nginx |
权限排查:
sudo nginx -t
sudo namei -l /etc/nginx/.htpasswd
ps -eo user,group,comm | grep nginx
sudo tail -n 100 /var/log/nginx/error.log