Deploying in Kubernetes
ARMORRED images are designed for secure Kubernetes deployments. They run as non-root, support read-only root filesystems, and comply with the Kubernetes Pod Security Standards restricted profile. This guide provides production-ready manifests for deploying ARMORRED images with appropriate security contexts, health checks, and resource management.
Deployment: NGINX Hardened
HTTP probes are the recommended health check pattern because they work regardless of whether a shell is present in the image.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
armorred.org/tier: hardened
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
armorred.org/tier: hardened
spec:
securityContext:
runAsNonRoot: true
runAsUser: 65534
runAsGroup: 65534
fsGroup: 65534
seccompProfile:
type: RuntimeDefault
containers:
- name: nginx
image: ghcr.io/armorred/nginx:1.26-hardened
ports:
- containerPort: 8080
name: http
protocol: TCP
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 3
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 2
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 500m
memory: 128Mi
volumeMounts:
- name: tmp
mountPath: /tmp
- name: logs
mountPath: /var/log/nginx
volumes:
- name: tmp
emptyDir:
sizeLimit: 64Mi
- name: logs
emptyDir:
sizeLimit: 128Mi
Service
Expose the deployment through a Service targeting port 8080:
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
selector:
app: nginx
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
type: ClusterIP
Clients connect to the Service on port 80, which routes to the container's port 8080.
Health Checks
ARMORRED nginx images expose a /health endpoint that returns HTTP 200:
location /health {
access_log off;
return 200 "OK\n";
add_header Content-Type text/plain;
}
HTTP Probes (Recommended)
HTTP probes work with all ARMORRED images regardless of shell availability:
livenessProbe:
httpGet:
path: /health
port: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
Exec Probes
If the specific image includes a shell, exec probes are also available. However, HTTP probes are preferred for consistency and lower overhead:
readinessProbe:
exec:
command:
- /bin/sh
- -c
- "curl -sf http://localhost:8080/health || exit 1"
Check the image analysis pages to verify whether a specific image and tier includes a shell before relying on exec probes.
Writable Directories
ARMORRED images run with readOnlyRootFilesystem: true. Mount emptyDir volumes for directories that require write access:
| Mount Path | Purpose | Recommended Size |
|---|---|---|
/tmp | Nginx temp files (client body, proxy, fastcgi) | 64Mi |
/var/log/nginx | Access and error logs (if not using /dev/stdout) | 128Mi |
The nginx configuration directs temporary files to /tmp:
client_body_temp_path /tmp/client_body; proxy_temp_path /tmp/proxy; fastcgi_temp_path /tmp/fastcgi; uwsgi_temp_path /tmp/uwsgi; scgi_temp_path /tmp/scgi;
Custom Configuration
Mount a ConfigMap to override the default nginx configuration:
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
data:
nginx.conf: |
worker_processes auto;
pid /tmp/nginx.pid;
error_log /dev/stderr warn;
events {
worker_connections 1024;
use epoll;
}
http {
server_tokens off;
access_log /dev/stdout;
sendfile on;
keepalive_timeout 65;
client_body_temp_path /tmp/client_body;
proxy_temp_path /tmp/proxy;
fastcgi_temp_path /tmp/fastcgi;
uwsgi_temp_path /tmp/uwsgi;
scgi_temp_path /tmp/scgi;
server {
listen 8080;
server_name _;
location / {
root /var/www/html;
index index.html;
}
location /health {
access_log off;
return 200 "OK\n";
add_header Content-Type text/plain;
}
}
}
Reference the ConfigMap in the Deployment:
volumeMounts:
- name: config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
readOnly: true
volumes:
- name: config
configMap:
name: nginx-config
Note: The ARMORRED nginx binary expects its configuration at a Nix store path by default. When mounting a custom config, override the command:
command: ["/nix/store/.../bin/nginx"] args: ["-c", "/etc/nginx/nginx.conf", "-g", "daemon off;"]
Network Policies
Restrict ingress and egress traffic to only what the application requires:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: nginx-policy
spec:
podSelector:
matchLabels:
app: nginx
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
role: ingress-controller
ports:
- port: 8080
protocol: TCP
egress: []
This policy:
- Allows ingress only from pods labeled
role: ingress-controlleron port 8080 - Blocks all egress traffic (nginx serving static content needs no outbound connections)
- Denies all other ingress by default
Adjust egress rules if nginx is configured as a reverse proxy.
Pod Security Standards
ARMORRED images comply with the Kubernetes Pod Security Standards restricted profile. Enforce this at the namespace level:
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
The restricted profile requires:
| Requirement | ARMORRED Compliance |
|---|---|
runAsNonRoot: true | UID 65534 by default |
allowPrivilegeEscalation: false | Supported |
readOnlyRootFilesystem: true | Designed for read-only root |
capabilities.drop: ALL | No capabilities needed |
seccompProfile: RuntimeDefault | Compatible |
Resource Limits
Set appropriate resource requests and limits based on workload:
| Workload | CPU Request | CPU Limit | Memory Request | Memory Limit |
|---|---|---|---|---|
| Static content | 50m | 200m | 32Mi | 64Mi |
| Reverse proxy | 100m | 500m | 64Mi | 128Mi |
| High traffic | 250m | 1000m | 128Mi | 256Mi |
The reduced image size (121 MB hardened, 72 MB locked) means faster startup and lower baseline memory consumption compared to upstream (196 MB).
Complete Example
A production-ready deployment combining all security controls:
If the image does not include a shell, exec attempts will fail, preventing attackers from obtaining an interactive session even after container compromise: