When distributing pre-built Docker containers, preventing unauthorized shell access becomes crucial for protecting sensitive information like configuration files, API keys, or serial numbers. The standard java:7
base image (Ubuntu-based) includes multiple shell options (bash
, dash
, sh
), creating potential security vulnerabilities.
Malicious users typically attempt container access through:
docker exec -it [container] bash
docker attach [container]
docker run -ti --entrypoint=/bin/bash [image]
Method 1: Remove Shell Binaries
Modify your Dockerfile to eliminate shells during build:
FROM java:7
RUN rm -f /bin/bash /bin/sh /bin/dash \
&& rm -f /usr/bin/bash /usr/bin/sh /usr/bin/dash
Method 2: Use Minimal Base Images
FROM gcr.io/distroless/java:11
COPY app.jar /app/
CMD ["/app/app.jar"]
1. Read-only Filesystem:
docker run --read-only -d mycontainer
2. User Namespace Isolation:
# Add to /etc/docker/daemon.json
{
"userns-remap": "default"
}
3. Seccomp Profiles:
docker run --security-opt seccomp=no-exec.json mycontainer
Test your hardened container with:
docker export [container] | gzip -c > test.tar.gz
gzip -dc test.tar.gz | docker import - test_image
docker run -ti --entrypoint=/bin/bash test_image
This should fail with "No such file or directory" for shell access attempts.
For sensitive data like serial numbers:
# Use Docker secrets (Swarm mode)
echo "serial123" | docker secret create serial_number -
docker service create --secret serial_number mycontainer
When deploying Docker containers in production, preventing unauthorized shell access becomes crucial for protecting sensitive data like configuration files, API keys, or serial numbers. The standard java:7
base image (Ubuntu-based) includes multiple shell binaries (bash
, sh
, dash
), creating potential entry points for attackers.
To comprehensively secure your container, we need to address three main entry methods:
# Method 1: Prevent docker exec
FROM java:7
RUN rm -f /bin/bash /bin/sh /bin/dash
# Method 2: Block attach via custom ENTRYPOINT
ENTRYPOINT ["java", "-jar", "your_app.jar"]
CMD ["--no-shell"]
A robust solution combines Docker configuration with filesystem hardening:
# Dockerfile hardening example
FROM java:7
# Remove shells and related utilities
RUN rm -rf /bin/*sh /bin/bash /usr/bin/ssh* \
&& apt-get purge -y --auto-remove openssh-server*
# Create non-root user
RUN useradd -ms /bin/false appuser
USER appuser
# Secure ENTRYPOINT with exec form
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
Even with these measures, we should add runtime protection:
# Run container with security flags
docker run -d \
--read-only \
--security-opt=no-new-privileges \
--cap-drop=ALL \
your_container_image
Test the hardened container using the verification steps:
# Attempt shell access (should all fail)
docker exec -it secured_container sh
docker attach secured_container
docker run -ti --entrypoint=/bin/bash secured_container
For maximum security, implement a custom seccomp profile:
{
"defaultAction": "SCMP_ACT_ERRNO",
"syscalls": [
{
"names": [
"execve",
"fork",
"clone",
"kill"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
Apply it during container execution:
docker run --security-opt seccomp=profile.json your_image
For configuration files containing serial numbers:
# Use tmpfs for sensitive data
docker run -d --tmpfs /etc/config:rw,noexec,nosuid,size=1m your_image
# Alternative: encrypted volumes
docker volume create --driver crypt --name secure_config