setup-sudo-user.sh

Skript pre kapitolu Sudo user — preč od root prihlasovania

Veľkosť: 15440 B
Upravené: 2026-05-16 06:55 UTC
SHA256: 034c240c163b…
  1#!/usr/bin/env bash
  2# =============================================================================
  3# VCS Akadémia — Level 1 / Chapter 2: Sudo user setup
  4# -----------------------------------------------------------------------------
  5# Web:    https://vcs-akademia.net/kurz/level-1/sudo-user
  6# GitHub: github.com/virtucybersecurity/vcs-akademia
  7#
  8# Usage (run on the VPS, as root):
  9#   curl -O https://vcs-akademia.net/script/level-1/sudo-user/setup-sudo-user.sh
 10#   bash setup-sudo-user.sh
 11#
 12# =============================================================================
 13# PREČO TENTO SKRIPT EXISTUJE
 14# =============================================================================
 15#
 16# Root login je bezpečnostné riziko z troch konkrétnych dôvodov:
 17#
 18#   1. Žiadny audit trail.
 19#      Logy zobrazujú "root logged in", "root ran command X" — bez
 20#      informácie o tom, kto reálne za týmto bol. Pri incidente
 21#      forenzná analýza prakticky nemožná.
 22#
 23#   2. Univerzálny cieľ botnetov.
 24#      90% pokusov o brute-force SSH login mieri na 'root'. Premenovať
 25#      sa nedá, ale prihlasovanie ako root sa dá vypnúť (CH 4).
 26#
 27#   3. Katastrofické chyby.
 28#      'rm -rf /tmp/cache' ako root pri preklepe = celý filesystem.
 29#      Sudo vyžaduje vedomé povolenie pred deštruktívnymi operáciami.
 30#
 31# Tento skript rieši všetky tri:
 32#   - Vytvorí non-root používateľa s vlastným menom a heslom
 33#   - Pridá ho do sudo skupiny
 34#   - Otestuje, že sudo funguje pre nový účet
 35#   - Aktualizuje baseline dokumentáciu
 36#
 37# Skript NEVYPÍNA root SSH login — to robí CH 4 (SSH hardening),
 38# kde sa to dá robiť bezpečne so záložným plánom.
 39#
 40# =============================================================================
 41# AKO TO TENTO SKRIPT ROBÍ
 42# =============================================================================
 43#
 44# Skript beží PRIAMO NA VPS ako root. V 4 krokoch:
 45#
 46#   1. Overenie — bežíme ako root? Existuje balík sudo?
 47#   2. Vytvorenie používateľa cez adduser (interaktívne, pýta heslo).
 48#   3. Pridanie do sudo skupiny cez usermod -aG sudo (append, nie replace).
 49#   4. Test sudo z nového účtu (overí, že sudo skutočne funguje).
 50#
 51# Po úspešnom dokončení skript aktualizuje /root/server-baseline.txt
 52# o informáciu o novom používateľovi a vypíše ďalšie kroky.
 53#
 54# =============================================================================
 55
 56set -u
 57set -o pipefail
 58
 59# -----------------------------------------------------------------------------
 60# Colors (ANSI escape codes)
 61# -----------------------------------------------------------------------------
 62RED='\033[0;31m'
 63GREEN='\033[0;32m'
 64YELLOW='\033[1;33m'
 65BLUE='\033[0;34m'
 66NC='\033[0m'
 67
 68# -----------------------------------------------------------------------------
 69# Output helpers
 70# -----------------------------------------------------------------------------
 71print_info()    { printf "${BLUE}[INFO]${NC} %s\n" "$*"; }
 72print_success() { printf "${GREEN}[OK]${NC} %s\n" "$*"; }
 73print_warning() { printf "${YELLOW}[VAROVANIE]${NC} %s\n" "$*" >&2; }
 74print_error()   { printf "${RED}[CHYBA]${NC} %s\n" "$*" >&2; }
 75print_fatal()   { print_error "$*"; cleanup_on_exit; exit 1; }
 76
 77# -----------------------------------------------------------------------------
 78# Cleanup — remove the script file when we're done (success or failure).
 79# -----------------------------------------------------------------------------
 80SCRIPT_SOURCE="${BASH_SOURCE[0]:-$0}"
 81
 82cleanup_on_exit() {
 83    [ -f "$SCRIPT_SOURCE" ] || return 0
 84
 85    local script_dir script_name script_path
 86    script_dir="$(cd "$(dirname "$SCRIPT_SOURCE")" 2>/dev/null && pwd)" || return 0
 87    script_name="$(basename "$SCRIPT_SOURCE")"
 88    script_path="${script_dir}/${script_name}"
 89
 90    case "$script_name" in
 91        setup-sudo-user.sh) ;;
 92        *) return 0 ;;
 93    esac
 94
 95    case "$script_path" in
 96        /|/bin/*|/sbin/*|/usr/*|/etc/*|/var/*|/lib/*|/lib64/*|/boot/*)
 97            return 0 ;;
 98    esac
 99
100    [ -f "$script_path" ] && rm -f "$script_path" 2>/dev/null && \
101        print_info "Skript bol odstránený: $script_path"
102    return 0
103}
104
105trap cleanup_on_exit EXIT
106
107# -----------------------------------------------------------------------------
108# Pre-flight checks
109# -----------------------------------------------------------------------------
110preflight() {
111    # Must be Linux
112    if [ "$(uname -s)" != "Linux" ]; then
113        print_fatal "Tento skript sa spúšťa NA serveri (Linux), nie lokálne."
114    fi
115
116    # Must be root (we create users and modify groups)
117    if [ "$(id -u)" -ne 0 ]; then
118        print_fatal "Tento skript musí byť spustený ako root.
119
120Ak si na serveri ako bežný používateľ, prepni sa:
121  sudo bash setup-sudo-user.sh
122
123Ak si stratil root prístup, využi rescue mode poskytovateľa."
124    fi
125
126    # Must be Debian/Ubuntu based (uses adduser, apt)
127    if ! command -v adduser >/dev/null 2>&1; then
128        print_fatal "Tento skript podporuje len Debian/Ubuntu (adduser). Pre iné distribúcie použi ručný postup z kapitoly."
129    fi
130
131    # Check sudo is installed
132    if ! command -v sudo >/dev/null 2>&1; then
133        print_warning "Balík 'sudo' nie je nainštalovaný."
134        print_info "Inštalujem ho..."
135        if ! apt update >/dev/null 2>&1 || ! apt install -y sudo >/dev/null 2>&1; then
136            print_fatal "Nepodarilo sa nainštalovať sudo. Skontroluj sieť alebo apt."
137        fi
138        print_success "Balík sudo nainštalovaný."
139    fi
140}
141
142# -----------------------------------------------------------------------------
143# Confirmation prompt
144# -----------------------------------------------------------------------------
145confirm() {
146    local prompt="${1:-Pokračovať?}"
147    local reply
148    printf "${YELLOW}%s [y/N]:${NC} " "$prompt"
149    read -r reply </dev/tty
150    case "$reply" in
151        y|Y|yes|YES|ano|ANO) return 0 ;;
152        *) printf "${BLUE}[INFO]${NC} Zrušené.\n"; exit 0 ;;
153    esac
154}
155
156# -----------------------------------------------------------------------------
157# Ask for user input with optional default and validation
158# -----------------------------------------------------------------------------
159ask() {
160    local prompt="$1"
161    local default="${2:-}"
162    local reply
163    if [ -n "$default" ]; then
164        printf "%s [%s]: " "$prompt" "$default" >&2
165    else
166        printf "%s: " "$prompt" >&2
167    fi
168    read -r reply </dev/tty
169    if [ -z "$reply" ]; then
170        echo "$default"
171    else
172        echo "$reply"
173    fi
174}
175
176# -----------------------------------------------------------------------------
177# Validate username
178# Linux username conventions: lowercase, alphanumeric + hyphens, start with letter
179# -----------------------------------------------------------------------------
180validate_username() {
181    local name="$1"
182
183    # Check length (1-32 chars)
184    if [ -z "$name" ] || [ ${#name} -gt 32 ]; then
185        echo "Meno musí mať 1–32 znakov."
186        return 1
187    fi
188
189    # Check pattern: starts with lowercase letter, then alphanumeric/hyphen/underscore
190    if ! echo "$name" | grep -qE '^[a-z][a-z0-9_-]*$'; then
191        echo "Meno musí začínať malým písmenom a obsahovať len malé písmená, čísla, '_' a '-'."
192        return 1
193    fi
194
195    # Reject obvious bad names
196    case "$name" in
197        root|admin|administrator|superuser|god|toor|sudo)
198            echo "Meno '$name' je zlá voľba — provokuješ útočníkov. Použi tvoje skutočné meno."
199            return 1
200            ;;
201    esac
202
203    # Check if user already exists
204    if id "$name" >/dev/null 2>&1; then
205        echo "Používateľ '$name' už existuje."
206        return 1
207    fi
208
209    return 0
210}
211
212# -----------------------------------------------------------------------------
213# Create the new user (interactive — adduser asks for password)
214# -----------------------------------------------------------------------------
215create_user() {
216    local username="$1"
217
218    print_info "Vytváram používateľa '$username' cez adduser..."
219    print_info "adduser sa ťa opýta na heslo a pár nepovinných polí."
220    echo
221
222    # adduser is interactive — handles password prompt, home dir, default files
223    if ! adduser --gecos "" "$username"; then
224        print_fatal "adduser zlyhal."
225    fi
226
227    print_success "Používateľ '$username' vytvorený."
228}
229
230# -----------------------------------------------------------------------------
231# Add user to sudo group
232# -----------------------------------------------------------------------------
233add_to_sudo_group() {
234    local username="$1"
235
236    print_info "Pridávam '$username' do sudo skupiny..."
237
238    # -aG: append (a) to group(s) (G). Without -a, would replace all groups.
239    if ! usermod -aG sudo "$username"; then
240        print_fatal "usermod zlyhal."
241    fi
242
243    # Verify
244    if ! groups "$username" | grep -q '\bsudo\b'; then
245        print_fatal "Pridanie do sudo skupiny zlyhalo — '$username' nie je v skupine sudo."
246    fi
247
248    print_success "'$username' je teraz v skupine sudo."
249    print_info "Skupiny: $(groups "$username" | cut -d: -f2 | xargs)"
250}
251
252# -----------------------------------------------------------------------------
253# Test sudo functionality for new user
254# This is critical — we want to verify sudo actually works before celebrating.
255# -----------------------------------------------------------------------------
256test_sudo() {
257    local username="$1"
258
259    print_info "Testujem sudo z účtu '$username'..."
260    print_info "(Krátky príkaz, ktorý vyžaduje root práva.)"
261    echo
262
263    # Run 'true' (no-op) as the user via sudo. This forces password prompt.
264    # We capture output so we can interpret the result.
265    print_info "Zadaj heslo pre '$username' (nie root heslo):"
266
267    if su - "$username" -c 'sudo -k; sudo -p "[sudo] heslo pre %u: " true' 2>&1; then
268        print_success "Sudo funguje pre '$username'."
269        return 0
270    else
271        print_error "Sudo test zlyhal."
272        print_warning "Možné príčiny:"
273        print_warning "  - Nesprávne heslo (skús ešte raz spustením skriptu)"
274        print_warning "  - '$username' nie je v sudo skupine (overiť: 'groups $username')"
275        print_warning "  - Konfigurácia sudo zakazuje skupinu (overiť: '/etc/sudoers')"
276        return 1
277    fi
278}
279
280# -----------------------------------------------------------------------------
281# Update baseline document (if it exists)
282# -----------------------------------------------------------------------------
283update_baseline() {
284    local username="$1"
285    local baseline="/root/server-baseline.txt"
286
287    if [ ! -f "$baseline" ]; then
288        print_info "Baseline dokument neexistuje ($baseline)."
289        print_info "Ak si CH 1 ešte nedotiahol, môžeš ho pridať neskôr."
290        return 0
291    fi
292
293    print_info "Aktualizujem baseline ($baseline)..."
294
295    {
296        echo ""
297        echo "=== Sudo používateľ ==="
298        echo "Pridaný: $(date +%Y-%m-%d)"
299        echo "Meno: $username"
300        echo "UID: $(id -u "$username")"
301        echo "GID: $(id -g "$username")"
302        echo "Skupiny: $(groups "$username" | cut -d: -f2 | xargs)"
303        echo "Domov: $(getent passwd "$username" | cut -d: -f6)"
304    } >> "$baseline"
305
306    print_success "Baseline aktualizovaný."
307}
308
309# -----------------------------------------------------------------------------
310# Final summary
311# -----------------------------------------------------------------------------
312print_summary() {
313    local username="$1"
314
315    echo
316    printf "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
317    printf "${GREEN}  Sudo používateľ pripravený: HOTOVO${NC}\n"
318    printf "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
319    echo
320    printf "  Nový účet:          ${BLUE}%s${NC}\n" "$username"
321    printf "  Domovský adresár:   %s\n" "$(getent passwd "$username" | cut -d: -f6)"
322    printf "  Prístup k sudo:     ${GREEN}Áno${NC}\n"
323    echo
324    printf "${YELLOW}  ČO ROBIŤ TERAZ:${NC}\n"
325    echo
326    printf "  1. Odhlás sa z root účtu:\n"
327    printf "     ${BLUE}exit${NC}\n"
328    echo
329    printf "  2. Prihlás sa z lokálneho počítača ako '%s':\n" "$username"
330    printf "     ${BLUE}ssh %s@TVOJA_IP${NC}\n" "$username"
331    echo
332    printf "  3. Otestuj sudo z novej session:\n"
333    printf "     ${BLUE}sudo whoami${NC}\n"
334    printf "     (mal by si dostať 'root')\n"
335    echo
336    printf "${YELLOW}  Ďalej:${NC} CH 3 — SSH key authentication\n"
337    printf "  Pridáme SSH kľúč pre nový non-root účet, aby si sa\n"
338    printf "  prihlasoval bez hesla.\n"
339    printf "  https://vcs-akademia.net/kurz/level-1/ssh-key\n"
340    echo
341    print_warning "ZATIAĽ NEVYPÍNAJ root SSH login!"
342    print_warning "To robí CH 4 — bezpečne, so záložným plánom."
343    echo
344}
345
346# -----------------------------------------------------------------------------
347# MAIN
348# -----------------------------------------------------------------------------
349main() {
350    printf "\n${BLUE}=== VCS Akadémia — CH 2: Sudo používateľ ===${NC}\n\n"
351
352    cat <<EOF
353Tento skript urobí 4 kroky:
354  1. Overí, že bežíme ako root a máme nainštalovaný 'sudo'.
355  2. Vytvorí nového non-root používateľa (pýta meno + heslo).
356  3. Pridá ho do sudo skupiny.
357  4. Otestuje, že sudo z nového účtu skutočne funguje.
358
359BEZPEČNOSTNÁ POZNÁMKA:
360  Skript NEVYPÍNA root SSH login. Root účet zostane funkčný.
361  Vypnutie root SSH je obsahom CH 4 (SSH hardening), kde sa
362  to robí bezpečne, s testovaním pred vypnutím.
363
364EOF
365
366    confirm "Chceš pokračovať?"
367
368    printf "\n${BLUE}-- Pre-flight kontrola --${NC}\n"
369    preflight
370    print_success "Pripravený na vytvorenie používateľa."
371
372    printf "\n${BLUE}-- Krok 1/4 — Voľba mena --${NC}\n"
373    print_info "Konvencia pre meno používateľa:"
374    print_info "  - 1–32 znakov, len malé písmená, čísla, '_' a '-'"
375    print_info "  - Začína malým písmenom"
376    print_info "  - Rozumné: 'peter', 'mike', 'admin1', 'deploy-bot'"
377    print_info "  - Vyhni sa: 'root2', 'superuser', 'admin' (provokácia útočníkov)"
378    echo
379
380    local username
381    local validation_error
382    while true; do
383        username=$(ask "Meno nového používateľa" "")
384        validation_error=$(validate_username "$username")
385        if [ $? -eq 0 ]; then
386            break
387        fi
388        print_warning "$validation_error"
389        if ! confirm "Skúsiť znova?"; then
390            print_fatal "Zrušené."
391        fi
392    done
393
394    echo
395    print_info "Použijem meno: $username"
396    confirm "Pokračovať?"
397
398    printf "\n${BLUE}-- Krok 2/4 — Vytvorenie účtu --${NC}\n"
399    create_user "$username"
400
401    printf "\n${BLUE}-- Krok 3/4 — Sudo skupina --${NC}\n"
402    add_to_sudo_group "$username"
403
404    printf "\n${BLUE}-- Krok 4/4 — Test sudo --${NC}\n"
405    if ! test_sudo "$username"; then
406        print_warning "Sudo test zlyhal, ale účet '$username' bol vytvorený a pridaný do sudo skupiny."
407        print_warning "Skús sa prihlásiť ako '$username' manuálne a otestuj 'sudo whoami'."
408        # Don't fatal — user account exists, just couldn't verify
409    fi
410
411    # Update baseline (non-fatal if missing)
412    echo
413    update_baseline "$username"
414
415    print_summary "$username"
416}
417
418main "$@"
⤓ Stiahni raw skript ← Späť na kapitolu