Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save johan--/b2523ef77dd92bd963417652c20fece9 to your computer and use it in GitHub Desktop.

Select an option

Save johan--/b2523ef77dd92bd963417652c20fece9 to your computer and use it in GitHub Desktop.
Set up a Ubuntu server to deploy Kamal 2.x Docker containers to, hardened security and production ready
#!/bin/bash
# Production Kamal Docker Host Setup Script
# Sets up a secure, production-ready Docker host on Ubuntu Server 24.04 LTS
# Ready for Kamal 2.x deployments
set -euo pipefail
# --- AESTHETICS ---
# Define color codes for echo messages
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
# Define the escape sequence for the alien emoji (U+1F47D)
ALIEN='\xF0\x9F\x91\xBD'
# Define the variable for resetting the color back to the default
NC='\033[0m'
# Function to print colorized output with alien emoji
print_message() {
local color=$1
local message=$2
echo -e "${color}${ALIEN} ${message}${NC}"
}
# Function to handle errors
handle_error() {
print_message "${RED}" "An error occurred. Exiting..."
exit 1
}
# Set up error handling
trap 'handle_error' ERR
# Check if script is run as root
if [[ $EUID -ne 0 ]]; then
print_message "${RED}" "This script must be run as root"
exit 1
fi
# System Updates
print_message "${YELLOW}" "Updating system packages..."
apt-get update && DEBIAN_FRONTEND=noninteractive apt-get upgrade -y
# Install essential packages
print_message "${YELLOW}" "Installing essential packages..."
DEBIAN_FRONTEND=noninteractive apt-get install -y \
ufw \
fail2ban \
curl \
wget \
gnupg \
lsb-release \
ca-certificates \
apt-transport-https \
software-properties-common \
sysstat \
auditd \
audispd-plugins \
unattended-upgrades \
acl
# Configure UFW
print_message "${YELLOW}" "Configuring firewall..."
ufw --force reset
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow http
ufw allow https
echo "y" | ufw enable
# Configure fail2ban with aggressive settings
print_message "${YELLOW}" "Configuring fail2ban..."
cat <<EOF > /etc/fail2ban/jail.local
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
[docker-login]
enabled = true
port = http,https
filter = apache-auth
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
EOF
systemctl enable fail2ban
systemctl restart fail2ban
# Install Docker
print_message "${YELLOW}" "Installing Docker..."
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
rm get-docker.sh
# Docker Configuration
print_message "${YELLOW}" "Configuring Docker..."
mkdir -p /etc/docker
cat <<EOF > /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"icc": false,
"live-restore": true,
"userland-proxy": false,
"no-new-privileges": true,
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 64000,
"Soft": 64000
}
},
"metrics-addr": "0.0.0.0:9323",
"experimental": true,
"features": {
"buildkit": true
}
}
EOF
systemctl enable docker
systemctl restart docker
# Set up Docker user
print_message "${YELLOW}" "Creating docker user..."
adduser --system --group --shell /bin/bash --home /home/docker --disabled-password docker
usermod -aG docker docker
# Set up SSH for docker user
print_message "${YELLOW}" "Configuring SSH..."
mkdir -p /home/docker/.ssh
chmod 700 /home/docker/.ssh
if [ -f /root/.ssh/authorized_keys ]; then
cp /root/.ssh/authorized_keys /home/docker/.ssh/
chmod 600 /home/docker/.ssh/authorized_keys
chown -R docker:docker /home/docker/.ssh
else
print_message "${RED}" "No SSH key found in /root/.ssh/authorized_keys"
exit 1
fi
# SSH Hardening
cat <<EOF > /etc/ssh/sshd_config
Include /etc/ssh/sshd_config.d/*.conf
Port 22
AddressFamily inet
Protocol 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
SyslogFacility AUTH
LogLevel VERBOSE
LoginGraceTime 20
PermitRootLogin no
StrictModes yes
MaxAuthTries 3
MaxSessions 2
PubkeyAuthentication yes
AuthenticationMethods publickey
HostbasedAuthentication no
IgnoreRhosts yes
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
UsePAM no
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
PermitTTY yes
PrintMotd no
ClientAliveInterval 300
ClientAliveCountMax 2
TCPKeepAlive no
AllowUsers docker
KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
EOF
# Restart SSH service - now using correct service name
if systemctl is-active --quiet ssh; then
systemctl restart ssh
elif systemctl is-active --quiet sshd; then
systemctl restart sshd
else
print_message "${RED}" "SSH service not found. Please check your SSH installation."
exit 1
fi
# Kamal-specific sudo permissions
print_message "${YELLOW}" "Configuring sudo for Kamal..."
cat <<EOF > /etc/sudoers.d/docker-user
docker ALL=(ALL) NOPASSWD: /usr/sbin/service docker restart
docker ALL=(ALL) NOPASSWD: /bin/systemctl restart docker
EOF
chmod 440 /etc/sudoers.d/docker-user
# System hardening
print_message "${YELLOW}" "Hardening system..."
cat <<EOF >> /etc/sysctl.conf
# Network security
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0
net.ipv4.tcp_syncookies = 1
# Docker needs IPv4 forwarding
net.ipv4.ip_forward = 1
# System limits for Kamal/Docker
fs.file-max = 65535
kernel.pid_max = 65536
net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.tcp_tw_reuse = 1
vm.max_map_count = 262144
EOF
sysctl -p
# Log rotation for Docker
cat <<EOF > /etc/logrotate.d/docker-logs
/var/lib/docker/containers/*/*.log {
rotate 7
daily
compress
missingok
delaycompress
copytruncate
size 100M
}
EOF
# Enable security services
systemctl enable sysstat auditd
systemctl start sysstat auditd
# Configure unattended upgrades
cat <<EOF > /etc/apt/apt.conf.d/50unattended-upgrades
Unattended-Upgrade::Allowed-Origins {
"\${distro_id}:\${distro_codename}";
"\${distro_id}:\${distro_codename}-security";
"\${distro_id}ESMApps:\${distro_codename}-apps-security";
"\${distro_id}ESM:\${distro_codename}-infra-security";
};
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Remove-New-Unused-Dependencies "true";
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
Unattended-Upgrade::MinimalSteps "true";
EOF
# Clean up
apt-get autoremove -y
apt-get clean
print_message "${GREEN}" "Setup complete! Please reboot the system."
print_message "${YELLOW}" "Important next steps:"
print_message "${YELLOW}" "1. Configure your load balancer to point to this host"
print_message "${YELLOW}" "2. Set up your .kamal/secrets file on your deployment machine"
print_message "${YELLOW}" "3. Run 'kamal deploy' to deploy your application"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment