Docker部署Flask SSH代理转发应用遇公钥权限拒绝问题
问题诊断与解决方案
核心问题
容器内手动启动SSH代理并添加密钥后,终端进程能正常使用代理转发,但Flask应用进程无法获取到SSH代理的环境变量,导致调用SSH命令时找不到代理,触发Permission denied (publickey)错误。
原因分析
- 用户环境隔离:Docker Compose指定容器以
root用户启动,但Flask应用最终以tester用户运行。手动在终端执行的ssh-agent和ssh-add是在root用户环境下,而Flask进程属于tester用户,两者环境变量不共享。 - 环境变量未传递:SSH代理依赖
SSH_AUTH_SOCK和SSH_AGENT_PID两个关键环境变量,Flask进程启动时没有继承这些变量,导致SSH命令无法找到代理套接字。 - 权限问题:挂载的SSH密钥文件在
/root/.ssh目录下,tester用户没有读取权限,即使传递了环境变量,也无法访问密钥或代理套接字。
解决方案
方案1:统一用户环境,自动启动SSH代理
调整容器启动流程,让tester用户自动启动SSH代理并加载密钥,确保Flask进程继承代理环境变量。
1. 修改Dockerfile
FROM python:3.11-slim-bullseye # 更新/安装必要的库 RUN apt-get -y update && apt-get install -y openssh-client curl sudo && rm -rf /var/lib/apt/lists/* # 创建tester用户并配置权限 RUN adduser --disabled-password --gecos "" tester RUN adduser tester sudo RUN mkdir -p /home/tester/.ssh && chown -R tester:tester /home/tester/.ssh && chmod 700 /home/tester/.ssh RUN pip3 install poetry # 创建项目目录并设置权限 WORKDIR /home/tester/config-app COPY . . RUN chown -R tester:tester /home/tester/config-app # 通过poetry安装Python依赖 RUN poetry config virtualenvs.create false RUN poetry install USER tester # 切换到应用目录 WORKDIR /home/tester/config-app/src/config_fetcher # 编写启动脚本,自动启动SSH代理并启动Flask COPY start.sh /home/tester/start.sh RUN chmod +x /home/tester/start.sh CMD ["/home/tester/start.sh"]
2. 创建启动脚本start.sh
#!/bin/bash # 启动SSH代理 eval $(ssh-agent -s) # 添加密钥(如果密钥路径有变化,自行调整) ssh-add /home/tester/.ssh/id_ed25519 # 启动Flask应用 poetry run flask --app app run --host 0.0.0.0 --port 5000
3. 修改docker-compose.yml
调整volume挂载路径到tester用户的.ssh目录,移除不必要的user: root配置:
version: '3.8' services: config-fetcher: image: config-app volumes: - ~/.ssh/config:/home/tester/.ssh/config:rw - ~/.ssh/id_ed25519:/home/tester/.ssh/id_ed25519:rw - ~/.ssh/id_ed25519.pub:/home/tester/.ssh/id_ed25519.pub:rw - ~/.ssh/known_hosts:/home/tester/.ssh/known_hosts:rw build: context: . dockerfile: ./Dockerfile ports: - "5000:5000"
方案2:Flask代码中显式指定SSH密钥(无需代理转发)
如果不想依赖SSH代理,可以在SSH命令中直接指定密钥文件,绕过代理:
修改get_config_via_ssh函数中的命令:
def get_config_via_ssh(user, host, tunnel_ip): try: # 直接指定密钥文件路径,替换为容器内tester用户的密钥路径 cmd2 = f"ssh -i /home/tester/.ssh/id_ed25519 -A -tt {user}@{host} -tt \"ssh -i /home/tester/.ssh/id_ed25519 root@{tunnel_ip} 'cat /storage/config'\"" process = subprocess.getoutput(cmd2, encoding='utf-8') cleaner = ConfigCleaner() return cleaner.clean(process) except Exception as e: print(e) print("[!] Cannot connect to the device SSH Server") return {"error": str(e)} # 不要直接exit,避免Flask进程退出
额外注意事项
- 确保挂载的密钥文件权限正确:容器内
/home/tester/.ssh/id_ed25519权限应为600,可以在启动脚本中添加chmod 600 /home/tester/.ssh/id_ed25519命令调整。 - 如果远程主机需要验证指纹,提前在
known_hosts文件中添加对应主机的指纹,避免SSH交互提示。
内容的提问来源于stack exchange,提问作者Dani Danger
相关产品推荐
相关产品推荐

