如何限制基于tomcat:9.0.87镜像的K8s容器的kubectl exec登录访问?
看起来你踩了个常见的坑:想通过删除容器里的shell来阻止kubectl exec登录,但没想到直接删掉bash会导致Tomcat启动失败。这是因为Tomcat的catalina.sh启动脚本依赖bash运行,你删掉bash后,脚本找不到解释器,自然就启动失败了,也就是你看到的/usr/bin/env: ‘bash’: No such file or directory错误。
我给你几个可行的解决方案,你可以根据实际情况选择:
方案一:绕过catalina.sh,直接启动Tomcat的Java进程
既然catalina.sh依赖bash,那我们可以跳过这个脚本,直接用Java命令启动Tomcat,这样就不需要bash了,也就可以安全删除shell文件。
首先你需要找到catalina.sh run最终执行的Java命令:可以先运行原镜像,进入容器执行ps aux查看Tomcat的启动命令,或者直接查看catalina.sh的内容提取出Java命令。
然后修改你的Dockerfile,把Java命令直接作为CMD执行,同时删除shell:
FROM public.int.repositories.cloud.sap/tomcat:9.0.87 as deploy COPY --from=build /cdi-web/target/cdi-web.war /usr/local/tomcat/webapps # 删除所有交互shell RUN rm -rf /bin/sh /bin/dash /bin/bash EXPOSE 8080 # 直接用Java命令启动Tomcat,替换成你提取到的完整命令 CMD ["java", "-Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties", "-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager", "-Djdk.tls.ephemeralDHKeySize=2048", "-Djava.protocol.handler.pkgs=org.apache.catalina.webresources", "-Dorg.apache.catalina.security.SecurityListener.UMASK=0027", "-Dignore.endorsed.dirs=", "-classpath", "/usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar", "-Dcatalina.base=/usr/local/tomcat", "-Dcatalina.home=/usr/local/tomcat", "-Djava.io.tmpdir=/usr/local/tomcat/temp", "org.apache.catalina.startup.Bootstrap", "start"]
这样容器启动时直接运行Java进程,不需要依赖bash,同时用户执行kubectl exec时找不到可用的shell,也就无法进入容器。
方案二:保留bash但限制普通用户的访问权限(需调整启动逻辑)
如果你不想修改Tomcat的启动方式,可以保留bash,但通过权限配置让容器的运行用户无法执行bash,但需要调整启动逻辑避免Tomcat启动失败:
- 创建一个非root用户用来运行Tomcat
- 将bash移动到仅root可访问的目录,修改
catalina.sh的shebang指向该路径 - 切换到非root用户启动Tomcat
对应的Dockerfile:
FROM public.int.repositories.cloud.sap/tomcat:9.0.87 as deploy COPY --from=build /cdi-web/target/cdi-web.war /usr/local/tomcat/webapps # 创建仅root可访问的目录存放bash RUN mkdir -p /root/restricted_bin && mv /bin/bash /root/restricted_bin/ # 修改catalina.sh的shebang,让它用root目录下的bash执行 RUN sed -i 's|#!/usr/bin/env bash|#!/root/restricted_bin/bash|' /usr/local/tomcat/bin/catalina.sh # 删除公共路径下的shell软链接 RUN rm -rf /bin/sh /bin/dash /bin/bash # 创建非root用户并切换 RUN useradd -m tomcat USER tomcat EXPOSE 8080 CMD ["catalina.sh", "run"]
这样Tomcat启动时能通过修改后的shebang找到bash,而普通用户(容器运行用户)无法访问/root/restricted_bin/下的bash,执行kubectl exec时会因找不到shell而失败。
方案三:通过Kubernetes SecurityContext限制exec操作权限
另一种更优雅的方式是不修改镜像,而是通过Kubernetes的SecurityContext配置来限制容器的权限,降低exec后的风险:
- 在Pod/Deployment的spec中设置
runAsNonRoot: true,让容器以非root用户运行 - 设置
allowPrivilegeEscalation: false,禁止权限提升 - 如果集群启用了PodSecurity Admission Controller,给命名空间配置
restricted策略,进一步限制容器的特权操作
虽然这个方法不能完全阻止用户exec进入容器,但会极大限制用户在容器内的操作权限,降低安全风险。
总结一下,最直接解决你当前问题的是方案一,绕过catalina.sh直接启动Java进程,既可以删除shell阻止exec登录,又不会影响Tomcat的正常启动。
备注:内容来源于stack exchange,提问作者Amrutha V

