Adding recovery for debian desktop

This commit is contained in:
juvdiaz 2026-06-11 11:02:23 -06:00
parent f121910cba
commit a69966c457
6 changed files with 555 additions and 0 deletions

2
.gitignore vendored
View File

@ -3,9 +3,11 @@
*.tfstate.backup *.tfstate.backup
.terraform/ .terraform/
.lab/ .lab/
bootstrap/host/inventory/
# Ignore local archive dumps and backups # Ignore local archive dumps and backups
*.tar *.tar
*.tar.gz
*.zip *.zip
infra/gitea/data/ infra/gitea/data/

View File

@ -92,6 +92,21 @@ On the Debian host:
- SSH access to the OCI edge host - SSH access to the OCI edge host
- enough persistent storage for `/var/openebs/local` and `/var/lib/docker` - enough persistent storage for `/var/openebs/local` and `/var/lib/docker`
After a clean Debian reinstall, bootstrap the host package set and desktop
configuration first:
```bash
sudo apt-get update
sudo apt-get install -y git ansible sudo
git clone <repo-url> /home/jv/my-homelab-configs
cd /home/jv/my-homelab-configs
ansible-playbook -K -i bootstrap/host/inventory.ini bootstrap/host/playbook.yml
```
If you kept the private app-config archive generated from the old Debian host,
pass its generated `ansible-vars.yml` with `-e @/path/to/ansible-vars.yml`.
Details are in `bootstrap/host/README.md`.
The default kubeconfig path is `/home/jv/.kube/config`. Override it with The default kubeconfig path is `/home/jv/.kube/config`. Override it with
`KUBECONFIG_PATH` or `TF_VAR_kubeconfig_path` when needed. `KUBECONFIG_PATH` or `TF_VAR_kubeconfig_path` when needed.

40
bootstrap/host/README.md Normal file
View File

@ -0,0 +1,40 @@
# Debian Host Bootstrap
This layer restores the Debian control-plane and personal desktop package set
before the OpenTofu/Kubernetes bootstrap runs.
Fresh Debian recovery path:
```bash
sudo apt-get update
sudo apt-get install -y git ansible sudo
sudo usermod -aG sudo jv
git clone <repo-url> /home/jv/my-homelab-configs
cd /home/jv/my-homelab-configs
ansible-playbook -K -i bootstrap/host/inventory.ini bootstrap/host/playbook.yml
./lab.sh up
```
To also restore captured application behavior/configuration, keep the generated
`home-configs.tar.gz` outside Git and pass the generated vars file:
```bash
ansible-playbook -K -i bootstrap/host/inventory.ini bootstrap/host/playbook.yml \
-e @/path/to/debian-pc-ansible-inventory/ansible-vars.yml
```
The playbook:
- configures apt repositories for Docker, Kubernetes, Helm, OpenTofu, and Brave
- installs the captured Debian package set, filtering packages unavailable on
the current Debian release
- installs Zoom from the upstream `.deb` when requested on amd64
- adds `jv` to expected local groups
- enables core services such as Docker, containerd, kubelet, ssh, cron, and
Bluetooth when installed
- creates homelab persistence directories used by `lab.sh up`
- optionally extracts the captured user config archive into `/home/jv`
Secrets and active sessions are intentionally not stored in this repo. Keep
SSH keys, kubeconfigs, SOPS keys, Docker auth, and browser session/password data
in a separate encrypted backup if you want them restored.

View File

@ -0,0 +1,253 @@
---
debian_pc_user: jv
debian_pc_user_home: "/home/{{ debian_pc_user }}"
debian_pc_kubernetes_minor_version: v1.36
debian_pc_enable_passwordless_sudo: true
debian_pc_persistent_directories:
- /var/openebs/local/registry
- /var/lib/docker
- "/home/{{ debian_pc_user }}/git-server"
- "/home/{{ debian_pc_user }}/backups/gitea"
- "/home/{{ debian_pc_user }}/backups/gitea-restore-drills"
debian_pc_user_groups:
- sudo
- docker
- audio
- bluetooth
- video
- plugdev
debian_pc_enable_services:
- ssh
- cron
- docker
- containerd
- kubelet
- bluetooth
debian_pc_hold_packages:
- containerd.io
- kubeadm
- kubectl
- kubelet
debian_pc_base_packages:
- apt-transport-https
- bash-completion
- ca-certificates
- curl
- git
- gnupg
- gpg
- python3
- python3-apt
- rsync
- sudo
- tar
- unzip
- wget
- zip
# Captured from the Debian PC manual package inventory. The playbook filters
# packages that are unavailable for the current Debian release after adding the
# third-party repositories, so this list can remain faithful to the source host.
debian_pc_apt_manual_packages:
- adduser
- amd64-microcode
- ansible
- apt
- apt-listchanges
- apt-transport-https
- apt-utils
- aria2
- base-files
- base-passwd
- bash
- bash-completion
- bind9-dnsutils
- bind9-host
- blueman
- bluez
- bluez-tools
- brave-browser
- bsdutils
- busybox
- bzip2
- ca-certificates
- chafa
- chromium
- conky-all
- console-setup
- containerd.io
- containernetworking-plugins
- coreutils
- cpio
- cron
- cron-daemon-common
- curl
- dash
- dbus
- debconf
- debconf-i18n
- debian-archive-keyring
- debian-faq
- debianutils
- dhcpcd-base
- diffutils
- direnv
- dmidecode
- dnsmasq
- doc-debian
- docker-buildx-plugin
- docker-ce
- docker-ce-cli
- docker-ce-rootless-extras
- docker-compose-plugin
- docker-model-plugin
- dosfstools
- dpkg
- e2fsprogs
- fdisk
- file
- findutils
- firmware-amd-graphics
- firmware-realtek
- firmware-sof-signed
- gcc-14-base
- geany
- geany-plugins
- gettext-base
- git
- gnumeric
- gnupg
- gparted
- gpg
- grep
- groff-base
- grub-common
- grub-efi-amd64
- gzip
- haveged
- helm
- hostname
- i3
- ifupdown
- imagemagick
- inetutils-telnet
- init
- initramfs-tools
- init-system-helpers
- installation-report
- iproute2
- iputils-ping
- keyboard-configuration
- kitty
- kmod
- krb5-locales
- kubeadm
- kubectl
- kubelet
- laptop-detect
- less
- linux-image-amd64
- linux-sysctl-defaults
- locales
- login
- login.defs
- logrotate
- logsave
- lsof
- lvm2
- man-db
- manpages
- mawk
- mcomix
- media-types
- mount
- mplayer
- nano
- ncurses-base
- ncurses-bin
- ncurses-term
- netbase
- netcat-traditional
- nfs-common
- nftables
- nginx
- nodejs
- openbox
- open-iscsi
- openssh-client
- openssl-provider-legacy
- os-prober
- parted
- passwd
- pavucontrol
- pciutils
- perl
- perl-base
- pipx
- procps
- psmisc
- pulseaudio-module-bluetooth
- qcomicbook
- readline-common
- reportbug
- rocminfo
- rsync
- screen
- sed
- sensible-utils
- shellcheck
- shim-signed
- smplayer
- sqv
- sudo
- synapse
- systemd
- systemd-sysv
- systemd-timesyncd
- sysvinit-utils
- tar
- task-english
- task-laptop
- tasksel
- task-ssh-server
- tcpdump
- thefuck
- tilda
- tmux
- tofu
- traceroute
- transmission
- tree
- tzdata
- ucf
- udev
- unzip
- usbutils
- util-linux
- util-linux-extra
- vim
- vim-common
- vim-tiny
- virtualenvwrapper
- vlc
- wamerican
- wget
- whiptail
- wmctrl
- wtmpdb
- xlsx2csv
- xorg
- xz-utils
- zip
- zlib1g
- zoom
- zstd
debian_pc_config_archive: ""
debian_pc_restore_configs: true

View File

@ -0,0 +1,3 @@
[debian_pc]
localhost ansible_connection=local

242
bootstrap/host/playbook.yml Normal file
View File

@ -0,0 +1,242 @@
---
- name: Restore Debian homelab control-plane and desktop host
hosts: debian_pc
become: true
gather_facts: true
pre_tasks:
- name: Require Debian
ansible.builtin.assert:
that:
- ansible_facts.os_family == "Debian"
- ansible_facts.distribution == "Debian"
fail_msg: "This host bootstrap is intentionally scoped to Debian."
- name: Install apt prerequisites
ansible.builtin.apt:
name: "{{ debian_pc_base_packages }}"
state: present
update_cache: true
- name: Ensure apt keyring directory exists
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
mode: "0755"
tasks:
- name: Add Docker apt key
ansible.builtin.get_url:
url: https://download.docker.com/linux/debian/gpg
dest: /etc/apt/keyrings/docker.asc
mode: "0644"
- name: Add Docker apt source
ansible.builtin.copy:
dest: /etc/apt/sources.list.d/docker.sources
mode: "0644"
content: |
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: {{ ansible_facts.distribution_release }}
Components: stable
Architectures: {{ ansible_facts.architecture | regex_replace('x86_64', 'amd64') | regex_replace('aarch64', 'arm64') }}
Signed-By: /etc/apt/keyrings/docker.asc
- name: Add Kubernetes apt key
ansible.builtin.shell: |
set -euo pipefail
curl -fsSL "https://pkgs.k8s.io/core:/stable:/{{ debian_pc_kubernetes_minor_version }}/deb/Release.key" |
gpg --dearmor >/etc/apt/keyrings/kubernetes-apt-keyring.gpg
chmod 0644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg
args:
executable: /bin/bash
creates: /etc/apt/keyrings/kubernetes-apt-keyring.gpg
- name: Add Kubernetes apt source
ansible.builtin.copy:
dest: /etc/apt/sources.list.d/kubernetes.list
mode: "0644"
content: |
deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/{{ debian_pc_kubernetes_minor_version }}/deb/ /
- name: Add Helm apt key
ansible.builtin.shell: |
set -euo pipefail
curl -fsSL https://baltocdn.com/helm/signing.asc |
gpg --dearmor >/etc/apt/keyrings/helm.gpg
chmod 0644 /etc/apt/keyrings/helm.gpg
args:
executable: /bin/bash
creates: /etc/apt/keyrings/helm.gpg
- name: Add Helm apt source
ansible.builtin.copy:
dest: /etc/apt/sources.list.d/helm-stable-debian.list
mode: "0644"
content: |
deb [arch={{ ansible_facts.architecture | regex_replace('x86_64', 'amd64') | regex_replace('aarch64', 'arm64') }} signed-by=/etc/apt/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main
- name: Add OpenTofu apt keys
ansible.builtin.shell: |
set -euo pipefail
curl -fsSL https://get.opentofu.org/opentofu.gpg >/etc/apt/keyrings/opentofu.gpg
curl -fsSL https://packages.opentofu.org/opentofu/tofu/gpgkey |
gpg --dearmor >/etc/apt/keyrings/opentofu-repo.gpg
chmod 0644 /etc/apt/keyrings/opentofu.gpg /etc/apt/keyrings/opentofu-repo.gpg
args:
executable: /bin/bash
creates: /etc/apt/keyrings/opentofu-repo.gpg
- name: Add OpenTofu apt source
ansible.builtin.copy:
dest: /etc/apt/sources.list.d/opentofu.list
mode: "0644"
content: |
deb [signed-by=/etc/apt/keyrings/opentofu.gpg,/etc/apt/keyrings/opentofu-repo.gpg] https://packages.opentofu.org/opentofu/tofu/any/ any main
- name: Add Brave apt key
ansible.builtin.get_url:
url: https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg
dest: /etc/apt/keyrings/brave-browser-archive-keyring.gpg
mode: "0644"
- name: Add Brave apt source
ansible.builtin.copy:
dest: /etc/apt/sources.list.d/brave-browser-release.sources
mode: "0644"
content: |
Types: deb
URIs: https://brave-browser-apt-release.s3.brave.com/
Suites: stable
Components: main
Architectures: amd64 arm64
Signed-By: /etc/apt/keyrings/brave-browser-archive-keyring.gpg
- name: Refresh apt cache after repository setup
ansible.builtin.apt:
update_cache: true
- name: Write desired package list for availability filtering
ansible.builtin.copy:
dest: /tmp/homelab-debian-pc-desired-packages.txt
mode: "0644"
content: |
{% for package in (debian_pc_base_packages + debian_pc_apt_manual_packages) | unique | sort %}
{{ package }}
{% endfor %}
- name: Filter packages available to apt
ansible.builtin.shell: |
set -euo pipefail
while IFS= read -r package; do
[ -n "$package" ] || continue
if apt-cache policy "$package" | awk '/Candidate:/ && $2 != "(none)" { found = 1 } END { exit found ? 0 : 1 }'; then
printf '%s\n' "$package"
fi
done </tmp/homelab-debian-pc-desired-packages.txt
args:
executable: /bin/bash
register: debian_pc_installable_packages_result
changed_when: false
- name: Track installable package list
ansible.builtin.set_fact:
debian_pc_installable_packages: "{{ debian_pc_installable_packages_result.stdout_lines | default([]) }}"
- name: Install available Debian PC packages
ansible.builtin.apt:
name: "{{ debian_pc_installable_packages }}"
state: present
when: debian_pc_installable_packages | length > 0
- name: Install Zoom from upstream deb when requested on amd64
ansible.builtin.apt:
deb: https://zoom.us/client/latest/zoom_amd64.deb
state: present
when:
- "'zoom' in debian_pc_apt_manual_packages"
- ansible_facts.architecture == "x86_64"
- name: Hold Kubernetes and runtime packages
ansible.builtin.dpkg_selections:
name: "{{ item }}"
selection: hold
loop: "{{ debian_pc_hold_packages }}"
failed_when: false
- name: Ensure expected local groups exist
ansible.builtin.group:
name: "{{ item }}"
state: present
loop: "{{ debian_pc_user_groups }}"
- name: Ensure user supplementary groups
ansible.builtin.user:
name: "{{ debian_pc_user }}"
groups: "{{ debian_pc_user_groups }}"
append: true
- name: Allow passwordless sudo for homelab automation
ansible.builtin.copy:
dest: "/etc/sudoers.d/90-homelab-{{ debian_pc_user }}"
owner: root
group: root
mode: "0440"
validate: "visudo -cf %s"
content: |
{{ debian_pc_user }} ALL=(ALL) NOPASSWD:ALL
when: debian_pc_enable_passwordless_sudo | bool
- name: Ensure persistent homelab directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ debian_pc_user if item.startswith(debian_pc_user_home) else 'root' }}"
group: "{{ debian_pc_user if item.startswith(debian_pc_user_home) else 'root' }}"
mode: "{{ '0755' if item.startswith(debian_pc_user_home) else '0775' }}"
loop: "{{ debian_pc_persistent_directories }}"
- name: Enable expected services when installed
ansible.builtin.systemd:
name: "{{ item }}"
enabled: true
state: started
loop: "{{ debian_pc_enable_services }}"
failed_when: false
- name: Check whether config archive is present
ansible.builtin.stat:
path: "{{ debian_pc_config_archive }}"
register: debian_pc_config_archive_stat
when:
- debian_pc_restore_configs | bool
- debian_pc_config_archive | length > 0
- name: Restore captured home application configs
ansible.builtin.unarchive:
src: "{{ debian_pc_config_archive }}"
dest: "{{ debian_pc_user_home }}"
remote_src: true
owner: "{{ debian_pc_user }}"
group: "{{ debian_pc_user }}"
when:
- debian_pc_restore_configs | bool
- debian_pc_config_archive | length > 0
- debian_pc_config_archive_stat.stat.exists | default(false)
- name: Report packages skipped because apt has no candidate
ansible.builtin.shell: |
set -euo pipefail
comm -23 \
<(sort -u /tmp/homelab-debian-pc-desired-packages.txt) \
<(printf '%s\n' {{ debian_pc_installable_packages | map('quote') | join(' ') }} | sort -u)
args:
executable: /bin/bash
register: debian_pc_skipped_packages_result
changed_when: false
- name: Show skipped packages
ansible.builtin.debug:
msg: "{{ debian_pc_skipped_packages_result.stdout_lines }}"
when: debian_pc_skipped_packages_result.stdout_lines | length > 0