non-interactive --yes: ask() assumes prompt default (no read); timezone defaults UTC; --username implies force-create; create-user name defaults to mk under --yes

This commit is contained in:
mk 2026-07-28 05:21:23 +03:00
parent 34137d185e
commit 8930e7aa76
3 changed files with 99 additions and 4 deletions

View File

@ -2,9 +2,15 @@
# prompt.sh — plain y/N prompts.
# ask "prompt?" [default y|n] -> sets REPLY
# Under ${ALL:-0} (the --yes flag) assume the stated default without
# reading stdin, so a fully non-interactive run never blocks on a read.
ask() {
local p="$1" d="${2:-y}" hint
if [[ "$d" == "y" ]]; then hint="[Y/n]"; else hint="[y/N]"; fi
if (( ${ALL:-0} )); then
REPLY="$d"
return
fi
read -rp "$p $hint " REPLY
[[ -z "$REPLY" ]] && REPLY="$d"
}

View File

@ -14,6 +14,7 @@
# - Root + no SUDO_USER + Debian/Ubuntu -> ask y/N (optional, apt works as root).
# - --create-user flag -> force prompt regardless.
# - --username NAME -> override default name, no prompt.
# - --yes (no --username) -> default to 'mk' silently (no stall).
#
# Passwordless, no user password (NOPASSWD). Idempotent: re-runs re-sync
# authorized_keys + sudoers rather than failing.
@ -40,7 +41,8 @@ run_create_user() {
_handle_direct_root() {
local should_create=0
if (( CREATE_USER )); then
if (( CREATE_USER )) || [[ -n "${USERNAME_ARG:-}" ]]; then
[[ -n "${USERNAME_ARG:-}" ]] && log "--username given: forcing user creation"
should_create=1
elif [[ "$DISTRO" == "arch" ]]; then
# AUR/makepkg cannot run as root — we genuinely need a non-root sudoer.

View File

@ -2,7 +2,7 @@
# 50-hardening — system defaults. Each sub-step y/N, all idempotent.
step_name="hardening"
step_desc="ssh, ufw, updates, unattended, tz, hostname, swap, locale, fail2ban, angie config"
step_desc="ssh, ufw, updates, unattended, tz, hostname, swap, locale, fail2ban, angie config + built-in ACME"
step_run="run_hardening"
run_hardening() {
@ -15,6 +15,7 @@ run_hardening() {
_h_locale
_h_fail2ban
_h_angie_config
_h_angie_acme
ok "hardening pass complete"
}
@ -104,8 +105,12 @@ _h_tz_hostname() {
if command -v timedatectl >/dev/null 2>&1; then
if yn "Set timezone (default UTC)?" y; then
local tz="${REPLY_TZ:-}"
read -rp " timezone [UTC]: " tz
[[ -z "$tz" ]] && tz="UTC"
if (( ${ALL:-0} )); then
tz="UTC"; ok "--yes: timezone defaulting to UTC"
else
read -rp " timezone [UTC]: " tz
[[ -z "$tz" ]] && tz="UTC"
fi
sudo_ timedatectl set-timezone "$tz" && ok "timezone set to $tz"
fi
else
@ -367,4 +372,86 @@ if command -v angie >/dev/null 2>&1; then
fi
EOF
sudo_ chmod +x /usr/local/bin/angie-enable /usr/local/bin/angie-disable
}
# ── angie built-in ACME (Let's Encrypt) ────────────────────────────────────
# No certbot: Angie ships http_acme and fetches/renews certs itself.
# Bootstrap can't know your domain/email, so we ship an example TLS server
# target + an `angie-issue` helper that creates the live acme_client on demand.
_h_angie_acme() {
if ! command -v angie >/dev/null 2>&1; then
echo " skip angie ACME (angie not installed)"; return
fi
if ! yn "Ship Angie built-in ACME template + angie-issue helper?" y; then
echo " skip angie ACME"; return
fi
local d=/etc/angie
# example TLS server target — NOT auto-enabled (.example, so angie-enable won't glob it)
if [[ ! -f "$d/targets/example-https.conf.example" ]]; then
sudo_ tee "$d/targets/example-https.conf.example" >/dev/null <<'EOF'
# Template for a TLS host using Angie built-in ACME.
# Use it via the helper: angie-issue <domain> [you@email]
# Or by hand: copy to targets/<domain>.conf, fill <DOMAIN>, then: angie-enable <domain>
server {
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name <DOMAIN>;
acme default; # add this server_name to the shared 'default' ACME certificate
ssl_certificate $acme_cert_default;
ssl_certificate_key $acme_cert_key_default;
# http -> https redirect
if ($scheme = http) { return 301 https://$host$request_uri; }
location / {
# replace with your app / proxy_pass upstream;
return 200 "angie + builtin ACME ok\n";
add_header Content-Type text/plain;
}
}
EOF
fi
sudo_ tee /usr/local/bin/angie-issue >/dev/null <<'EOF'
#!/usr/bin/env bash
# angie-issue <domain> [email] — create a TLS host backed by Angie built-in ACME.
# First call (needs email) writes /etc/angie/modules/http/acme.conf with the
# shared 'default' acme_client; every call writes targets/<domain>.conf and enables it.
# All enabled domains using `acme default` share one cert covering all their server_names.
set -euo pipefail
domain="${1:-}"; email="${2:-}"
[ -n "$domain" ] || { echo "usage: angie-issue <domain> [email]" >&2; exit 2; }
acme_conf=/etc/angie/modules/http/acme.conf
if [ ! -f "$acme_conf" ]; then
[ -n "$email" ] || { echo "first-time: also pass your email for Let's Encrypt" >&2; exit 2; }
cat > "$acme_conf" <<ACME
# managed by angie-issue — Angie built-in ACME (Let's Encrypt)
# resolver is required by the acme_client directive (http context).
resolver 1.1.1.1 8.8.8.8 valid=300s ipv6=off;
acme_client default https://acme-v02.api.letsencrypt.org/directory email=${email};
ACME
echo "wrote $acme_conf (email=${email})"
else
echo "_existing $acme_conf (shared default client)"
fi
target=/etc/angie/targets/${domain}.conf
sed -e "s|<DOMAIN>|${domain}|g" /etc/angie/targets/example-https.conf.example > "$target"
echo "wrote $target"
ln -sfn "../targets/${domain}.conf" /etc/angie/_on/${domain}.conf
echo "enabled: $domain"
sudo angie -t
sudo systemctl reload angie && echo "angie reloaded — certificate is obtained automatically."
EOF
sudo_ chmod +x /usr/local/bin/angie-issue
ok "angie ACME ready: example host at $d/targets/example-https.conf.example"
echo " issue a cert: angie-issue <domain> you@email"
echo " then reload picks it up; Angie renews automatically before expiry."
}