#!/usr/bin/with-contenv bash set -euo pipefail echo "[custom-init] ssh + acl setup starting" # ---- user management (Alpine-friendly, idempotent) ---- if ! id -u testuser >/dev/null 2>&1; then # Alpine has adduser by default adduser -D -s /bin/bash testuser # UNLOCK the account for SSH publickey auth (BusyBox adduser -D sets '!' in /etc/shadow) # Convert "testuser:!:" (locked) -> "testuser::" (no password, unlocked) sed -i -e 's/^testuser:!:/testuser::/' -e 's/^testuser:\*:/testuser::/' /etc/shadow || true fi # ---- SSH host keys ---- ssh-keygen -A # ---- persistent client keypair in /mnt/ssh-keys ---- mkdir -p /mnt/ssh-keys if [ ! -f /mnt/ssh-keys/id_rsa ]; then echo "[custom-init] generating new SSH key pair in /mnt/ssh-keys" ssh-keygen -t rsa -b 4096 -m PEM -f /mnt/ssh-keys/id_rsa -N '' chmod 600 /mnt/ssh-keys/id_rsa chmod 644 /mnt/ssh-keys/id_rsa.pub fi # ---- authorize key for testuser ---- install -d -m 700 -o testuser -g testuser /home/testuser/.ssh cat /mnt/ssh-keys/id_rsa.pub > /home/testuser/.ssh/authorized_keys chown testuser:testuser /home/testuser/.ssh/authorized_keys chmod 600 /home/testuser/.ssh/authorized_keys # ---- sshd_config (idempotent; avoid duplicate lines every restart) ---- SSHD_CFG=/etc/ssh/sshd_config grep -qE '^PermitRootLogin\s+' "$SSHD_CFG" || echo 'PermitRootLogin no' >> "$SSHD_CFG" grep -qE '^PasswordAuthentication\s+' "$SSHD_CFG" || echo 'PasswordAuthentication no' >> "$SSHD_CFG" grep -qE '^KbdInteractiveAuthentication\s+' "$SSHD_CFG" || echo 'KbdInteractiveAuthentication no' >> "$SSHD_CFG" grep -qE '^PermitEmptyPasswords\s+' "$SSHD_CFG" || echo 'PermitEmptyPasswords no' >> "$SSHD_CFG" grep -qE '^PubkeyAuthentication\s+' "$SSHD_CFG" || echo 'PubkeyAuthentication yes' >> "$SSHD_CFG" grep -qE '^LogLevel\s+' "$SSHD_CFG" || echo 'LogLevel VERBOSE' >> "$SSHD_CFG" grep -qE '^AuthorizedKeysFile\s+' "$SSHD_CFG" || echo 'AuthorizedKeysFile .ssh/authorized_keys' >> "$SSHD_CFG" # ---- sudoers: use fail2ban-client (systemctl isn't present in LSIO containers) ---- # (LSIO support guidance is to control fail2ban via fail2ban-client, not systemctl/service) :contentReference[oaicite:5]{index=5} install -d -m 0750 /etc/sudoers.d cat > /etc/sudoers.d/fail2ban-ui <<'EOF' testuser ALL=(ALL) NOPASSWD: /usr/bin/fail2ban-client * EOF chmod 0440 /etc/sudoers.d/fail2ban-ui # ---- ACLs: run AFTER LSIO has created /config/fail2ban on first boot ---- # The container creates /config content on first run. :contentReference[oaicite:6]{index=6} mkdir -p /config/fail2ban/{action.d,filter.d,jail.d} # Apply both access ACL and default ACL (so new files/dirs inherit) # First apply recursively to existing files/directories, then set default ACLs for d in /config/fail2ban /config/fail2ban/action.d /config/fail2ban/filter.d /config/fail2ban/jail.d; do # Apply access ACL recursively to existing files/directories setfacl -R -m u:testuser:rwX,m::rwX "$d" # Set default ACL so new files/dirs inherit permissions setfacl -d -m u:testuser:rwX,m::rwX "$d" # Also ensure the directory itself has the access ACL setfacl -m u:testuser:rwX,m::rwX "$d" done # /etc/fail2ban is a symlink to /config/fail2ban in this image; setfacl follows symlinks by default. if [ -e /etc/fail2ban ]; then # Apply recursively to existing files/directories setfacl -R -m u:testuser:rwX,m::rwX /etc/fail2ban || true # Set default ACL setfacl -d -m u:testuser:rwX,m::rwX /etc/fail2ban || true # Ensure the symlink target has access ACL setfacl -m u:testuser:rwX,m::rwX /etc/fail2ban || true fi echo "[custom-init] ssh + acl setup complete"