Docker AuthZ插件开发疑问:如何获取本地Linux用户实现授权绕过
Docker授权插件获取本地Linux用户的解决方案
问题原因
Docker本地unix socket连接时,授权请求的RequestBody中的User字段为空——因为Docker daemon不会主动传递本地用户信息。此时需要通过Socket的Peer Credentials获取发起请求的客户端进程的UID,再映射为用户名来实现特定用户的授权绕过。
实现步骤
1. 获取Socket的Peer Credentials
通过操作系统的SO_PEERCRED socket选项,可获取连接到Docker daemon的客户端进程的PID、UID和GID。在Flask中,需从WSGI环境中获取当前请求的Socket文件描述符,再调用系统调用解析凭证。
2. 将UID转换为用户名
利用Python内置的pwd模块,将获取到的UID转换为对应的Linux用户名。
3. 修改授权逻辑
在exec请求的判断逻辑中,添加特定用户白名单,允许其绕过授权限制。
修改后的完整代码示例
import json import re from re import search import base64 from flask import Flask, request import struct import fcntl import pwd plug = Flask(__name__) @plug.route("/AuthZPlugin.AuthZReq", methods=["POST"]) def req(): res = json.loads(request.data) response = {"Allow": True} # 获取发起请求的本地用户信息 client_user = None try: # 从uWSGI环境获取socket文件描述符(不同WSGI服务器可能有差异) sock_fd = request.environ.get('uwsgi.socket') if sock_fd: SO_PEERCRED = 17 # Linux系统的SO_PEERCRED常量值 # 定义存储凭证的结构体格式:pid_t, uid_t, gid_t(各4字节) cred_buf = struct.pack('III', 0, 0, 0) cred_len = struct.calcsize('III') # 调用ioctl获取peer凭证 cred_data = fcntl.ioctl(sock_fd, SO_PEERCRED, cred_buf) _, uid, _ = struct.unpack('III', cred_data) # 将UID转换为用户名 client_user = pwd.getpwuid(uid).pw_name except Exception as e: print(f"获取用户信息失败: {str(e)}") # 处理exec请求的授权逻辑 if search(r'/(exec)$', res["RequestUri"]) is not None: # 允许指定用户(例如"admin")绕过授权 if client_user == "admin": response = {"Allow": True} else: response = {"Allow": False} return json.dumps(response) if __name__ == "__main__": plug.run(host='0.0.0.0', port=5000)
注意事项
- WSGI服务器兼容性:不同WSGI服务器传递Socket的方式不同,示例中使用的是uWSGI的
uwsgi.socket变量。如果使用Gunicorn等其他服务器,需要调整获取Socket的方式。 - 用户映射权限:如果授权插件运行在Docker容器内,需要将主机的
/etc/passwd挂载到容器中(例如-v /etc/passwd:/etc/passwd:ro),否则无法将UID转换为用户名。 - TCP连接场景:此方法仅适用于本地unix socket连接Docker daemon的情况。如果用户通过TCP连接,需要依赖TLS证书的CN字段等其他认证方式来识别用户。
内容的提问来源于stack exchange,提问作者pythonhmmm
相关产品推荐
相关产品推荐

