如何禁用Docker容器的Shell访问?基于OpenJDK 17构建Java Web应用的解决方案咨询
docker exec登录的最佳方案 首先得说清楚为什么你当前的镜像还能被docker exec访问:Amazon Corretto 17 Alpine镜像本质上还是基于Alpine Linux,默认包含了sh Shell,而且容器默认以root用户运行,所以有权限执行docker exec的人都能直接登录进去。
下面是几种靠谱的解决方案,你可以根据自己的需求选择:
1. 创建无Shell的专用用户运行应用
这是最推荐的方案,既安全又不会破坏镜像的基础功能。思路是在镜像中创建一个系统用户,指定其Shell为/sbin/nologin(无登录权限的Shell),然后切换到该用户运行应用。
修改后的Dockerfile如下:
FROM amazoncorretto:17-alpine3.15 # 创建无Shell的系统用户,-D表示不设置密码,-s指定登录Shell RUN adduser -D -s /sbin/nologin appuser # 复制你的应用Jar包 COPY myapp.jar /myapp.jar # 切换到创建的用户 USER appuser # 保持你的ENTRYPOINT(用exec形式,避免启动Shell父进程) ENTRYPOINT ["java","-jar","/myapp.jar"]
这样设置后,即使有人尝试docker exec -it my-container sh,会因为用户的Shell是nologin而直接退出,无法登录。而且这种方法不会影响Java应用的运行,因为Java不需要依赖Shell。
2. 删除镜像中的Shell二进制文件
如果不需要镜像中存在任何Shell,可以直接删除sh和bash这类文件。这种方法更彻底,但要注意:如果你的应用启动脚本依赖Shell(比如用Shell形式的ENTRYPOINT/CMD),会导致应用无法启动。不过你的ENTRYPOINT已经用了exec形式(["java",...]),所以完全没问题。
修改后的Dockerfile:
FROM amazoncorretto:17-alpine3.15 # 删除默认的Shell文件 RUN rm -f /bin/sh /bin/bash COPY myapp.jar /myapp.jar ENTRYPOINT ["java","-jar","/myapp.jar"]
尝试docker exec时,会因为找不到Shell命令而报错,从而阻止登录。缺点是如果后续需要临时调试镜像会比较麻烦,但如果你的需求是完全禁止容器访问,这是个不错的选择。
3. 使用Distroless无发行版镜像
Distroless镜像专门为容器化应用设计,只包含运行应用所需的最基础依赖(比如Java运行时),完全没有Shell、包管理器等多余组件。这种镜像体积小,安全性极高,天生就无法通过docker exec登录。
你可以切换到Google的Distroless Java 17镜像,Dockerfile如下:
FROM gcr.io/distroless/java17-debian11 COPY myapp.jar /myapp.jar ENTRYPOINT ["java","-jar","/myapp.jar"]
这种方案的优点是镜像最精简、最安全;缺点是如果需要调试应用,几乎没有办法进入容器(除非额外添加调试工具),适合已经完成测试、不需要频繁调试的生产环境应用。
额外注意事项
- 确保你的ENTRYPOINT/CMD始终使用exec形式(即数组格式
["command", "arg1"]),而不是Shell形式(比如ENTRYPOINT java -jar /myapp.jar)。Shell形式会启动一个Shell作为父进程,即使你禁用了用户的Shell,这个父Shell可能还是会被docker exec利用。 - 如果你用Kubernetes等编排工具部署容器,还可以通过设置
securityContext进一步限制容器权限,比如禁止特权模式、限制用户ID等,增强整体安全性。
内容的提问来源于stack exchange,提问作者Mac

