Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nginx 反代 + auth_basic 时 401 错误 #3829

Closed
4 tasks done
myfingerhurt opened this issue Mar 12, 2023 · 10 comments · May be fixed by #7894 or AlistGo/alist-web#242
Closed
4 tasks done

Nginx 反代 + auth_basic 时 401 错误 #3829

myfingerhurt opened this issue Mar 12, 2023 · 10 comments · May be fixed by #7894 or AlistGo/alist-web#242
Labels
bug Something isn't working wontfix This will not be worked on

Comments

@myfingerhurt
Copy link

myfingerhurt commented Mar 12, 2023

Please make sure of the following things

  • I have read the documentation.
  • I'm sure there are no duplicate issues or discussions.
  • I'm sure it's due to alist and not something else(such as Dependencies or Operational).
  • I'm sure I'm using the latest version

Alist Version / Alist 版本

v3.13.2

Driver used / 使用的存储驱动

local

Describe the bug / 问题描述

点击展开配置文件
server {
 #Other settings

auth_basic           "You Shall Not Pass!";
auth_basic_user_file /etc/nginx/.htpasswd;

location / {
	proxy_pass 	http://192.168.20.18:5244/;
	proxy_http_version                 1.1;
	### Proxy headers
	proxy_set_header Host              $http_host; # <- This is necessary!!!
	proxy_set_header X-Real-IP         $my_real_ip;
	proxy_set_header X-Forwarded-For   $my_remote_addr;
	proxy_set_header X-Forwarded-Host  $http_host;
	proxy_set_header X-Forwarded-Port  $server_port;
	proxy_connect_timeout              60s;
	proxy_send_timeout                 60s;
	proxy_read_timeout                 60s;
}

}

  • 原因: 想要给alist的入口再加一层简单的密码保护,掩盖特征,阻断扫描
    同时qBittorrent的WebUI, 以及nastools使用这种两层密码的反代方式完全没有问题。
  • 现象: 使用开头的nginx配置,输入完auth_basic的第一层密码后会提示401错误,无法继续操作
    Failed fetching settings: Request failed with status code 401
  • 期望: 能显示alist的登录框以便输入第二层密码
  • 对比: 注释掉auth_basic后可以登录alist一切正常
  • 排查: 发现是/api路径遭遇了401错误,猜测是调用api时没有继承浏览器的session?
    加了这个临时绕开,但是显然API也是必须要受到第一层密码保护的
location /api {
		proxy_pass 	http://192.168.20.18:5244/;
  • 结论: 如果可能的话希望可以支持上层套auth_basic的反代设置
  • 测试环境: Windows 10, Firefox 110.0.1
作为参考,这里是qBittorrent的反代设置
server {
  auth_basic           "You Shall Not Pass!";
  auth_basic_user_file htpasswd;

  proxy_set_header            Host            $http_host; # <- This is necessary!!!
  proxy_set_header            X-Real-IP       $my_real_ip;
  proxy_set_header            X-Forwarded-For $my_remote_addr;
  proxy_http_version          1.1;

  location /qbit2/ {
    proxy_intercept_errors on;
    proxy_pass        http://127.0.0.1:8080/; # tailing / slash is necessary
  }
}

Reproduction / 复现链接

  • 401错误卡在/api/public/settings
    image

  • 加入bypass后

location /api {
		proxy_pass 	http://192.168.20.18:5244/;

image

Logs / 日志

No response

@myfingerhurt myfingerhurt added the bug Something isn't working label Mar 12, 2023
@welcome
Copy link

welcome bot commented Mar 12, 2023

Thanks for opening your first issue here! Be sure to follow the issue template!

@xhofe
Copy link
Collaborator

xhofe commented Mar 13, 2023

因为后端也使用了 Authorization头传递token,这与basic auth冲突。

@myfingerhurt
Copy link
Author

myfingerhurt commented Mar 13, 2023

@xhofe 非常感谢提供思路

然后我用curl测试了一下,发现如果加认证访问/api/public/settings后可以获得200响应
所以至少第一步还是我最开始的猜测,应该是browser那边的脚本没有继承浏览器的session导致的
至于第二步如果api可以接受在body/json中传送认证字段那么这个问题就可以解决

  • 本地200
    curl http://192.168.20.18:5244/api/public/settings
    {"code":200,"message":"success","data":{...
  • nginx反代无认证,401
    curl https://alist4.domain.com/api/public/settings
    <head><title>401 Authorization Required</title></head>
  • nginx反代加认证,200
    curl -u user:password https://alist4.domain.com/api/public/settings
    {"code":200,"message":"success","data":{...

从下面这nginx的log来看,401不是back_end发出的,而是nginx直接给出的401,说明客户端那边在请求API的时候并没给出Authorization 。从alist的debug log可以看到curl https://alist4.domain.com/api/public/settings出现401错误的的时候,alist没有访问记录,所以401是nginx给出的。

Click to expand the logs

[info] 3295616#3295616: *55041 no user/password was provided for basic authentication, client: 127.0.0.1, server: ~^(alist)\d{0,1}.domain.com$, request: "GET /api/public/settings HTTP/2.0", host: "alist9.domain.com", referrer: "https://alist9.domain.com/"
[debug] 3295616#3295616: *55041 http finalize request: 401, "/api/public/settings?" a:1, c:1
[debug] 3295616#3295616: *55041 http special response: 401, "/api/public/settings?"
[debug] 3295616#3295616: *55041 xslt filter header
[debug] 3295616#3295616: *55041 http2 header filter
[debug] 3295616#3295616: *55041 http2 output header: ":status: 401"
[debug] 3295616#3295616: *55041 http2 output header: "server: nginx/1.18.0 (Ubuntu)"
[debug] 3295616#3295616: *55041 http2 output header: "date: Mon, 13 Mar 2023 08:46:23 GMT"
[debug] 3295616#3295616: *55041 http2 output header: "content-type: text/html"
[debug] 3295616#3295616: *55041 http2 output header: "content-length: 188"
[debug] 3295616#3295616: *55041 http2 output header: "www-authenticate: Basic realm="You Shall Not Pass!""

这个log可以判断访问root/的时候,有Authorization header,上面的log可以看到访问api的时候就没了。
X-My-Authorization是我为了测试单独加上的头

Click to expand the logs

[debug] 3295616#3295616: 55041 http proxy header:
"GET / HTTP/1.1
X-My-Authorization: Basic dXNlcjpub3RoaW5nX3RvX2xvb2tfYXQK=
Host: 192.168.20.18:5244
Connection: close
cf-ipcountry: JP
cdn-loop: cloudflare
accept-encoding: gzip
x-forwarded-for: 123.456.789.321
cf-ray: 7a7301930b298335-KIX
x-forwarded-proto: https
cf-visitor: {"scheme":"https"}
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,
/*;q=0.8
accept-language: en-US,en;q=0.5
dnt: 1
authorization: Basic dXNlcjpub3RoaW5nX3RvX2xvb2tfYXQK=
upgrade-insecure-requests: 1
sec-fetch-dest: document
sec-fetch-mode: navigate
sec-fetch-site: none
sec-fetch-user: ?1
cf-connecting-ip: 123.456.789.321

"

@xhofe xhofe added the wontfix This will not be worked on label Mar 16, 2023
@github-actions
Copy link

Hello @myfingerhurt, this issue will not be worked on and will be closed.
你好 @myfingerhurt,这不会被处理,将被关闭。

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Mar 16, 2023
@andywang1011
Copy link

可以参考这个
https://alist.nn.ci/zh/faq/howto.html

@supersu097
Copy link

因为后端也使用了 Authorization头传递token,这与basic auth冲突。

希望能够兼容一下哈~

@supersu097
Copy link

@xhofe 非常感谢提供思路

然后我用curl测试了一下,发现如果加认证访问/api/public/settings后可以获得200响应 所以至少第一步还是我最开始的猜测,应该是browser那边的脚本没有继承浏览器的session导致的 至于第二步如果api可以接受在body/json中传送认证字段那么这个问题就可以解决

  • 本地200
    curl http://192.168.20.18:5244/api/public/settings
    {"code":200,"message":"success","data":{...
  • nginx反代无认证,401
    curl https://alist4.domain.com/api/public/settings
    <head><title>401 Authorization Required</title></head>
  • nginx反代加认证,200
    curl -u user:password https://alist4.domain.com/api/public/settings
    {"code":200,"message":"success","data":{...

从下面这nginx的log来看,401不是back_end发出的,而是nginx直接给出的401,说明客户端那边在请求API的时候并没给出Authorization 。从alist的debug log可以看到curl https://alist4.domain.com/api/public/settings出现401错误的的时候,alist没有访问记录,所以401是nginx给出的。

Click to expand the logs
这个log可以判断访问root/的时候,有Authorization header,上面的log可以看到访问api的时候就没了。 X-My-Authorization是我为了测试单独加上的头

Click to expand the logs

@myfingerhurt 请问一下现在/api这个uri加上认证后可以200了,不加401,不就说明nginx的basic auth生效了么,为什么你却说“排查: 发现是/api路径遭遇了401错误,猜测是调用api时没有继承浏览器的session?加了这个临时绕开,但是显然API也是必须要受到第一层密码保护的”,为什么是又被绕过了呀

@Weltolk
Copy link

Weltolk commented Sep 28, 2023

same issue,不是401,而是nginx不断弹出认证窗口,猜测Authorization字段被alist覆写了

@hkxueqi
Copy link

hkxueqi commented Jan 31, 2024

我也遇到了这个问题 ,我通过nginx反代开放服务,以达到隐藏webcms特征目的。
访问服务时要先经过http统一认证服务,也就是需要有Authorization:base64 {}头。这是第一层认证

但我发现alist服务认证也是使用了Authorization:头来放置jwt认证。

我测试了,一个请求只能有一个Authorization:头,而且只能放置一种认证方式。也就说说两种认证方式不能同时出现。

以下方式都是不行的:

GET /api/fs/list HTTP/1.1
Host: x.x.x.x
Authorization:Basic xxxxx==  
Authorization: eyJhbGciOiJIUzIxxxxx...

也是行不通的

GET /api/fs/list HTTP/1.1
Host: x.x.x.x
Authorization:Basic xxxxx== ; eyJhbGciOiJIUzIxxxxx...

目前已知能让两重认证兼容的方式就是修改 请求头标示。即:改掉nginx的Authorization为其他字段或,alist的Authorization改为其他字段。
Authorization是http服务标准认证方式nginx、Apache、iis均支持,我不知道如何修改,也没找到相关资料。
但据我所知修改alist的Authorization标示为token: ,jwt:等字段可能是目前更加有可能的方案。

@hkxueqi
Copy link

hkxueqi commented Feb 1, 2024

@myfingerhurt
我查阅了该项目其他有关“Authorization”的问题:
我采用此方式,主要来自于“安全”和“性能”两方面的考虑。
1.alist我是采用方向代理方式部署,开放在公网,并且中间件作为负载均衡。可以通过域名或目录指向不同web服务器
2.我不想让任何人看到我部署了alist_http服务,只能看到我开放了一个tcp_http协议的服务

这种结构有两个好处:
1.多次认证多次过滤流量,nginx在第一层承担了脏流量,减轻了alist服务器的流量压力和性能开销。
2.当面对恶意攻击者时,他需要突破两道认证防线,提高了攻击的难度,我相对的更加安全。
详细解释:
1.起到代理服务器的作用,通过中间件的反向代理我可以让访问者先经过了中间件的第一层身份认证,在未通过中间件(nginx)的基本认证(Basic access authentication)时,不会有任何访问者的流量会到达alist-server,访问者看不到任何web页面或cms特征,只有一个认证对话框,只有通过了中间件(nginx)的基本认证时,nginx才会将请求发送至alist服务器,这是才会出现alist的页面,进行alist的认证登陆。
2.由于注重数据安全和数据本地化存储,私有云没有公网ip的环境我需要vps端口转发,但采用直接部署,或将端口直接映射转发,我的alist服务器就直接暴露互联网,可能收到漏洞,目录扫描,暴力破解,等恶意扫描威胁,或是逻辑漏洞:认证绕过,未授权访问等问题时我的数据将泄漏。攻击者流量是直接经过vps后到达我的本地主机(家庭内网,公司内网),如果未来出现高危漏洞,可能直接通过http服务获得一个控制web主机的shell,那我的本地web主机和本地内网都将收到威胁。所以我需要用户额和alist之间有一个类似二次认证或堡垒机的方案

我目前采用的是proxy或vpn方案,需要安装软件和配置较为繁琐,对于普通用户操作上并不友好,且在跨平台win,mac,Mobile上并不友好。且我不能让临时用户访问我的LAN,进行授权配置更加繁琐。而我上述场景只需要在browser上 通过两次login即可。对用户来说是简单的,对运维来说也是相对安全的。

所以希望未来alist能够支持将Authorization修改为其他关键字的版本,或目前是否有其他方案。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working wontfix This will not be worked on
Projects
None yet
6 participants