Update install_server_setup.sh
This commit is contained in:
parent
a36614af4b
commit
68108dac3b
1 changed files with 0 additions and 0 deletions
|
|
@ -1,460 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# shellcheck disable=SC2162
|
||||
set -euo pipefail
|
||||
|
||||
# Debian 13 (Trixie) provisioning — one-link + users-file capable (repo-safe)
|
||||
# - Optional Docker
|
||||
# - Preload users from CSV/URL (USERS_SPEC) + interactive add
|
||||
# - Per-user SSH key install (validated), sudo group add, NOPASSWD (confirm per group)
|
||||
# - Root SSH key (optional) + root password rotation (saved, not printed)
|
||||
# - Special "Niko" helper (full name: Niko Andreopoulos) if you want it
|
||||
# - SSH hardening (after verifying second session)
|
||||
# - UFW + Fail2Ban
|
||||
# - Credentials saved to /root/credentials-<timestamp>.txt (0600) or GPG-encrypted if root key exists
|
||||
#
|
||||
# ENV (optional):
|
||||
# USERS_SPEC=<http(s)://... | /path/to/users.csv> # Users list to preload
|
||||
# ROOT_SSH_PUBKEY="ssh-ed25519 AAAA... root@host"
|
||||
# NIKO_SSH_PUBKEY="ssh-ed25519 AAAA... niko@host"
|
||||
#
|
||||
# SAFE FOR PUBLIC REPO:
|
||||
# - No secrets printed to stdout
|
||||
# - Do NOT commit real keys or credential outputs
|
||||
|
||||
################################
|
||||
# Helpers
|
||||
################################
|
||||
prompt_yn() {
|
||||
local q="$1"; local def="${2:-Y}"; local ans prompt
|
||||
case "$def" in Y|y) prompt=" [Y/n] ";; N|n) prompt=" [y/N] ";; *) prompt=" [y/n] ";; esac
|
||||
while true; do
|
||||
read -r -p "$q$prompt" ans || true
|
||||
ans="${ans:-$def}"
|
||||
case "$ans" in Y|y|yes|YES) return 0;; N|n|no|NO) return 1;; *) echo "Please answer y or n.";; esac
|
||||
done
|
||||
}
|
||||
|
||||
require_root() {
|
||||
if [[ "$(id -u)" -ne 0 ]]; then
|
||||
echo "Please run as root (use sudo)." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
gen_password() {
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
openssl rand -base64 24 | tr -d '/+=' | cut -c1-22
|
||||
else
|
||||
tr -dc 'A-Za-z0-9' </dev/urandom | head -c22
|
||||
fi
|
||||
}
|
||||
|
||||
validate_pubkey() {
|
||||
local key="$1"
|
||||
[[ -z "${key// /}" ]] && return 1
|
||||
if command -v ssh-keygen >/dev/null 2>&1; then
|
||||
ssh-keygen -l -f - <<<"$key" >/dev/null 2>&1 || return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
ensure_pkg_tools() {
|
||||
apt-get update -y
|
||||
apt-get install -y ca-certificates curl gnupg lsb-release openssh-client
|
||||
}
|
||||
|
||||
################################
|
||||
# Docker (optional)
|
||||
################################
|
||||
install_docker() {
|
||||
echo "Installing Docker from the official repository..."
|
||||
apt-get remove -y docker docker-engine docker.io containerd runc || true
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "Docker GPG key fingerprint:"
|
||||
gpg --show-keys --with-colons /etc/apt/keyrings/docker.gpg | awk -F: '/^fpr:/ {print $10; exit}'
|
||||
if ! prompt_yn "Does this match the official Docker docs?" Y; then
|
||||
echo "Aborting per user choice."
|
||||
exit 1
|
||||
fi
|
||||
. /etc/os-release
|
||||
CODENAME="${VERSION_CODENAME:-trixie}"
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $CODENAME stable" \
|
||||
> /etc/apt/sources.list.d/docker.list
|
||||
apt-get update -y
|
||||
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
systemctl enable --now docker
|
||||
echo "Docker installed and started."
|
||||
}
|
||||
|
||||
################################
|
||||
# State + credential file
|
||||
################################
|
||||
declare -A GENERATED_PASSWORDS # username->password
|
||||
declare -A INSTALLED_PUBKEYS # username->yes
|
||||
declare -a CREATED_USERS
|
||||
declare -A GROUP_CONFIRMED # group->confirmed NOPASSWD
|
||||
|
||||
CRED_FILE=""
|
||||
cred_file_init() {
|
||||
local ts; ts="$(date +%F-%H%M%S)"
|
||||
if command -v gpg >/dev/null 2>&1 && gpg --list-keys root >/dev/null 2>&1; then
|
||||
CRED_FILE="/root/credentials-${ts}.txt.gpg"
|
||||
: > /root/credentials-tmp.txt
|
||||
chmod 600 /root/credentials-tmp.txt
|
||||
else
|
||||
CRED_FILE="/root/credentials-${ts}.txt"
|
||||
: > "$CRED_FILE"
|
||||
chmod 600 "$CRED_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
cred_append() {
|
||||
local line="$1"
|
||||
if [[ "$CRED_FILE" == *.gpg ]]; then
|
||||
printf "%s\n" "$line" >> /root/credentials-tmp.txt
|
||||
else
|
||||
printf "%s\n" "$line" >> "$CRED_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
cred_file_finalize() {
|
||||
if [[ "$CRED_FILE" == *.gpg ]]; then
|
||||
gpg --yes --batch -r root -e /root/credentials-tmp.txt
|
||||
mv /root/credentials-tmp.txt.gpg "$CRED_FILE"
|
||||
rm -f /root/credentials-tmp.txt
|
||||
chmod 600 "$CRED_FILE"
|
||||
echo "Encrypted credentials written to: $CRED_FILE"
|
||||
else
|
||||
echo "Credentials written to: $CRED_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
################################
|
||||
# Users & SSH
|
||||
################################
|
||||
create_user_if_needed() {
|
||||
local username="$1"; local gecos="$2"
|
||||
if id "$username" &>/dev/null; then
|
||||
echo "User '$username' already exists."
|
||||
else
|
||||
adduser --disabled-password --gecos "$gecos" "$username"
|
||||
CREATED_USERS+=("$username")
|
||||
echo "Created user: $username"
|
||||
fi
|
||||
}
|
||||
|
||||
set_user_password() {
|
||||
local username="$1"
|
||||
local pw; pw="$(gen_password)"
|
||||
GENERATED_PASSWORDS["$username"]="$pw"
|
||||
echo "${username}:${pw}" | chpasswd
|
||||
cred_append "user ${username} password: ${pw}"
|
||||
}
|
||||
|
||||
install_user_key() {
|
||||
local username="$1"; local pubkey="$2"
|
||||
if ! validate_pubkey "$pubkey"; then
|
||||
echo "Invalid SSH key for '$username'; skipping."
|
||||
return
|
||||
fi
|
||||
local homedir; homedir=$(getent passwd "$username" | cut -d: -f6)
|
||||
install -d -m 700 -o "$username" -g "$username" "$homedir/.ssh"
|
||||
touch "$homedir/.ssh/authorized_keys"
|
||||
chmod 600 "$homedir/.ssh/authorized_keys"
|
||||
chown "$username:$username" "$homedir/.ssh/authorized_keys"
|
||||
if ! grep -Fxq "$pubkey" "$homedir/.ssh/authorized_keys"; then
|
||||
echo "$pubkey" >> "$homedir/.ssh/authorized_keys"
|
||||
chown "$username:$username" "$homedir/.ssh/authorized_keys"
|
||||
INSTALLED_PUBKEYS["$username"]="yes"
|
||||
echo "Installed SSH key for $username"
|
||||
else
|
||||
echo "Key already present for $username"
|
||||
fi
|
||||
}
|
||||
|
||||
add_user_to_group_nopasswd() {
|
||||
local username="$1"; local group="$2"
|
||||
[[ -z "${group// /}" ]] && return 0
|
||||
if ! getent group "$group" >/dev/null; then
|
||||
groupadd "$group"
|
||||
echo "Group '$group' created."
|
||||
fi
|
||||
|
||||
# Create sudoers drop-in once per group (ask/confirm once)
|
||||
if [[ -z "${GROUP_CONFIRMED[$group]:-}" ]]; then
|
||||
echo "About to grant NOPASSWD:ALL to group '$group'."
|
||||
if prompt_yn "Proceed with passwordless sudo for '$group'?" N; then
|
||||
local sudoers_file="/etc/sudoers.d/${group}-nopasswd"
|
||||
echo "%${group} ALL=(ALL) NOPASSWD:ALL" > "$sudoers_file"
|
||||
chmod 440 "$sudoers_file"
|
||||
visudo -cf "$sudoers_file" >/dev/null
|
||||
GROUP_CONFIRMED[$group]="yes"
|
||||
echo "NOPASSWD sudo enabled for group '$group'."
|
||||
else
|
||||
GROUP_CONFIRMED[$group]="no"
|
||||
echo "Skipped NOPASSWD for group '$group'."
|
||||
fi
|
||||
fi
|
||||
|
||||
usermod -aG "$group" "$username"
|
||||
echo "Added '$username' to '$group'."
|
||||
}
|
||||
|
||||
################################
|
||||
# SSH hardening / firewall
|
||||
################################
|
||||
harden_ssh() {
|
||||
local sshd="/etc/ssh/sshd_config"; local changed=0
|
||||
echo "Open a SECOND SSH session now and confirm key login works."
|
||||
if ! prompt_yn "Proceed to SSH hardening?" Y; then
|
||||
echo "Skipping SSH hardening."
|
||||
return
|
||||
fi
|
||||
|
||||
if prompt_yn "Disable root SSH login?" Y; then
|
||||
if grep -qE '^\s*PermitRootLogin' "$sshd"; then
|
||||
sed -ri 's/^\s*PermitRootLogin.*/PermitRootLogin no/' "$sshd"
|
||||
else
|
||||
echo "PermitRootLogin no" >> "$sshd"
|
||||
fi
|
||||
changed=1
|
||||
fi
|
||||
if prompt_yn "Disable SSH password authentication (key-only)?" Y; then
|
||||
if grep -qE '^\s*PasswordAuthentication' "$sshd"; then
|
||||
sed -ri 's/^\s*PasswordAuthentication.*/PasswordAuthentication no/' "$sshd"
|
||||
else
|
||||
echo "PasswordAuthentication no" >> "$sshd"
|
||||
fi
|
||||
changed=1
|
||||
fi
|
||||
if prompt_yn "Change SSH port from 22?" N; then
|
||||
read -r -p "New port (1-65535): " newport
|
||||
if [[ "$newport" =~ ^[0-9]+$ ]] && (( newport >= 1 && newport <= 65535 )); then
|
||||
if grep -qE '^\s*Port' "$sshd"; then
|
||||
sed -ri "s/^\s*Port.*/Port $newport/" "$sshd"
|
||||
else
|
||||
echo "Port $newport" >> "$sshd"
|
||||
fi
|
||||
changed=1
|
||||
echo "SSH port set to $newport."
|
||||
else
|
||||
echo "Invalid port; skipping."
|
||||
fi
|
||||
fi
|
||||
(( changed )) && { systemctl reload ssh || systemctl reload sshd || true; echo "sshd reloaded."; }
|
||||
}
|
||||
|
||||
setup_ufw() {
|
||||
apt-get install -y ufw
|
||||
ufw default deny incoming
|
||||
ufw default allow outgoing
|
||||
local ssh_port="22"
|
||||
if grep -qE '^\s*Port\s+[0-9]+' /etc/ssh/sshd_config; then
|
||||
ssh_port=$(awk '/^\s*Port/ {print $2; exit}' /etc/ssh/sshd_config)
|
||||
fi
|
||||
ufw allow "$ssh_port"/tcp
|
||||
if prompt_yn "Enable UFW now?" Y; then
|
||||
yes | ufw enable
|
||||
ufw status
|
||||
else
|
||||
echo "UFW not enabled."
|
||||
fi
|
||||
}
|
||||
|
||||
setup_fail2ban() {
|
||||
apt-get install -y fail2ban
|
||||
systemctl enable --now fail2ban
|
||||
mkdir -p /etc/fail2ban/jail.d
|
||||
cat > /etc/fail2ban/jail.d/ssh.local <<'EOF'
|
||||
[sshd]
|
||||
enabled = true
|
||||
mode = aggressive
|
||||
port = ssh
|
||||
filter = sshd
|
||||
backend = systemd
|
||||
maxretry = 5
|
||||
findtime = 10m
|
||||
bantime = 1h
|
||||
EOF
|
||||
systemctl restart fail2ban
|
||||
echo "Fail2Ban installed and enabled."
|
||||
}
|
||||
|
||||
################################
|
||||
# Preload: users file
|
||||
################################
|
||||
fetch_users_spec() {
|
||||
local spec="$1"
|
||||
[[ -z "${spec:-}" ]] && return 1
|
||||
if [[ "$spec" =~ ^https?:// ]]; then
|
||||
curl -fsSL "$spec"
|
||||
else
|
||||
cat "$spec"
|
||||
fi
|
||||
}
|
||||
|
||||
process_users_line() {
|
||||
# Simple CSV: username,Full Name,ssh-key,group
|
||||
# Note: no commas inside fields; lines starting with # are ignored
|
||||
local line="$1"
|
||||
[[ -z "${line// /}" ]] && return 0
|
||||
[[ "$line" =~ ^# ]] && return 0
|
||||
|
||||
IFS=',' read -r username fullname pubkey group <<< "$line"
|
||||
username="$(echo -n "${username:-}" | xargs)"
|
||||
fullname="$(echo -n "${fullname:-}" | xargs)"
|
||||
pubkey="$(echo -n "${pubkey:-}" | sed 's/^[ \t]*//;s/[ \t]*$//')"
|
||||
group="$(echo -n "${group:-}" | xargs)"
|
||||
|
||||
[[ -z "$username" ]] && { echo "Skipping line (no username): $line"; return 0; }
|
||||
|
||||
create_user_if_needed "$username" "$fullname"
|
||||
set_user_password "$username"
|
||||
[[ -n "$pubkey" ]] && install_user_key "$username" "$pubkey"
|
||||
[[ -n "$group" ]] && add_user_to_group_nopasswd "$username" "$group"
|
||||
}
|
||||
|
||||
preload_users_from_spec() {
|
||||
local spec="$1"
|
||||
echo "Preloading users from: $spec"
|
||||
local content
|
||||
content="$(fetch_users_spec "$spec")" || { echo "Failed to read USERS_SPEC '$spec'"; return 1; }
|
||||
while IFS= read -r line; do
|
||||
process_users_line "$line"
|
||||
done <<< "$content"
|
||||
}
|
||||
|
||||
################################
|
||||
# Summary (no secrets printed)
|
||||
################################
|
||||
print_summary() {
|
||||
echo
|
||||
echo "========== SUMMARY (no passwords shown) =========="
|
||||
echo "Users created:"
|
||||
if ((${#CREATED_USERS[@]})); then
|
||||
printf ' - %s\n' "${CREATED_USERS[@]}"
|
||||
else
|
||||
echo " - none"
|
||||
fi
|
||||
echo "SSH keys installed for:"
|
||||
if ((${#INSTALLED_PUBKEYS[@]})); then
|
||||
for u in "${!INSTALLED_PUBKEYS[@]}"; do echo " - $u"; done
|
||||
else
|
||||
echo " - none"
|
||||
fi
|
||||
echo "Sudoers drop-ins:"
|
||||
ls -1 /etc/sudoers.d || true
|
||||
echo "SSH settings:"
|
||||
grep -E '^(PermitRootLogin|PasswordAuthentication|Port)\s' /etc/ssh/sshd_config || true
|
||||
echo "UFW:"
|
||||
ufw status || echo "UFW not installed or inactive."
|
||||
echo "Fail2Ban:"
|
||||
systemctl is-active fail2ban >/dev/null 2>&1 && echo "active" || echo "inactive"
|
||||
echo "Docker:"
|
||||
systemctl is-active docker >/dev/null 2>&1 && echo "active" || echo "not installed/active"
|
||||
echo "Credentials saved to: $CRED_FILE"
|
||||
echo "=================================================="
|
||||
}
|
||||
|
||||
################################
|
||||
# Main
|
||||
################################
|
||||
main() {
|
||||
require_root
|
||||
ensure_pkg_tools
|
||||
cred_file_init
|
||||
|
||||
# Docker
|
||||
if prompt_yn "Install Docker?" N; then
|
||||
install_docker
|
||||
fi
|
||||
|
||||
# Root SSH key
|
||||
local ROOT_KEY="${ROOT_SSH_PUBKEY:-}"
|
||||
if [[ -z "$ROOT_KEY" ]] && prompt_yn "Install a public SSH key for root?" Y; then
|
||||
echo "Paste root SSH public key (single line):"
|
||||
read ROOT_KEY || true
|
||||
fi
|
||||
if [[ -n "${ROOT_KEY// /}" ]] && validate_pubkey "$ROOT_KEY"; then
|
||||
install -d -m 700 /root/.ssh
|
||||
touch /root/.ssh/authorized_keys
|
||||
chmod 600 /root/.ssh/authorized_keys
|
||||
if ! grep -Fxq "$ROOT_KEY" /root/.ssh/authorized_keys 2>/dev/null; then
|
||||
echo "$ROOT_KEY" >> /root/.ssh/authorized_keys
|
||||
INSTALLED_PUBKEYS["root"]="yes"
|
||||
echo "Installed root SSH key."
|
||||
else
|
||||
echo "Root SSH key already present."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Root password
|
||||
if prompt_yn "Generate and set a new root password? (saved to credentials file)" Y; then
|
||||
local rpw; rpw="$(gen_password)"
|
||||
GENERATED_PASSWORDS["root"]="$rpw"
|
||||
echo "root:${rpw}" | chpasswd
|
||||
cred_append "root password: ${rpw}"
|
||||
else
|
||||
cred_append "root password: (unchanged)"
|
||||
fi
|
||||
|
||||
# Niko (optional)
|
||||
if prompt_yn "Create user 'Niko' (full: 'Niko Andreopoulos') and assign your key?" Y; then
|
||||
create_user_if_needed "Niko" "Niko Andreopoulos"
|
||||
set_user_password "Niko"
|
||||
local NIKO_KEY="${NIKO_SSH_PUBKEY:-}"
|
||||
if [[ -z "$NIKO_KEY" ]] && prompt_yn "Paste an SSH public key for Niko?" Y; then
|
||||
echo "Paste SSH public key for Niko (single line):"
|
||||
read NIKO_KEY || true
|
||||
fi
|
||||
[[ -n "${NIKO_KEY// /}" ]] && install_user_key "Niko" "$NIKO_KEY"
|
||||
fi
|
||||
|
||||
# Preload from USERS_SPEC
|
||||
if [[ -n "${USERS_SPEC:-}" ]]; then
|
||||
preload_users_from_spec "$USERS_SPEC" || true
|
||||
fi
|
||||
|
||||
# Interactive additional users
|
||||
if prompt_yn "Would you like to add additional users now?" Y; then
|
||||
echo "Enter a comma-separated list (e.g., dev1,dev2), or leave empty to skip:"
|
||||
read others || true
|
||||
if [[ -n "${others// /}" ]]; then
|
||||
IFS=',' read -r -a arr <<< "$others"
|
||||
for u in "${arr[@]}"; do
|
||||
u="$(echo "$u" | xargs)"; [[ -z "$u" ]] && continue
|
||||
read -r -p "Full name for '$u' (optional): " fullname || true
|
||||
create_user_if_needed "$u" "${fullname:-}"
|
||||
set_user_password "$u"
|
||||
if prompt_yn "Add an SSH public key for '$u'?" Y; then
|
||||
echo "Paste SSH public key for $u (single line):"
|
||||
read key || true
|
||||
[[ -n "${key// /}" ]] && install_user_key "$u" "$key"
|
||||
fi
|
||||
if prompt_yn "Add '$u' to a sudo group?" N; then
|
||||
read -r -p "Enter sudo group name: " gname || true
|
||||
[[ -n "${gname// /}" ]] && add_user_to_group_nopasswd "$u" "$gname"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# Security
|
||||
if prompt_yn "Apply SSH hardening (after verifying second session)?" Y; then
|
||||
harden_ssh
|
||||
fi
|
||||
if prompt_yn "Install & configure UFW?" Y; then
|
||||
setup_ufw
|
||||
fi
|
||||
if prompt_yn "Install & enable Fail2Ban?" Y; then
|
||||
setup_fail2ban
|
||||
fi
|
||||
|
||||
cred_file_finalize
|
||||
print_summary
|
||||
echo "Done."
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Loading…
Add table
Add a link
Reference in a new issue