Secure Podman pods with custom network policies, traffic filtering, and microsegmentation using CNI plugins and netavark. Implement zero-trust networking with firewall rules and container isolation.
Prerequisites
- Root or sudo access
- Basic understanding of containers and networking
- Familiarity with iptables and systemd
What this solves
Podman's default networking allows containers to communicate freely, creating potential security risks in multi-tenant environments. This tutorial implements network policies and microsegmentation to control traffic between pods, isolate workloads, and create secure network boundaries using CNI plugins and custom firewall rules.
Step-by-step configuration
Update system and install Podman with network tools
Install Podman and required networking components for advanced security configuration.
sudo apt update && sudo apt upgrade -y
sudo apt install -y podman cni-plugins iptables netavark aardvark-dns slirp4netns
Configure Podman for rootless operation with network security
Enable user namespaces and configure subuid/subgid mappings for secure container isolation.
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $(whoami)
echo "net.ipv4.ip_unprivileged_port_start=80" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
podman system migrate
Create custom secure network configurations
Define network policies with traffic filtering and microsegmentation capabilities.
{
"name": "secure-frontend",
"cniVersion": "1.0.0",
"plugins": [
{
"type": "bridge",
"bridge": "secure-frontend0",
"isDefaultGateway": true,
"ipMasq": true,
"hairpinMode": true,
"ipam": {
"type": "host-local",
"subnet": "10.89.0.0/24",
"routes": [
{
"dst": "0.0.0.0/0"
}
]
}
},
{
"type": "firewall",
"backend": "iptables"
},
{
"type": "tuning"
}
]
}
Create backend network with restricted access
Configure an isolated backend network that blocks external internet access.
{
"name": "secure-backend",
"cniVersion": "1.0.0",
"plugins": [
{
"type": "bridge",
"bridge": "secure-backend0",
"isDefaultGateway": false,
"ipMasq": false,
"hairpinMode": false,
"ipam": {
"type": "host-local",
"subnet": "10.90.0.0/24"
}
},
{
"type": "firewall",
"backend": "iptables",
"iptablesAdminChainName": "BACKEND_ADMIN"
}
]
}
Configure network policies with iptables rules
Implement granular traffic filtering between network segments using custom iptables chains.
#!/bin/bash
Create custom iptables chains for network policies
iptables -t filter -N PODMAN_FRONTEND_POLICY 2>/dev/null
iptables -t filter -N PODMAN_BACKEND_POLICY 2>/dev/null
iptables -t filter -N PODMAN_MICROSEGMENT 2>/dev/null
Flush existing rules
iptables -t filter -F PODMAN_FRONTEND_POLICY
iptables -t filter -F PODMAN_BACKEND_POLICY
iptables -t filter -F PODMAN_MICROSEGMENT
Frontend network policies (10.89.0.0/24)
Allow HTTP/HTTPS outbound
iptables -t filter -A PODMAN_FRONTEND_POLICY -s 10.89.0.0/24 -p tcp --dport 80 -j ACCEPT
iptables -t filter -A PODMAN_FRONTEND_POLICY -s 10.89.0.0/24 -p tcp --dport 443 -j ACCEPT
Allow DNS
iptables -t filter -A PODMAN_FRONTEND_POLICY -s 10.89.0.0/24 -p udp --dport 53 -j ACCEPT
iptables -t filter -A PODMAN_FRONTEND_POLICY -s 10.89.0.0/24 -p tcp --dport 53 -j ACCEPT
Allow communication to backend network
iptables -t filter -A PODMAN_FRONTEND_POLICY -s 10.89.0.0/24 -d 10.90.0.0/24 -p tcp --dport 5432 -j ACCEPT
iptables -t filter -A PODMAN_FRONTEND_POLICY -s 10.89.0.0/24 -d 10.90.0.0/24 -p tcp --dport 6379 -j ACCEPT
Backend network policies (10.90.0.0/24)
Block all outbound internet access
iptables -t filter -A PODMAN_BACKEND_POLICY -s 10.90.0.0/24 ! -d 10.0.0.0/8 -j DROP
Allow established connections back from frontend
iptables -t filter -A PODMAN_BACKEND_POLICY -s 10.90.0.0/24 -d 10.89.0.0/24 -m state --state ESTABLISHED,RELATED -j ACCEPT
Microsegmentation rules
Block inter-container communication within same network unless explicitly allowed
iptables -t filter -A PODMAN_MICROSEGMENT -s 10.89.0.0/24 -d 10.89.0.0/24 -j DROP
iptables -t filter -A PODMAN_MICROSEGMENT -s 10.90.0.0/24 -d 10.90.0.0/24 -j DROP
Apply policies to FORWARD chain
iptables -t filter -I FORWARD -j PODMAN_FRONTEND_POLICY
iptables -t filter -I FORWARD -j PODMAN_BACKEND_POLICY
iptables -t filter -I FORWARD -j PODMAN_MICROSEGMENT
echo "Network policies applied successfully"
Make network policy script executable and persistent
Set proper permissions and create systemd service for automatic policy enforcement.
sudo chmod 755 /usr/local/bin/podman-network-policies.sh
sudo /usr/local/bin/podman-network-policies.sh
[Unit]
Description=Podman Network Security Policies
After=network.target
Before=podman.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/podman-network-policies.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Enable persistent network policies
Enable the systemd service to apply network policies automatically on boot.
sudo systemctl daemon-reload
sudo systemctl enable --now podman-network-policies.service
sudo systemctl status podman-network-policies.service
Create pod with network security policies
Deploy a secured pod using the custom network configuration with traffic filtering.
podman pod create --name secure-web-pod \
--network secure-frontend \
--security-opt label=level:s0:c100,c200 \
--security-opt no-new-privileges \
--publish 8080:80
Deploy containers with microsegmentation
Run application containers with strict network isolation and minimal privileges.
# Deploy web frontend with network restrictions
podman run -d --name web-frontend \
--pod secure-web-pod \
--security-opt label=level:s0:c100,c200 \
--read-only \
--tmpfs /tmp:noexec,nosuid,size=100m \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
nginx:alpine
Create backend pod with isolated network
podman pod create --name secure-db-pod \
--network secure-backend \
--security-opt label=level:s0:c300,c400 \
--security-opt no-new-privileges
Deploy database with strict isolation
podman run -d --name postgres-db \
--pod secure-db-pod \
--security-opt label=level:s0:c300,c400 \
--read-only \
--tmpfs /tmp:noexec,nosuid,size=50m \
--tmpfs /var/run/postgresql:noexec,nosuid,size=10m \
--cap-drop ALL \
-e POSTGRES_DB=appdb \
-e POSTGRES_USER=appuser \
-e POSTGRES_PASSWORD=secure_password123 \
postgres:15-alpine
Configure network monitoring and logging
Set up traffic monitoring to detect policy violations and security incidents.
#!/bin/bash
Enable iptables logging for policy violations
iptables -t filter -A PODMAN_MICROSEGMENT -j LOG --log-prefix "PODMAN_POLICY_VIOLATION: " --log-level 4
iptables -t filter -A PODMAN_BACKEND_POLICY -s 10.90.0.0/24 ! -d 10.0.0.0/8 -j LOG --log-prefix "BACKEND_INTERNET_BLOCK: " --log-level 4
Monitor network connections
echo "Monitoring Podman network traffic. Check /var/log/syslog for policy violations."
Display current connections
echo "Current network connections:"
ss -tuln | grep -E ":(80|443|5432|6379|8080)"
Show iptables packet counters
echo "\nNetwork policy statistics:"
iptables -t filter -L PODMAN_FRONTEND_POLICY -v -n
iptables -t filter -L PODMAN_BACKEND_POLICY -v -n
sudo chmod 755 /usr/local/bin/monitor-podman-traffic.sh
Test network policies and microsegmentation
Verify that traffic filtering and isolation rules are working correctly.
# Test frontend to backend connectivity (should work on allowed ports)
podman exec web-frontend nc -zv 10.90.0.1 5432
Test blocked inter-container communication within same network
podman run --rm --network secure-frontend alpine nc -zv 10.89.0.2 80
Test backend internet access (should be blocked)
podman exec postgres-db nc -zv 8.8.8.8 53
View policy violations in logs
sudo tail -f /var/log/syslog | grep "PODMAN_POLICY"
Verify your setup
Confirm that network policies and microsegmentation are properly configured and enforcing security boundaries.
# Check pod status and network assignments
podman pod ls
podman network ls
Verify iptables rules are active
sudo iptables -t filter -L PODMAN_FRONTEND_POLICY -v -n
sudo iptables -t filter -L PODMAN_BACKEND_POLICY -v -n
sudo iptables -t filter -L PODMAN_MICROSEGMENT -v -n
Test network connectivity
podman exec web-frontend wget -q --spider --timeout=5 http://example.com && echo "Frontend internet: OK" || echo "Frontend internet: BLOCKED"
podman exec postgres-db wget -q --spider --timeout=5 http://example.com && echo "Backend internet: ERROR - Should be blocked" || echo "Backend internet: BLOCKED - OK"
Check container security labels
podman inspect web-frontend --format '{{.ProcessLabel}}'
podman inspect postgres-db --format '{{.ProcessLabel}}'
Monitor traffic
sudo /usr/local/bin/monitor-podman-traffic.sh
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Containers can't reach internet | iptables rules blocking all traffic | Check sudo iptables -L -v -n and verify ACCEPT rules are before DROP rules |
| Network policies not applying | CNI plugins not properly configured | Restart podman service: systemctl --user restart podman |
| SELinux denials blocking containers | Incorrect security labels | Check sudo ausearch -m avc and use --security-opt label=disable for testing |
| DNS resolution failing | Firewall blocking DNS traffic | Ensure UDP/TCP port 53 is allowed in frontend policy |
| Inter-pod communication failing | Overly restrictive microsegmentation | Add specific allow rules before the blanket DROP rule in PODMAN_MICROSEGMENT |
Next steps
- Configure Podman secrets management with HashiCorp Vault integration
- Configure Podman image scanning with Trivy security vulnerability detection
- Implement Kubernetes network policies with Calico for microsegmentation
- Configure Podman monitoring with Prometheus and Grafana dashboards
- Implement Podman backup automation with volume snapshots and restoration
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Global variables
SCRIPT_NAME="$(basename "$0")"
CLEANUP_FILES=()
CLEANUP_CHAINS=()
# Usage
usage() {
echo "Usage: $SCRIPT_NAME [OPTIONS]"
echo "Options:"
echo " -h, --help Show this help message"
echo " --cleanup Remove installed configurations"
exit 1
}
# Logging functions
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
# Cleanup function
cleanup() {
log_warn "Cleaning up due to error..."
for file in "${CLEANUP_FILES[@]}"; do
[[ -f "$file" ]] && rm -f "$file"
done
for chain in "${CLEANUP_CHAINS[@]}"; do
iptables -t filter -F "$chain" 2>/dev/null || true
iptables -t filter -X "$chain" 2>/dev/null || true
done
}
# Trap for cleanup on error
trap cleanup ERR
# Parse arguments
CLEANUP_MODE=false
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help) usage ;;
--cleanup) CLEANUP_MODE=true; shift ;;
*) log_error "Unknown option: $1"; usage ;;
esac
done
# Check prerequisites
if [[ $EUID -eq 0 ]]; then
log_error "Do not run this script as root. Use sudo when needed."
exit 1
fi
if ! command -v sudo >/dev/null 2>&1; then
log_error "sudo is required but not installed"
exit 1
fi
# Auto-detect distro
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update && apt upgrade -y"
PKG_INSTALL="apt install -y"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
;;
*)
log_error "Unsupported distro: $ID"
exit 1
;;
esac
else
log_error "Cannot detect Linux distribution"
exit 1
fi
log_info "Detected distribution: $PRETTY_NAME"
# Cleanup mode
if [[ "$CLEANUP_MODE" == true ]]; then
echo "[1/3] Removing custom iptables chains..."
sudo iptables -t filter -F PODMAN_FRONTEND_POLICY 2>/dev/null || true
sudo iptables -t filter -F PODMAN_BACKEND_POLICY 2>/dev/null || true
sudo iptables -t filter -F PODMAN_MICROSEGMENT 2>/dev/null || true
sudo iptables -t filter -X PODMAN_FRONTEND_POLICY 2>/dev/null || true
sudo iptables -t filter -X PODMAN_BACKEND_POLICY 2>/dev/null || true
sudo iptables -t filter -X PODMAN_MICROSEGMENT 2>/dev/null || true
echo "[2/3] Removing network configurations..."
sudo rm -f /etc/cni/net.d/89-secure-frontend.conflist
sudo rm -f /etc/cni/net.d/90-secure-backend.conflist
echo "[3/3] Cleanup completed"
exit 0
fi
# Main installation
echo "[1/6] Updating system packages..."
sudo $PKG_UPDATE
echo "[2/6] Installing Podman and network tools..."
if [[ "$PKG_MGR" == "apt" ]]; then
sudo $PKG_INSTALL podman cni-plugins iptables netavark aardvark-dns slirp4netns
else
sudo $PKG_INSTALL podman cni-plugins iptables netavark aardvark-dns slirp4netns
fi
echo "[3/6] Configuring rootless operation..."
CURRENT_USER=$(whoami)
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 "$CURRENT_USER"
# Configure unprivileged ports
if ! grep -q "net.ipv4.ip_unprivileged_port_start" /etc/sysctl.conf 2>/dev/null; then
echo "net.ipv4.ip_unprivileged_port_start=80" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
fi
# Migrate podman system
podman system migrate 2>/dev/null || true
echo "[4/6] Creating secure network configurations..."
sudo mkdir -p /etc/cni/net.d
# Create secure frontend network
FRONTEND_CONFIG="/etc/cni/net.d/89-secure-frontend.conflist"
CLEANUP_FILES+=("$FRONTEND_CONFIG")
sudo tee "$FRONTEND_CONFIG" > /dev/null <<EOF
{
"name": "secure-frontend",
"cniVersion": "1.0.0",
"plugins": [
{
"type": "bridge",
"bridge": "secure-frontend0",
"isDefaultGateway": true,
"ipMasq": true,
"hairpinMode": true,
"ipam": {
"type": "host-local",
"subnet": "10.89.0.0/24",
"routes": [
{
"dst": "0.0.0.0/0"
}
]
}
},
{
"type": "firewall",
"backend": "iptables"
},
{
"type": "tuning"
}
]
}
EOF
# Create secure backend network
BACKEND_CONFIG="/etc/cni/net.d/90-secure-backend.conflist"
CLEANUP_FILES+=("$BACKEND_CONFIG")
sudo tee "$BACKEND_CONFIG" > /dev/null <<EOF
{
"name": "secure-backend",
"cniVersion": "1.0.0",
"plugins": [
{
"type": "bridge",
"bridge": "secure-backend0",
"isDefaultGateway": false,
"ipMasq": false,
"hairpinMode": false,
"ipam": {
"type": "host-local",
"subnet": "10.90.0.0/24"
}
},
{
"type": "firewall",
"backend": "iptables",
"iptablesAdminChainName": "BACKEND_ADMIN"
}
]
}
EOF
sudo chown root:root "$FRONTEND_CONFIG" "$BACKEND_CONFIG"
sudo chmod 644 "$FRONTEND_CONFIG" "$BACKEND_CONFIG"
echo "[5/6] Configuring network policies with iptables..."
# Create custom iptables chains
CHAINS=("PODMAN_FRONTEND_POLICY" "PODMAN_BACKEND_POLICY" "PODMAN_MICROSEGMENT")
for chain in "${CHAINS[@]}"; do
CLEANUP_CHAINS+=("$chain")
sudo iptables -t filter -N "$chain" 2>/dev/null || true
sudo iptables -t filter -F "$chain"
done
# Frontend network policies (10.89.0.0/24)
sudo iptables -t filter -A PODMAN_FRONTEND_POLICY -s 10.89.0.0/24 -p tcp --dport 80 -j ACCEPT
sudo iptables -t filter -A PODMAN_FRONTEND_POLICY -s 10.89.0.0/24 -p tcp --dport 443 -j ACCEPT
sudo iptables -t filter -A PODMAN_FRONTEND_POLICY -s 10.89.0.0/24 -p udp --dport 53 -j ACCEPT
sudo iptables -t filter -A PODMAN_FRONTEND_POLICY -s 10.89.0.0/24 -p tcp --dport 53 -j ACCEPT
sudo iptables -t filter -A PODMAN_FRONTEND_POLICY -s 10.89.0.0/24 -d 10.90.0.0/24 -p tcp --dport 5432 -j ACCEPT
sudo iptables -t filter -A PODMAN_FRONTEND_POLICY -s 10.89.0.0/24 -d 10.90.0.0/24 -p tcp --dport 6379 -j ACCEPT
# Backend network policies (10.90.0.0/24)
sudo iptables -t filter -A PODMAN_BACKEND_POLICY -s 10.90.0.0/24 ! -d 10.0.0.0/8 -j DROP
sudo iptables -t filter -A PODMAN_BACKEND_POLICY -s 10.90.0.0/24 -d 10.89.0.0/24 -m state --state ESTABLISHED,RELATED -j ACCEPT
# Microsegmentation rules
sudo iptables -t filter -A PODMAN_MICROSEGMENT -s 10.89.0.0/24 -d 10.89.0.0/24 -j DROP
sudo iptables -t filter -A PODMAN_MICROSEGMENT -s 10.90.0.0/24 -d 10.90.0.0/24 -j DROP
echo "[6/6] Verifying installation..."
# Verify networks are available
if podman network ls | grep -q "secure-frontend" && podman network ls | grep -q "secure-backend"; then
log_info "✓ Secure networks created successfully"
else
log_warn "Networks may need to be recreated after reboot"
fi
# Verify iptables chains
CHAINS_OK=true
for chain in "${CHAINS[@]}"; do
if ! sudo iptables -t filter -L "$chain" >/dev/null 2>&1; then
CHAINS_OK=false
break
fi
done
if [[ "$CHAINS_OK" == true ]]; then
log_info "✓ Network policy chains configured successfully"
else
log_error "Some iptables chains failed to configure"
exit 1
fi
log_info "✓ Podman security configuration completed successfully!"
echo ""
echo "Next steps:"
echo "1. Create pods with: podman pod create --network secure-frontend --name frontend-pod"
echo "2. Create backend pods with: podman pod create --network secure-backend --name backend-pod"
echo "3. Test network isolation between pods"
echo "4. Use --cleanup flag to remove all configurations"
Review the script before running. Execute with: bash install.sh