Forked from rameerez/kamal-production-server-setup.sh
Created
November 21, 2024 11:48
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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