Basic Auth


https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html
https://nginx.org/en/docs/http/ngx_http_proxy_module.html

1. 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-utils

RHEL / 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/.htpasswd

3. 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/.htpasswd

4. 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 nginx

5. 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/alertmanager

7. 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.log

8. 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 允许但没密码时返回 401

9. 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