Run sshd in Docker container
Table of content:
## 背景随着容器化,团队出现了越来越多的调试需求,比如线上跑一个脚本,或者安装各种工具进行问题调试,甚至每个人都想有一个隔离的调试的环境。
于是「调试容器」的项目应运而生,也就是提供一个和线上(可以任意选择环境或者机房)一致但是不注册流量的容器,可以用做调试,跑脚本或者做一些实验的地方。
提供了一个这样的容器,就需要提供一个登录方式,最开始我们的选型是 docker-ssh (基于 docker exec),相关项目链接: https://github.com/alash3al/dockssh (但是做了一点更改)。这样的选型, 在登录上非常方便(但是有部分安全隐患,因为没有任何验证),在团队内跑了很长一段时间。
但是随着调试容器的使用范围越来越广,大家经常会出现需要 scp 容器里脚本生成的文件,但是没有比较方便的途径。(docker-ssh 的方案不满足当前的场景),于是在调试容器里面支持 scp 成为了一个呼声越来越大的需求。
那要怎么做呢 ?最开始一想,非常简单呀,在容器里面跑一个 sshd,把对应创建调试容器的用户的 Public key 处理下不就好了么 ?
简化出来的步骤是这样的:
1 | apt-get install -y openssh-server # 安装 openssh-server |
于是开始测试,但是发现了非常多的问题(坑)
遇到的问题
1. Docker run 时环境变量被 reset
现象是 Dockerfile 里面的 ENV 以及启动时注入的环境变量相比直接 docker exec 进去的都不可见。原因是 SSH 和 PAM 会 reset 掉环境变量
官方文档提到了这个问题, 提供的解决办法是:
- 如果是 Dockerfile 里面的 ENV,可以 echo 一份到 /etc/profile, eg.
echo "export VISIBLE=now" >> /etc/profile
(假设 export 一个 VISIBLE 为 now) - 如果是 Docker 以 docker run -e 的形式注入的环境变量,需要在启动命令里面同时处理一份
但是这样的步骤对我来说非常不能接受,所以最终我采用在 sshd 起起来之前执行下面的步骤env >> /etc/environment
但是 /etc/environment 本身是不支持带有换行符的值的,所以最终选择在注入环境变量之前将带有特殊符号的环境变量值处理好。具体原因详见
相关链接:
- 官方文档提出的解决方案 https://docs.docker.com/engine/examples/running_ssh_service/#environment-variables
- 其他人对官方提供的方案的疑问https://stackoverflow.com/questions/36292317/why-set-visible-now-in-etc-profile
- https://github.com/phusion/baseimage-docker/issues/54
- /etc/environment 这里的实现逻辑 http://www.kbase101.com/question/34486.html
2. Dockerfile 里处理的 System Path 需要重新处理
一些 Dockerfile,如 GO 的这两行,在 ssh 到容器中时, System Path 会有丢失
1 | ENV GOPATH /go |
通过 echo "export PATH=$PATH" >> /etc/profile
来绕过了这个问题
最终的结论
总结起来简化之后的步骤大致是这样的
1 | apt-get install -y openssh-server # 安装 openssh-server |
在支持 scp 的同时,带来的额外的好处是让容器的登录更加安全。
但是说回容器是否应该跑 sshd,这里的场景是「调试」所以没有太多去考虑业内最佳实践等。
不过这篇文章 列了很多想要跑 sshd 的场景和更好的解决方案, 推荐阅读。
关于头图
拍摄自清华