Obsah

Instalace Claude Code na serveru (Debian)

Vytvořeno: 6.5.2026 | Aktualizováno: 06.05.2026 14:18

Claude Code je AI coding assistant od Anthropicu pro příkazovou řádku. Tento článek popisuje instalaci na Debianu a doporučenou konfiguraci oprávnění pro bezpečné použití na produkčním serveru.

Instalace

Instalace probíhá přes quickstart skript:

curl -fsSL https://claude.ai/install.sh | bash

Po instalaci je potřeba přidat ~/.local/bin do PATH:

echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && source ~/.bashrc

Poté je příkaz claude dostupný z příkazové řádky.

Příprava serveru

Pokud přistupuješ na server jako root bez nastaveného firewallu, je vhodné nejdříve vytvořit neprivilegovaného uživatele, přepnout SSH na nestandardní port a zapnout UFW. Následující skript to provede interaktivně:

#!/bin/bash
# Instalační skript: hostname + nový uživatel + passwordless sudo + SSH + UFW
set -e
 
# ── Root check ────────────────────────────────────────────────────────────────
if [[ $EUID -ne 0 ]]; then
    echo "Spusťte skript jako root: sudo bash $0"
    exit 1
fi
 
# ── Závislosti ────────────────────────────────────────────────────────────────
echo "[*] Kontrola závislostí..."
 
MISSING_PKGS=()
command -v adduser     &>/dev/null || MISSING_PKGS+=(adduser)
command -v visudo      &>/dev/null || MISSING_PKGS+=(sudo)
command -v ufw         &>/dev/null || MISSING_PKGS+=(ufw)
command -v sshd        &>/dev/null || MISSING_PKGS+=(openssh-server)
command -v hostnamectl &>/dev/null || MISSING_PKGS+=(systemd)
command -v jq          &>/dev/null || MISSING_PKGS+=(jq)
 
if [[ ${#MISSING_PKGS[@]} -gt 0 ]]; then
    echo "[*] Instaluji chybějící balíčky: ${MISSING_PKGS[*]}"
    apt-get update -qq
    apt-get install -y -qq "${MISSING_PKGS[@]}"
    echo "[OK] Závislosti nainstalovány."
else
    echo "[OK] Všechny závislosti jsou dostupné."
fi
 
echo ""
 
# ── Vstup ─────────────────────────────────────────────────────────────────────
read -rp "Vytvořit nového uživatele? [y/N] " CREATE_USER
 
if [[ "$CREATE_USER" =~ ^[Yy]$ ]]; then
    read -rp "Jméno nového uživatele : " NEW_USER
    if [[ -z "$NEW_USER" ]]; then
        echo "Jméno uživatele nesmí být prázdné."
        exit 1
    fi
fi
 
read -rp "Nový hostname serveru  : " NEW_HOSTNAME
if [[ -z "$NEW_HOSTNAME" ]]; then
    echo "Hostname nesmí být prázdný."
    exit 1
fi
 
echo ""
 
# ── Vytvoření uživatele ────────────────────────────────────────────────────────
if [[ "$CREATE_USER" =~ ^[Yy]$ ]]; then
    if id "$NEW_USER" &>/dev/null; then
        echo "[INFO] Uživatel '$NEW_USER' již existuje – přeskakuji vytvoření."
    else
        adduser --gecos "" "$NEW_USER"
        echo "[OK] Uživatel '$NEW_USER' vytvořen."
    fi
 
    usermod -aG sudo "$NEW_USER"
    echo "[OK] Uživatel '$NEW_USER' přidán do skupiny sudo."
 
    SUDOERS_FILE="/etc/sudoers.d/${NEW_USER}-nopasswd"
    echo "${NEW_USER} ALL=(ALL) NOPASSWD:ALL" > "$SUDOERS_FILE"
    chmod 440 "$SUDOERS_FILE"
    if visudo -cf "$SUDOERS_FILE"; then
        echo "[OK] Passwordless sudo nakonfigurováno ($SUDOERS_FILE)."
    else
        echo "[CHYBA] Neplatná konfigurace sudoers – mažu soubor."
        rm -f "$SUDOERS_FILE"
        exit 1
    fi
else
    echo "[INFO] Vytvoření uživatele přeskočeno."
fi
 
# ── SSH konfigurace ───────────────────────────────────────────────────────────
SSHD_CONFIG="/etc/ssh/sshd_config"
BACKUP="${SSHD_CONFIG}.bak.$(date +%Y%m%d_%H%M%S)"
cp "$SSHD_CONFIG" "$BACKUP"
echo "[OK] Záloha sshd_config → $BACKUP"
 
if grep -qE "^#?Port " "$SSHD_CONFIG"; then
    sed -i 's/^#\?Port .*/Port 2022/' "$SSHD_CONFIG"
else
    echo "Port 2022" >> "$SSHD_CONFIG"
fi
 
if grep -qE "^#?PermitRootLogin " "$SSHD_CONFIG"; then
    sed -i 's/^#\?PermitRootLogin .*/PermitRootLogin no/' "$SSHD_CONFIG"
else
    echo "PermitRootLogin no" >> "$SSHD_CONFIG"
fi
 
if sshd -t; then
    echo "[OK] Syntaxe sshd_config je v pořádku."
else
    echo "[CHYBA] Špatná konfigurace sshd – obnovuji zálohu!"
    cp "$BACKUP" "$SSHD_CONFIG"
    exit 1
fi
 
# ── Hostname ──────────────────────────────────────────────────────────────────
hostnamectl set-hostname "$NEW_HOSTNAME"
echo "[OK] Hostname nastaven na '$NEW_HOSTNAME'."
 
# ── UFW pravidla (firewall se zatím nezapíná) ─────────────────────────────────
ufw allow 2022/tcp
echo "[OK] UFW pravidlo přidáno: 2022/tcp."
 
# ── Systemd socket override (jen pokud ssh.socket existuje a je aktivní) ──────
USE_SOCKET=false
if systemctl list-unit-files ssh.socket &>/dev/null && \
   systemctl is-enabled ssh.socket 2>/dev/null | grep -qE "^(enabled|static)"; then
    USE_SOCKET=true
    SOCKET_OVERRIDE_DIR="/etc/systemd/system/ssh.socket.d"
    SOCKET_OVERRIDE_FILE="${SOCKET_OVERRIDE_DIR}/override.conf"
    mkdir -p "$SOCKET_OVERRIDE_DIR"
    cat > "$SOCKET_OVERRIDE_FILE" << 'EOF'
[Socket]
ListenStream=
ListenStream=0.0.0.0:2022
ListenStream=[::]:2022
EOF
    systemctl daemon-reload
    echo "[OK] Systemd socket override vytvořen ($SOCKET_OVERRIDE_FILE)."
else
    echo "[INFO] ssh.socket není aktivní – port řídí pouze sshd_config."
    systemctl daemon-reload
fi
 
# ── Shrnutí a potvrzení ────────────────────────────────────────────────────────
echo ""
echo "════════════════════════════════════════════════════════"
echo "  Vše je připraveno. Čeká se na aktivaci:"
echo ""
echo "  Hostname      : $NEW_HOSTNAME"
if [[ "$CREATE_USER" =~ ^[Yy]$ ]]; then
    echo "  Nový uživatel : $NEW_USER (passwordless sudo)"
fi
echo "  SSH port      : 2022 (root login zakázán)"
echo "  UFW           : pravidlo pro 2022/tcp přidáno"
echo ""
echo "  Po potvrzení se provede:"
echo "    1. Zapnutí UFW (povoleno pouze 2022/tcp)"
echo "    2. Restart SSH na portu 2022"
echo ""
echo "  !! Vaše stávající SSH session bude ukončena. !!"
if [[ "$CREATE_USER" =~ ^[Yy]$ ]]; then
    echo "  Přihlaste se znovu: ssh -p 2022 ${NEW_USER}@<server>"
else
    echo "  Přihlaste se znovu: ssh -p 2022 <uživatel>@<server>"
fi
echo "════════════════════════════════════════════════════════"
echo ""
read -rp "Zapnout UFW a restartovat SSH? [y/N] " CONFIRM
 
if [[ "$CONFIRM" =~ ^[Yy]$ ]]; then
    ufw --force enable
    echo "[OK] UFW zapnut."
    echo "[*] Restartuji SSH – tato session bude ukončena..."
    if [[ "$USE_SOCKET" == true ]]; then
        systemctl restart ssh.socket
    fi
    if systemctl list-unit-files sshd.service &>/dev/null 2>&1; then
        systemctl restart sshd
    else
        systemctl restart ssh
    fi
else
    echo ""
    echo "[INFO] Aktivace přeskočena. Až budete připraveni, spusťte ručně:"
    echo "    sudo ufw --force enable"
    if [[ "$USE_SOCKET" == true ]]; then
        echo "    sudo systemctl restart ssh.socket"
    fi
    echo "    sudo systemctl restart ssh   # nebo sshd na některých Debianu"
    echo ""
    if [[ "$CREATE_USER" =~ ^[Yy]$ ]]; then
        echo "  Poté se přihlaste: ssh -p 2022 ${NEW_USER}@<server>"
    else
        echo "  Poté se přihlaste: ssh -p 2022 <uživatel>@<server>"
    fi
fi
Poznámka: Skript přidá UFW pravidlo pro port 2022, ale firewall nezapne automaticky. Až potvrdíš aktivaci, teprve pak se UFW aktivuje a SSH restartuje — stávající session bude ukončena. Přihlaš se znovu přes: ssh -p 2022 <uživatel>@<server>.

Konfigurace oprávnění

Po přihlášení jako nový uživatel nainstaluj Claude Code a nakonfiguruj ~/.claude/settings.json. Základní filosofie: allowlist, ne denylist. Do automaticky schválených příkazů patří jen příkazy bez vedlejších efektů — ostatní Claude vždy nejdřív navrhne a čeká na potvrzení.

jq je nutnou závislostí pro audit hook níže — skript výše ho nainstaluje automaticky.

.claude/settings.json

{
  "permissions": {
    "allow": [
      "Read(*)",
 
      "Bash(pwd)",
      "Bash(echo *)",
      "Bash(id)",
      "Bash(whoami)",
      "Bash(uname *)",
      "Bash(uptime)",
 
      "Bash(ls)",
      "Bash(ls *)",
      "Bash(find *)",
      "Bash(grep *)",
      "Bash(cat *)",
      "Bash(head *)",
      "Bash(tail *)",
      "Bash(stat *)",
 
      "Bash(ps *)",
      "Bash(ss *)",
      "Bash(df *)",
      "Bash(du *)",
      "Bash(free *)",
 
      "Bash(systemctl status *)",
      "Bash(systemctl is-active *)",
      "Bash(systemctl is-enabled *)",
      "Bash(systemctl list-units *)",
      "Bash(journalctl *)",
 
      "Bash(git status)",
      "Bash(git diff *)",
      "Bash(git log *)",
      "Bash(git show *)"
    ]
  }
}

Záměrně není v allowlistu Bash(env) ani Bash(printenv *) — mohou prozradit citlivé proměnné prostředí (tokeny, hesla).

Co to v praxi znamená

Operace Výsledek
ls, grep, find, cat, head, tail auto-schváleno
git status, git diff, git log, git show auto-schváleno
systemctl status / is-active / is-enabled / list-units auto-schváleno
journalctl * auto-schváleno
df, du, free, uptime, ps, ss auto-schváleno
rm, mv, cp Claude se zeptá
sudo cokoli Claude se zeptá
python3 skript.py, bash skript.sh Claude se zeptá
apt install, apt remove, apt update Claude se zeptá
chmod, chown Claude se zeptá
ufw, iptables, nft Claude se zeptá
git commit, git push, git reset Claude se zeptá
env, printenv Claude se zeptá
Edit(soubor), Write(soubor) Claude se zeptá

Audit hook

Hooky v Claude Code dostávají vstup jako JSON na stdin, parsovaný přes jq. Logování probíhá dvakrát: PreToolUse zachytí záměr (i když ho pak neschválíš), PostToolUse zachytí dokončení. Když pro nějaký příkaz vidíš jen PRE bez odpovídajícího POST, příkaz buď nebyl schválen, nebo byl přerušen.

Před první session vytvoř adresář pro logy:

mkdir -p ~/log

Sekci hooks přidej do ~/.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '\"[\\(now | strftime(\\\"%Y-%m-%d %H:%M:%S\\\"))] PRE  \\(.tool_input.command)\"' >> $HOME/log/claude-audit.log"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '\"[\\(now | strftime(\\\"%Y-%m-%d %H:%M:%S\\\"))] POST \\(.tool_input.command)\"' >> $HOME/log/claude-audit.log"
          }
        ]
      }
    ]
  }
}

Příklad výstupu v logu:

[2026-05-06 14:22:01] PRE  systemctl status nginx
[2026-05-06 14:22:01] POST systemctl status nginx
[2026-05-06 14:22:18] PRE  rm /etc/nginx/sites-enabled/old.conf
[2026-05-06 14:22:24] POST rm /etc/nginx/sites-enabled/old.conf
[2026-05-06 14:23:05] PRE  apt remove apache2
                              ← chybí POST = neschváleno nebo přerušeno

Statusline

Konfiguraci statusline (zobrazení stavu Claude Code v terminálu) najdeš na stránce Claude Code statusline.

Zdroje