feat: vollst. Installations-Doku, Ansible-Rollen ausimplementiert, claude_workspace-Rolle
- docs/installation.md: Netinstall + Dualboot Schritt-für-Schritt (LUKS nur am Notebook) - docs/postinstall-ansible.md: Ablauf + restmanuelle Schritte - roles/base: APT-Sources mit non-free-firmware + Backports, Locale, Tastatur, Zeitzone, Grundpakete - roles/desktop_kde: Plasma 6 + SDDM (ohne Recommends, kompakt) - roles/hardening: SSH key-only, UFW, unattended-upgrades - roles/dev_tools: Node.js via NodeSource, Python, Perl, Git-Defaults - roles/workstation_apps: Firefox, Thunderbird, LibreOffice, Codecs, KeePassXC - roles/claude_workspace (NEU): Claude Code + ccusage, SSH-Key für Gitea, Workspace-Clone (recurse-submodules), ~/.claude/settings.json - site.yml: alle Rollen mit Tags
This commit is contained in:
29
README.md
29
README.md
@@ -26,8 +26,9 @@ Begründung siehe `docs/distro-auswahl.md`.
|
|||||||
linux-workstations/
|
linux-workstations/
|
||||||
├── README.md
|
├── README.md
|
||||||
├── docs/
|
├── docs/
|
||||||
│ └── distro-auswahl.md # ADR Distribution
|
│ ├── distro-auswahl.md # ADR Distribution
|
||||||
├── install/ # Netinstall-Preseed, Partitionierungsnotizen (folgt)
|
│ ├── installation.md # Schritt-für-Schritt Netinstall + Dualboot
|
||||||
|
│ └── postinstall-ansible.md # Bootstrap & Ablauf
|
||||||
└── ansible/
|
└── ansible/
|
||||||
├── ansible.cfg
|
├── ansible.cfg
|
||||||
├── inventory.yml
|
├── inventory.yml
|
||||||
@@ -35,20 +36,30 @@ linux-workstations/
|
|||||||
├── group_vars/all.yml
|
├── group_vars/all.yml
|
||||||
├── host_vars/{notebook,pc,werkstatt}.yml
|
├── host_vars/{notebook,pc,werkstatt}.yml
|
||||||
└── roles/
|
└── roles/
|
||||||
├── base/ # Grundsystem, APT-Quellen, Firmware
|
├── base/ # APT-Quellen, Locale, Firmware, Grundpakete
|
||||||
├── desktop_kde/ # KDE Plasma + Anwendungen
|
├── desktop_kde/ # KDE Plasma 6 + SDDM + Apps
|
||||||
├── hardening/ # SSH, UFW, unattended-upgrades
|
├── hardening/ # SSH, UFW, unattended-upgrades
|
||||||
├── dev_tools/ # Editor, Git, Sprachen
|
├── dev_tools/ # Git, Node.js (NodeSource), Python, Perl
|
||||||
└── workstation_apps/ # Browser, Office, Mediencodecs
|
├── workstation_apps/ # Browser, Office, Multimedia
|
||||||
|
└── claude_workspace/ # Claude Code, ccusage, Workspace-Clone, ~/.claude/settings.json
|
||||||
```
|
```
|
||||||
|
|
||||||
## Workflow
|
## Workflow (Kurz)
|
||||||
|
|
||||||
|
Siehe `docs/installation.md` und `docs/postinstall-ansible.md` für Details.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Auf einem Zielrechner nach Erstinstallation:
|
# Nach Debian-Erstinstallation auf Zielrechner:
|
||||||
ssh-copy-id tom@notebook
|
ssh-copy-id tom@notebook.egonlebt.lan
|
||||||
ansible -i ansible/inventory.yml notebook -m ping
|
ansible -i ansible/inventory.yml notebook -m ping
|
||||||
|
|
||||||
|
ansible-playbook -i ansible/inventory.yml ansible/site.yml --limit notebook --check --diff
|
||||||
ansible-playbook -i ansible/inventory.yml ansible/site.yml --limit notebook
|
ansible-playbook -i ansible/inventory.yml ansible/site.yml --limit notebook
|
||||||
|
|
||||||
|
# Anschließend manuell pro Maschine (~1 min):
|
||||||
|
# 1. Public-Key aus ~/.ssh/id_ed25519_gitea.pub auf Gitea hochladen
|
||||||
|
# 2. ansible-playbook ... --tags claude_workspace (holt Workspace nach)
|
||||||
|
# 3. Auf dem Zielrechner: `claude` → OAuth-Login
|
||||||
```
|
```
|
||||||
|
|
||||||
## Repo
|
## Repo
|
||||||
|
|||||||
@@ -1,3 +1,84 @@
|
|||||||
---
|
---
|
||||||
# Grundsystem: APT-Quellen, Lokalisierung, Firmware, Hilfspakete
|
- name: APT-Quellen mit contrib + non-free-firmware + (Backports)
|
||||||
# TODO: implementieren — Skeleton-Datei
|
ansible.builtin.copy:
|
||||||
|
dest: /etc/apt/sources.list
|
||||||
|
mode: '0644'
|
||||||
|
content: |
|
||||||
|
deb http://deb.debian.org/debian/ trixie main contrib non-free-firmware
|
||||||
|
deb http://security.debian.org/debian-security trixie-security main contrib non-free-firmware
|
||||||
|
deb http://deb.debian.org/debian/ trixie-updates main contrib non-free-firmware
|
||||||
|
{% if enable_backports | default(true) %}
|
||||||
|
deb http://deb.debian.org/debian/ trixie-backports main contrib non-free-firmware
|
||||||
|
{% endif %}
|
||||||
|
register: apt_sources
|
||||||
|
|
||||||
|
- name: APT cache aktualisieren
|
||||||
|
ansible.builtin.apt:
|
||||||
|
update_cache: true
|
||||||
|
when: apt_sources.changed
|
||||||
|
|
||||||
|
- name: Locale-Paket
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: locales
|
||||||
|
state: present
|
||||||
|
cache_valid_time: 3600
|
||||||
|
|
||||||
|
- name: Locale aktivieren
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /etc/locale.gen
|
||||||
|
regexp: "^# ?{{ locale }} "
|
||||||
|
line: "{{ locale }} UTF-8"
|
||||||
|
register: locale_line
|
||||||
|
|
||||||
|
- name: locale-gen ausführen
|
||||||
|
ansible.builtin.command: locale-gen
|
||||||
|
when: locale_line.changed
|
||||||
|
|
||||||
|
- name: Standard-Locale setzen
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /etc/default/locale
|
||||||
|
mode: '0644'
|
||||||
|
content: "LANG={{ locale }}\n"
|
||||||
|
|
||||||
|
- name: Tastaturlayout
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /etc/default/keyboard
|
||||||
|
mode: '0644'
|
||||||
|
content: |
|
||||||
|
XKBMODEL="pc105"
|
||||||
|
XKBLAYOUT="{{ keyboard_layout }}"
|
||||||
|
XKBVARIANT=""
|
||||||
|
XKBOPTIONS=""
|
||||||
|
BACKSPACE="guess"
|
||||||
|
|
||||||
|
- name: Zeitzone
|
||||||
|
ansible.builtin.command: "timedatectl set-timezone {{ timezone }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Grundpakete
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- sudo
|
||||||
|
- curl
|
||||||
|
- wget
|
||||||
|
- gnupg
|
||||||
|
- ca-certificates
|
||||||
|
- apt-transport-https
|
||||||
|
- vim
|
||||||
|
- htop
|
||||||
|
- tmux
|
||||||
|
- rsync
|
||||||
|
- net-tools
|
||||||
|
- dnsutils
|
||||||
|
- firmware-linux
|
||||||
|
- firmware-linux-nonfree
|
||||||
|
- lsb-release
|
||||||
|
- bash-completion
|
||||||
|
- man-db
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Extra-Pakete je Host
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: "{{ extra_packages }}"
|
||||||
|
state: present
|
||||||
|
when: extra_packages | default([]) | length > 0
|
||||||
|
|||||||
11
ansible/roles/claude_workspace/defaults/main.yml
Normal file
11
ansible/roles/claude_workspace/defaults/main.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
claude_workspace_repo: "ssh://git@docker.egonlebt.lan:2222/egon/claude-workspace.git"
|
||||||
|
claude_workspace_dest: "/home/{{ primary_user }}/Claude"
|
||||||
|
gitea_ssh_host: docker.egonlebt.lan
|
||||||
|
gitea_ssh_port: 2222
|
||||||
|
claude_settings:
|
||||||
|
model: opus
|
||||||
|
advisorModel: opus
|
||||||
|
statusLine:
|
||||||
|
type: command
|
||||||
|
command: "node ~/Claude/.claude/statusline.js"
|
||||||
106
ansible/roles/claude_workspace/tasks/main.yml
Normal file
106
ansible/roles/claude_workspace/tasks/main.yml
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
---
|
||||||
|
# Setzt voraus: Node.js + git (aus dev_tools), npm verfügbar
|
||||||
|
|
||||||
|
- name: Claude Code (npm global)
|
||||||
|
ansible.builtin.command: npm install -g @anthropic-ai/claude-code
|
||||||
|
args:
|
||||||
|
creates: /usr/lib/node_modules/@anthropic-ai/claude-code/package.json
|
||||||
|
|
||||||
|
- name: ccusage (npm global, für Statusline-Tokenverbrauch)
|
||||||
|
ansible.builtin.command: npm install -g ccusage
|
||||||
|
args:
|
||||||
|
creates: /usr/lib/node_modules/ccusage/package.json
|
||||||
|
|
||||||
|
- name: ~/.ssh existiert
|
||||||
|
become_user: "{{ primary_user }}"
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "/home/{{ primary_user }}/.ssh"
|
||||||
|
state: directory
|
||||||
|
mode: '0700'
|
||||||
|
|
||||||
|
- name: SSH-Key für Gitea (ed25519, ohne Passphrase)
|
||||||
|
become_user: "{{ primary_user }}"
|
||||||
|
ansible.builtin.command: >
|
||||||
|
ssh-keygen -t ed25519
|
||||||
|
-f /home/{{ primary_user }}/.ssh/id_ed25519_gitea
|
||||||
|
-N "" -C "{{ primary_user }}@{{ inventory_hostname }} -> gitea"
|
||||||
|
args:
|
||||||
|
creates: "/home/{{ primary_user }}/.ssh/id_ed25519_gitea"
|
||||||
|
|
||||||
|
- name: known_hosts für Gitea vorpopulieren
|
||||||
|
become_user: "{{ primary_user }}"
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
ssh-keyscan -p {{ gitea_ssh_port }} -H {{ gitea_ssh_host }} 2>/dev/null \
|
||||||
|
| grep -v '^#' >> /home/{{ primary_user }}/.ssh/known_hosts
|
||||||
|
sort -u /home/{{ primary_user }}/.ssh/known_hosts \
|
||||||
|
-o /home/{{ primary_user }}/.ssh/known_hosts
|
||||||
|
args:
|
||||||
|
creates: "/home/{{ primary_user }}/.ssh/known_hosts"
|
||||||
|
|
||||||
|
- name: SSH-Config für Gitea
|
||||||
|
become_user: "{{ primary_user }}"
|
||||||
|
ansible.builtin.blockinfile:
|
||||||
|
path: "/home/{{ primary_user }}/.ssh/config"
|
||||||
|
create: true
|
||||||
|
mode: '0600'
|
||||||
|
marker: "# {mark} ANSIBLE MANAGED — gitea"
|
||||||
|
block: |
|
||||||
|
Host {{ gitea_ssh_host }}
|
||||||
|
Port {{ gitea_ssh_port }}
|
||||||
|
IdentityFile ~/.ssh/id_ed25519_gitea
|
||||||
|
IdentitiesOnly yes
|
||||||
|
User git
|
||||||
|
|
||||||
|
- name: Check ob Workspace schon geklont
|
||||||
|
become_user: "{{ primary_user }}"
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ claude_workspace_dest }}/.git"
|
||||||
|
register: ws_git
|
||||||
|
|
||||||
|
- name: claude-workspace klonen (mit Submodules)
|
||||||
|
become_user: "{{ primary_user }}"
|
||||||
|
ansible.builtin.git:
|
||||||
|
repo: "{{ claude_workspace_repo }}"
|
||||||
|
dest: "{{ claude_workspace_dest }}"
|
||||||
|
recursive: true
|
||||||
|
update: false
|
||||||
|
accept_hostkey: true
|
||||||
|
key_file: "/home/{{ primary_user }}/.ssh/id_ed25519_gitea"
|
||||||
|
when: not ws_git.stat.exists
|
||||||
|
ignore_errors: true # scheitert bevor Pubkey in Gitea liegt — wird erneut versucht
|
||||||
|
register: clone_result
|
||||||
|
|
||||||
|
- name: ~/.claude existiert
|
||||||
|
become_user: "{{ primary_user }}"
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "/home/{{ primary_user }}/.claude"
|
||||||
|
state: directory
|
||||||
|
mode: '0700'
|
||||||
|
|
||||||
|
- name: Globale Claude-Settings (~/.claude/settings.json)
|
||||||
|
become_user: "{{ primary_user }}"
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: "/home/{{ primary_user }}/.claude/settings.json"
|
||||||
|
mode: '0644'
|
||||||
|
content: "{{ claude_settings | to_nice_json }}\n"
|
||||||
|
|
||||||
|
- name: Public-Key für Gitea-Upload anzeigen
|
||||||
|
become_user: "{{ primary_user }}"
|
||||||
|
ansible.builtin.command: "cat /home/{{ primary_user }}/.ssh/id_ed25519_gitea.pub"
|
||||||
|
register: pubkey
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: HINWEIS — Public-Key auf Gitea hochladen
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "==========================================================="
|
||||||
|
- "Public-Key dieser Maschine ({{ inventory_hostname }}):"
|
||||||
|
- ""
|
||||||
|
- "{{ pubkey.stdout }}"
|
||||||
|
- ""
|
||||||
|
- "→ http://{{ gitea_ssh_host }}:3000/user/settings/keys"
|
||||||
|
- " → 'Schlüssel hinzufügen', oben einfügen, speichern."
|
||||||
|
- ""
|
||||||
|
- "Danach ggf. Workspace nachholen:"
|
||||||
|
- " ansible-playbook ... --tags claude_workspace --limit {{ inventory_hostname }}"
|
||||||
|
- "==========================================================="
|
||||||
@@ -1,3 +1,39 @@
|
|||||||
---
|
---
|
||||||
# KDE Plasma 6, SDDM, KDE-Anwendungen
|
- name: KDE Plasma + SDDM (Standardumfang ohne riesige Discover-Hänger)
|
||||||
# TODO: implementieren — Skeleton-Datei
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- kde-plasma-desktop
|
||||||
|
- sddm
|
||||||
|
- plasma-nm
|
||||||
|
- plasma-pa
|
||||||
|
- kde-config-sddm
|
||||||
|
- konsole
|
||||||
|
- dolphin
|
||||||
|
- kate
|
||||||
|
- okular
|
||||||
|
- gwenview
|
||||||
|
- spectacle
|
||||||
|
- ark
|
||||||
|
- kcalc
|
||||||
|
- kfind
|
||||||
|
- partitionmanager
|
||||||
|
- xdg-utils
|
||||||
|
- fonts-noto
|
||||||
|
- fonts-noto-color-emoji
|
||||||
|
state: present
|
||||||
|
install_recommends: false
|
||||||
|
|
||||||
|
- name: SDDM aktivieren als Default-Display-Manager
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /etc/X11/default-display-manager
|
||||||
|
mode: '0644'
|
||||||
|
content: "/usr/bin/sddm\n"
|
||||||
|
|
||||||
|
- name: Graphical Target als Default
|
||||||
|
ansible.builtin.command: systemctl set-default graphical.target
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: SDDM aktivieren
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: sddm
|
||||||
|
enabled: true
|
||||||
|
|||||||
@@ -1,3 +1,50 @@
|
|||||||
---
|
---
|
||||||
# Git, Editor, Sprachen (Python/Node/Perl für FHEM)
|
- name: Dev-Pakete (Sprachen, Build-Tools, Editor)
|
||||||
# TODO: implementieren — Skeleton-Datei
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- git
|
||||||
|
- build-essential
|
||||||
|
- python3
|
||||||
|
- python3-venv
|
||||||
|
- python3-pip
|
||||||
|
- pipx
|
||||||
|
- perl
|
||||||
|
- jq
|
||||||
|
- direnv
|
||||||
|
- shellcheck
|
||||||
|
- meld
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: NodeSource Keyring (für Node.js LTS)
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key
|
||||||
|
dest: /etc/apt/keyrings/nodesource.asc
|
||||||
|
mode: '0644'
|
||||||
|
register: ns_key
|
||||||
|
|
||||||
|
- name: NodeSource Repo
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /etc/apt/sources.list.d/nodesource.list
|
||||||
|
mode: '0644'
|
||||||
|
content: "deb [signed-by=/etc/apt/keyrings/nodesource.asc] https://deb.nodesource.com/node_20.x nodistro main\n"
|
||||||
|
register: ns_repo
|
||||||
|
|
||||||
|
- name: APT update nach NodeSource
|
||||||
|
ansible.builtin.apt:
|
||||||
|
update_cache: true
|
||||||
|
when: ns_key.changed or ns_repo.changed
|
||||||
|
|
||||||
|
- name: Node.js (LTS) installieren
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: nodejs
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Git globale Defaults für {{ primary_user }}
|
||||||
|
become_user: "{{ primary_user }}"
|
||||||
|
ansible.builtin.command: "git config --global {{ item.k }} {{ item.v }}"
|
||||||
|
loop:
|
||||||
|
- { k: 'user.name', v: 'egon' }
|
||||||
|
- { k: 'user.email', v: 'egon@egonlebt.de' }
|
||||||
|
- { k: 'pull.rebase', v: 'true' }
|
||||||
|
- { k: 'init.defaultBranch', v: 'main' }
|
||||||
|
changed_when: false
|
||||||
|
|||||||
5
ansible/roles/hardening/handlers/main.yml
Normal file
5
ansible/roles/hardening/handlers/main.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
- name: restart sshd
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: ssh
|
||||||
|
state: restarted
|
||||||
@@ -1,3 +1,48 @@
|
|||||||
---
|
---
|
||||||
# SSH-Hardening, UFW, unattended-upgrades, fail2ban
|
- name: Security-Pakete
|
||||||
# TODO: implementieren — Skeleton-Datei
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- ufw
|
||||||
|
- unattended-upgrades
|
||||||
|
- apt-listchanges
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: SSH — Passwort-Login deaktivieren
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
regexp: '^#?\s*PasswordAuthentication\s'
|
||||||
|
line: 'PasswordAuthentication no'
|
||||||
|
validate: 'sshd -t -f %s'
|
||||||
|
notify: restart sshd
|
||||||
|
|
||||||
|
- name: SSH — Root-Login deaktivieren
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: /etc/ssh/sshd_config
|
||||||
|
regexp: '^#?\s*PermitRootLogin\s'
|
||||||
|
line: 'PermitRootLogin no'
|
||||||
|
validate: 'sshd -t -f %s'
|
||||||
|
notify: restart sshd
|
||||||
|
|
||||||
|
- name: UFW Default Policy (incoming deny)
|
||||||
|
ansible.builtin.command: ufw default deny incoming
|
||||||
|
register: ufw_default
|
||||||
|
changed_when: "'Default incoming policy changed' in ufw_default.stdout"
|
||||||
|
|
||||||
|
- name: UFW — SSH erlauben
|
||||||
|
ansible.builtin.command: ufw allow OpenSSH
|
||||||
|
register: ufw_allow
|
||||||
|
changed_when: "'Rule added' in ufw_allow.stdout or 'Rules updated' in ufw_allow.stdout"
|
||||||
|
|
||||||
|
- name: UFW aktivieren
|
||||||
|
ansible.builtin.command: ufw --force enable
|
||||||
|
register: ufw_enable
|
||||||
|
changed_when: "'Firewall is active' in ufw_enable.stdout"
|
||||||
|
|
||||||
|
- name: unattended-upgrades konfigurieren
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: /etc/apt/apt.conf.d/20auto-upgrades
|
||||||
|
mode: '0644'
|
||||||
|
content: |
|
||||||
|
APT::Periodic::Update-Package-Lists "1";
|
||||||
|
APT::Periodic::Unattended-Upgrade "1";
|
||||||
|
APT::Periodic::AutocleanInterval "7";
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
---
|
---
|
||||||
# Browser, Office, Multimedia-Codecs
|
- name: Anwendungen (Browser, Office, Multimedia, Tools)
|
||||||
# TODO: implementieren — Skeleton-Datei
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- firefox-esr
|
||||||
|
- libreoffice
|
||||||
|
- libreoffice-l10n-de
|
||||||
|
- thunderbird
|
||||||
|
- thunderbird-l10n-de
|
||||||
|
- keepassxc
|
||||||
|
- vlc
|
||||||
|
- ffmpeg
|
||||||
|
- libavcodec-extra
|
||||||
|
- gimp
|
||||||
|
- inkscape
|
||||||
|
state: present
|
||||||
|
|||||||
@@ -3,8 +3,15 @@
|
|||||||
hosts: workstations
|
hosts: workstations
|
||||||
gather_facts: true
|
gather_facts: true
|
||||||
roles:
|
roles:
|
||||||
- base
|
- role: base
|
||||||
- desktop_kde
|
tags: [base]
|
||||||
- hardening
|
- role: hardening
|
||||||
- dev_tools
|
tags: [hardening]
|
||||||
- workstation_apps
|
- role: dev_tools
|
||||||
|
tags: [dev_tools]
|
||||||
|
- role: desktop_kde
|
||||||
|
tags: [desktop, kde]
|
||||||
|
- role: workstation_apps
|
||||||
|
tags: [apps]
|
||||||
|
- role: claude_workspace
|
||||||
|
tags: [claude_workspace, claude]
|
||||||
|
|||||||
105
docs/installation.md
Normal file
105
docs/installation.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# Installation Debian 13 (Trixie) — Dualboot
|
||||||
|
|
||||||
|
Diese Anleitung gilt für alle drei Workstations (notebook, pc, werkstatt). Unterschiede sind unten markiert.
|
||||||
|
|
||||||
|
## 1. Vorbereitung in Windows
|
||||||
|
|
||||||
|
1. **Updates fertigstellen**, dann `Datenträgerverwaltung` (`diskmgmt.msc`) öffnen
|
||||||
|
2. **BitLocker** deaktivieren auf der Partition, die verkleinert wird, sonst startet Windows nach dem GRUB-Eintrag nicht mehr sauber
|
||||||
|
3. Windows-Partition verkleinern → freien Bereich erzeugen:
|
||||||
|
- **Notebook:** mindestens 80 GB frei
|
||||||
|
- **PC:** mindestens 150 GB frei
|
||||||
|
- **Werkstatt:** mindestens 60 GB frei
|
||||||
|
4. **Schnellstart** in Windows deaktivieren (`Energieoptionen → Auswählen, was beim Drücken …`), sonst bleibt das NTFS in einem halben Zustand und Grub kann es nicht sicher zeigen
|
||||||
|
5. **BIOS/UEFI**:
|
||||||
|
- Boot-Mode: UEFI (nicht Legacy)
|
||||||
|
- **Secure Boot:** anlassen, Debian 13 unterstützt es out-of-the-box
|
||||||
|
- Fast Boot: aus (sonst kein USB-Boot)
|
||||||
|
|
||||||
|
## 2. Boot-Stick erstellen
|
||||||
|
|
||||||
|
ISO: `firmware-13.x.x-amd64-netinst.iso` von <https://cdimage.debian.org/cdimage/firmware/>
|
||||||
|
> Die **firmware**-Variante ist Pflicht — sonst fehlen WLAN-/Grafik-Treiber.
|
||||||
|
|
||||||
|
Stick (mind. 2 GB) schreiben:
|
||||||
|
- Windows: [Rufus](https://rufus.ie) → "DD-Image" Modus
|
||||||
|
- Linux: `sudo dd if=firmware-13.x.x-amd64-netinst.iso of=/dev/sdX bs=4M status=progress oflag=sync`
|
||||||
|
|
||||||
|
## 3. Debian-Installation
|
||||||
|
|
||||||
|
Vom Stick booten (BIOS-Boot-Menu, meist F12/F10/Esc).
|
||||||
|
|
||||||
|
### Wichtige Antworten im Installer
|
||||||
|
|
||||||
|
| Schritt | Antwort |
|
||||||
|
|---|---|
|
||||||
|
| Sprache | Deutsch |
|
||||||
|
| Tastatur | Deutsch |
|
||||||
|
| Hostname | `notebook` / `pc` / `werkstatt` |
|
||||||
|
| Domain | `egonlebt.lan` |
|
||||||
|
| Root-Passwort | **leer lassen** → Tom bekommt automatisch `sudo` |
|
||||||
|
| Benutzer | Tom Egon (`tom`) |
|
||||||
|
| Zeitzone | Europe/Berlin |
|
||||||
|
|
||||||
|
### Partitionierung
|
||||||
|
|
||||||
|
**Notebook (mit LUKS):**
|
||||||
|
- Methode: **Geführt — gesamten Laufwerk verwenden, mit verschlüsseltem LVM**
|
||||||
|
- Wenn der Installer die Methode wegen Windows nicht anbietet → **Manuell**:
|
||||||
|
- ESP existiert bereits (Windows) — wiederverwenden, mountpoint `/boot/efi`
|
||||||
|
- 1 GB unverschlüsselte `/boot` (ext4)
|
||||||
|
- Rest: LUKS-Container → LVM:
|
||||||
|
- `lv-root` 30 GB ext4 → `/`
|
||||||
|
- `lv-home` Rest ext4 → `/home`
|
||||||
|
- `lv-swap` 8 GB Swap (= RAM-Größe für Hibernate)
|
||||||
|
|
||||||
|
**PC + Werkstatt (ohne LUKS):**
|
||||||
|
- Methode: **Manuell**
|
||||||
|
- ESP wiederverwenden, mountpoint `/boot/efi`
|
||||||
|
- 30 GB ext4 → `/`
|
||||||
|
- Rest ext4 → `/home`
|
||||||
|
- Swap-File statt -Partition (per `systemd` nachgereicht, einfacher)
|
||||||
|
|
||||||
|
### Paketauswahl (Software-Auswahl)
|
||||||
|
|
||||||
|
**Nur ankreuzen:**
|
||||||
|
- ✅ SSH-Server
|
||||||
|
- ✅ Standard-Systemwerkzeuge
|
||||||
|
|
||||||
|
**Abwählen:**
|
||||||
|
- ❌ Debian-Desktop, GNOME, KDE — kommt alles über Ansible
|
||||||
|
|
||||||
|
So bleibt die Basis schlank (~1.2 GB).
|
||||||
|
|
||||||
|
### GRUB
|
||||||
|
|
||||||
|
- "GRUB auf primärem Laufwerk installieren" → **Ja**
|
||||||
|
- Nach Reboot: GRUB zeigt Debian + "Windows Boot Manager"
|
||||||
|
|
||||||
|
## 4. Erstboot (vor Ansible)
|
||||||
|
|
||||||
|
Auf der Zielmaschine einloggen (Konsole), dann:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# IP merken
|
||||||
|
ip -4 addr show | grep inet
|
||||||
|
# SSH läuft schon (Paketauswahl). Test vom Admin-Rechner:
|
||||||
|
# ssh tom@<host>.egonlebt.lan
|
||||||
|
```
|
||||||
|
|
||||||
|
Auf dem **Admin-Rechner** (Windows mit OpenSSH, oder schon installierter Linux):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Public Key kopieren — danach kein Passwort mehr nötig
|
||||||
|
ssh-copy-id tom@notebook.egonlebt.lan
|
||||||
|
```
|
||||||
|
|
||||||
|
Damit ist die Basis bereit für den Ansible-Run → `docs/postinstall-ansible.md`.
|
||||||
|
|
||||||
|
## 5. Sanity Checks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh tom@notebook.egonlebt.lan 'cat /etc/debian_version' # → 13.x
|
||||||
|
ssh tom@notebook.egonlebt.lan 'sudo -n true && echo sudo-ok'
|
||||||
|
ssh tom@notebook.egonlebt.lan 'efibootmgr -v | grep -E "debian|Windows"'
|
||||||
|
```
|
||||||
88
docs/postinstall-ansible.md
Normal file
88
docs/postinstall-ansible.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# Postinstall via Ansible
|
||||||
|
|
||||||
|
Voraussetzung: Debian-Grundinstallation läuft, SSH-Login per Key funktioniert (siehe `installation.md`).
|
||||||
|
|
||||||
|
## 1. Ansible auf dem Admin-Rechner
|
||||||
|
|
||||||
|
Ansible kann von **jedem Rechner** ausgeführt werden, der die Zielmaschinen per SSH erreicht und Python 3 hat. Empfohlen: die erste fertige Linux-Workstation wird zum Admin-Rechner für die nächsten.
|
||||||
|
|
||||||
|
Initial vom Windows-Rechner (WSL/Git-Bash) reicht auch:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux/WSL
|
||||||
|
sudo apt install -y ansible
|
||||||
|
# oder via pipx
|
||||||
|
pipx install ansible-core
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Ablauf
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/Claude/linux-workstations
|
||||||
|
|
||||||
|
# Verbindungstest
|
||||||
|
ansible -i ansible/inventory.yml notebook -m ping
|
||||||
|
|
||||||
|
# Trockenlauf
|
||||||
|
ansible-playbook -i ansible/inventory.yml ansible/site.yml --limit notebook --check --diff
|
||||||
|
|
||||||
|
# Echter Run
|
||||||
|
ansible-playbook -i ansible/inventory.yml ansible/site.yml --limit notebook
|
||||||
|
```
|
||||||
|
|
||||||
|
Die Playbook-Ausführung dauert beim ersten Mal ~20 min (KDE-Plasma-Pakete sind groß).
|
||||||
|
|
||||||
|
## 3. Was nach `site.yml` fertig ist
|
||||||
|
|
||||||
|
- Debian-Basis mit allen Updates, deutsche Locale, Zeitzone Berlin
|
||||||
|
- KDE Plasma 6 + SDDM (Login-Manager) — Reboot fällig
|
||||||
|
- SSH gehärtet (Key-only), UFW aktiv (nur SSH offen), `unattended-upgrades` an
|
||||||
|
- Git, Node.js, Python, Editor, Browser, LibreOffice, Codecs
|
||||||
|
- **Claude Code + ccusage installiert, `~/.claude/settings.json` mit Statusline gesetzt**
|
||||||
|
- **`~/Claude` geklont (`--recurse-submodules`)**, Git-User auf `egon` konfiguriert
|
||||||
|
- SSH-Key für Gitea generiert (`~/.ssh/id_ed25519_gitea`), `known_hosts` für `docker.egonlebt.lan:2222` vorpopuliert
|
||||||
|
|
||||||
|
## 4. Restmanuelle Schritte
|
||||||
|
|
||||||
|
Diese zwei Dinge muss man pro Maschine selbst tun — beides je 1 Minute.
|
||||||
|
|
||||||
|
### 4.1 SSH-Public-Key auf Gitea hochladen
|
||||||
|
|
||||||
|
Am Ende des Ansible-Runs zeigt die Rolle `claude_workspace` den Public-Key an. Wenn der Run schon vorbei ist:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh tom@notebook 'cat ~/.ssh/id_ed25519_gitea.pub'
|
||||||
|
```
|
||||||
|
|
||||||
|
→ in <http://docker.egonlebt.lan:3000/user/settings/keys> als neuen SSH-Key einfügen, Name z.B. `notebook-tom`.
|
||||||
|
|
||||||
|
Danach auf dem Zielrechner einmal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/id_ed25519_gitea -T git@docker.egonlebt.lan -p 2222
|
||||||
|
# Antwort: "Hi there, egon! You've successfully authenticated…"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 Claude Code anmelden
|
||||||
|
|
||||||
|
Direkt am Zielrechner (oder per `ssh -t`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
claude
|
||||||
|
# → folgt dem Browser-Login-Flow einmalig
|
||||||
|
```
|
||||||
|
|
||||||
|
Danach ist die Maschine voll einsatzbereit. Statusline, Hooks (Auto-Pull beim Start, Auto-Commit/Push beim Stop), MCP-Server (paperless, imap, ssh-infra) — alles aktiv, weil Konfiguration im Repo liegt.
|
||||||
|
|
||||||
|
## 5. Wartung
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Alles aktualisieren (nur APT-Pakete + Konfig-Drift heilen):
|
||||||
|
ansible-playbook -i ansible/inventory.yml ansible/site.yml
|
||||||
|
|
||||||
|
# Eine einzelne Rolle:
|
||||||
|
ansible-playbook -i ansible/inventory.yml ansible/site.yml --tags claude_workspace
|
||||||
|
|
||||||
|
# Eine einzelne Maschine:
|
||||||
|
ansible-playbook -i ansible/inventory.yml ansible/site.yml --limit werkstatt
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user