mirror of
https://github.com/fscotto/infra.git
synced 2026-09-27 19:03:47 +00:00
Compare commits
8 Commits
1517c92866
...
6da430b9e4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6da430b9e4 | ||
|
|
e92ff1c729 | ||
|
|
8d3f67885c | ||
|
|
bc9b381525 | ||
|
|
2004b48cf3 | ||
|
|
54e1e88a4e | ||
|
|
cb88e95385 | ||
|
|
764d329a8f |
19
AGENTS.md
19
AGENTS.md
@@ -1,6 +1,6 @@
|
||||
# AGENTS.md
|
||||
|
||||
Ansible-driven personal infrastructure repo for Fedora and Void desktops, WSL, and an Ubuntu server.
|
||||
Ansible-driven personal infrastructure repo for Fedora and Void desktops, Fedora CoreOS, WSL, and servers.
|
||||
|
||||
## Source Of Truth
|
||||
- Main orchestration: `ansible/site.yml`
|
||||
@@ -17,6 +17,7 @@ Ansible-driven personal infrastructure repo for Fedora and Void desktops, WSL, a
|
||||
- Workstation: `deadalus` is Windows + Fedora WSL.
|
||||
- Ubuntu server: `prometheus`
|
||||
- NAS: `atlas` (Rocky Linux 9, reached through SSH)
|
||||
- Always-on LAN node: `aegis` (Fedora CoreOS on Raspberry Pi 4, reached through SSH)
|
||||
- Hosts intentionally belong to multiple groups; trust `ansible/site.yml` over hostname assumptions.
|
||||
- Inventory axes are independent: `platform_*`, `role_*`, and `desktop_*`. Legacy `void` and `desktop` remain compatibility parents.
|
||||
|
||||
@@ -24,7 +25,10 @@ Ansible-driven personal infrastructure repo for Fedora and Void desktops, WSL, a
|
||||
- Preserve layering `all -> platform -> role -> desktop -> host`.
|
||||
- Keep `ansible/site.yml` small; orchestration belongs there, implementation belongs in roles.
|
||||
- Prefer minimal, targeted edits. Preserve idempotency and existing ordering.
|
||||
- All hosts use `ansible_connection: local`.
|
||||
- Use Git Flow branch prefixes: `feature/` for new functionality, `bugfix/` for non-urgent fixes,
|
||||
`hotfix/` for urgent production fixes, `release/` for release preparation, and `support/` for
|
||||
maintained release lines. Do not use abbreviated prefixes such as `feat/`.
|
||||
- Desktop and WSL hosts use `ansible_connection: local`; remote infrastructure hosts use SSH.
|
||||
- Treat `secrets/` as sensitive. Never print secret values.
|
||||
- Tmux plugins are bootstrapped by TPM on the host; the repo only keeps tmux config and custom helper scripts.
|
||||
- Read the relevant role tasks, templates, vars, and deployed dotfiles before editing.
|
||||
@@ -42,6 +46,7 @@ Ansible-driven personal infrastructure repo for Fedora and Void desktops, WSL, a
|
||||
- WSL workstation dev: `ansible-playbook ansible/site.yml --limit deadalus --check --diff`
|
||||
- Server: `ansible-playbook ansible/site.yml --limit prometheus --check --diff`
|
||||
- Atlas NAS: `ansible-playbook ansible/site.yml --limit atlas --check --diff`
|
||||
- Aegis CoreOS: `ansible-playbook ansible/site.yml --limit aegis --check --diff`
|
||||
- Focused checks:
|
||||
- Emacs is disabled by default; temporary Emacs check: `ansible-playbook ansible/site.yml --limit <host> --tags emacs --check --diff -e emacs_enabled=true`
|
||||
- AI coding agents: `ansible-playbook ansible/site.yml --limit <host> --tags ai_agents --check --diff`
|
||||
@@ -148,3 +153,13 @@ The dotfile vars follow the same split: `desktop_common_dotfiles` carries mode-i
|
||||
- Keep `README.md` and `AGENTS.md` aligned when workflows materially change.
|
||||
- If you add a new operational area, also add the narrowest validation command for it.
|
||||
- Call out checks you could not run and any follow-up verification needed.
|
||||
|
||||
## Aegis Fedora CoreOS Notes
|
||||
- `aegis` is a remote Fedora CoreOS Raspberry Pi 4 node. Bootstrap it once with
|
||||
`ansible/bootstrap/aegis.bu`; the remaining configuration is applied by `profile_aegis` over SSH.
|
||||
- Fedora CoreOS is immutable. Do not add it to mutable Fedora package or shared dotfile roles.
|
||||
- `profile_aegis` owns rootful Podman Quadlets, persistent container state under `/var/lib`, and the
|
||||
Podman auto-update timer. Keep Apple IDs and other credentials in Vault and use `no_log` for their
|
||||
rendering.
|
||||
- iCloudPD requires post-deployment interactive MFA initialization; its cookie/configuration state is
|
||||
persisted in `/var/lib/icloudpd/config`.
|
||||
|
||||
30
README.md
30
README.md
@@ -36,8 +36,9 @@ infra/
|
||||
|
||||
## Managed machines
|
||||
|
||||
The repo currently covers Fedora/GNOME desktops, one Fedora WSL workstation, an Ubuntu server, and
|
||||
a Rocky Linux 9 NAS. Configuration is layered instead of being tied to host names:
|
||||
The repo currently covers Fedora/GNOME desktops, one Fedora WSL workstation, a Fedora CoreOS LAN
|
||||
node, an Ubuntu server, and a Rocky Linux 9 NAS. Configuration is layered instead of being tied to
|
||||
host names:
|
||||
|
||||
```text
|
||||
common user environment
|
||||
@@ -52,6 +53,7 @@ common user environment
|
||||
| `ikaros` | Fedora | Personal workstation | GNOME |
|
||||
| `nymph` | Fedora | Desktop laptop | GNOME |
|
||||
| `deadalus` | Fedora WSL | Development workstation | — |
|
||||
| `aegis` | Fedora CoreOS | Always-on LAN node | — |
|
||||
| `prometheus` | Ubuntu | Server | — |
|
||||
| `atlas` | Rocky 9 | NAS | — |
|
||||
|
||||
@@ -112,6 +114,25 @@ ansible-playbook ansible/site.yml --limit prometheus \
|
||||
-e server_user_home=/srv/myuser
|
||||
```
|
||||
|
||||
## Aegis
|
||||
|
||||
`aegis` is a Raspberry Pi 4 running Fedora CoreOS. Provision it once with
|
||||
`ansible/bootstrap/aegis.bu`, after replacing the SSH public-key placeholder:
|
||||
|
||||
```bash
|
||||
butane --strict --pretty --output aegis.ign ansible/bootstrap/aegis.bu
|
||||
```
|
||||
|
||||
The `pi` user receives the configured SSH key, and partition 5 on `/dev/mmcblk0` is formatted as
|
||||
Btrfs and mounted as the root filesystem (`/`) on first boot. Formatting is destructive for that
|
||||
partition.
|
||||
|
||||
The controller then manages it remotely as `pi@aegis`; unlike local desktop profiles, Aegis is
|
||||
intentionally an SSH inventory target. `profile_aegis` manages rootful Podman Quadlets for AdGuard
|
||||
Home and iCloudPD, persistent data under `/var/lib`, the Podman auto-update timer, and
|
||||
`wake-ikaros`. Define `vault_aegis_icloudpd_apple_id` in Vault before applying it. iCloudPD still
|
||||
requires interactive MFA initialization after its first deployment.
|
||||
|
||||
## NAS
|
||||
|
||||
`atlas` is a Rocky Linux 9 NAS reached through SSH. Its pool already exists: the profile only
|
||||
@@ -222,6 +243,7 @@ ansible-playbook ansible/site.yml --limit deadalus --tags ai_agents --check --di
|
||||
| `profile_workstation_dev_wsl` | WSL development setup. |
|
||||
| `profile_server` | Server setup. |
|
||||
| `profile_atlas` | Rocky Linux 9 NAS setup. |
|
||||
| `profile_aegis` | Fedora CoreOS always-on LAN node. |
|
||||
| `dotfiles_common` | Shared user dotfiles. |
|
||||
|
||||
## What `site.yml` runs
|
||||
@@ -232,6 +254,7 @@ platform_void -> packages_void + services_runit
|
||||
platform_void & graphical_desktop -> profile_desktop_common + profile_desktop_sway + profile_desktop_niri + profile_desktop_host
|
||||
platform_fedora -> packages_fedora + services_systemd
|
||||
platform_rocky -> packages_rocky + services_systemd
|
||||
role_aegis -> profile_aegis
|
||||
atlas -> profile_atlas
|
||||
platform_fedora & role_personal_workstation -> profile_personal_workstation
|
||||
platform_fedora & desktop_gnome -> profile_desktop_gnome
|
||||
@@ -246,6 +269,8 @@ So, in practice:
|
||||
- `deadalus` gets the Fedora development layer followed by the WSL layer.
|
||||
- `ubuntu_server` configures `prometheus`.
|
||||
- `atlas` receives the Rocky platform layer and the NAS profile through SSH.
|
||||
- `aegis` receives only the immutable Fedora CoreOS profile through SSH; it does not receive
|
||||
mutable Fedora package or common dotfile roles.
|
||||
- Empty `platform_void` groups do nothing until they get a host.
|
||||
- The playbook never restarts the display manager during a run.
|
||||
- `secrets/vault.yml` and then `secrets/vault.local.yml` are loaded only when present.
|
||||
@@ -285,6 +310,7 @@ ansible-playbook ansible/site.yml --limit nymph --check --diff
|
||||
ansible-playbook ansible/site.yml --limit deadalus --check --diff
|
||||
ansible-playbook ansible/site.yml --limit prometheus --check --diff
|
||||
ansible-playbook ansible/site.yml --limit atlas --check --diff
|
||||
ansible-playbook ansible/site.yml --limit aegis --check --diff
|
||||
ansible-lint ansible/site.yml
|
||||
ansible-lint ansible/roles
|
||||
yamllint ansible/
|
||||
|
||||
32
ansible/bootstrap/aegis.bu
Normal file
32
ansible/bootstrap/aegis.bu
Normal file
@@ -0,0 +1,32 @@
|
||||
# Bootstrap monouso per Fedora CoreOS su Aegis.
|
||||
# Sostituire le chiavi SSH prima di generare Ignition con butane --strict.
|
||||
variant: fcos
|
||||
version: 1.6.0
|
||||
passwd:
|
||||
users:
|
||||
- name: pi
|
||||
ssh_authorized_keys:
|
||||
- "ssh-ed25519 CHANGEME_AEGIS_SSH_PUBLIC_KEY"
|
||||
storage:
|
||||
disks:
|
||||
- device: /dev/mmcblk0
|
||||
wipe_table: false
|
||||
partitions:
|
||||
- label: aegis-data
|
||||
number: 5
|
||||
size_mib: 0
|
||||
filesystems:
|
||||
- device: /dev/disk/by-partlabel/aegis-data
|
||||
format: btrfs
|
||||
label: aegis-data
|
||||
wipe_filesystem: false
|
||||
mount:
|
||||
path: /
|
||||
options:
|
||||
- compress=zstd
|
||||
files:
|
||||
- path: /etc/hostname
|
||||
mode: 0644
|
||||
contents:
|
||||
inline: |
|
||||
aegis
|
||||
2
ansible/inventory/group_vars/platform_fedora_coreos.yml
Normal file
2
ansible/inventory/group_vars/platform_fedora_coreos.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
---
|
||||
# Fedora CoreOS is immutable: do not attach the mutable Fedora package roles.
|
||||
8
ansible/inventory/host_vars/aegis.yml
Normal file
8
ansible/inventory/host_vars/aegis.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
ansible_host: aegis
|
||||
ansible_connection: ssh
|
||||
ansible_user: pi
|
||||
ansible_become: true
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
|
||||
aegis_icloudpd_apple_id: "{{ vault_aegis_icloudpd_apple_id | default('') }}"
|
||||
@@ -13,6 +13,10 @@ all:
|
||||
deadalus:
|
||||
ansible_connection: local
|
||||
|
||||
platform_fedora_coreos:
|
||||
hosts:
|
||||
aegis:
|
||||
|
||||
platform_rocky:
|
||||
hosts:
|
||||
atlas:
|
||||
@@ -24,6 +28,7 @@ all:
|
||||
fedora:
|
||||
children:
|
||||
platform_fedora:
|
||||
platform_fedora_coreos:
|
||||
|
||||
rocky:
|
||||
children:
|
||||
@@ -45,6 +50,10 @@ all:
|
||||
ikaros:
|
||||
ansible_connection: local
|
||||
|
||||
role_aegis:
|
||||
hosts:
|
||||
aegis:
|
||||
|
||||
desktop_sway:
|
||||
hosts: {}
|
||||
|
||||
|
||||
9
ansible/roles/profile_aegis/defaults/main.yml
Normal file
9
ansible/roles/profile_aegis/defaults/main.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
aegis_hostname: aegis
|
||||
aegis_adguard_image: docker.io/adguard/adguardhome:latest
|
||||
aegis_icloudpd_image: docker.io/boredazfcuk/icloudpd:latest
|
||||
aegis_icloudpd_folder_structure: '{:%Y/%m/%d}'
|
||||
aegis_icloudpd_synchronisation_interval: 86400
|
||||
aegis_icloudpd_apple_id: ""
|
||||
aegis_ikaros_mac_address: aa:bb:cc:dd:ee:ff
|
||||
aegis_wol_port: 9
|
||||
11
ansible/roles/profile_aegis/handlers/main.yml
Normal file
11
ansible/roles/profile_aegis/handlers/main.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Restart Aegis Quadlet services
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
loop:
|
||||
- adguardhome.service
|
||||
- icloudpd.service
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
77
ansible/roles/profile_aegis/tasks/main.yml
Normal file
77
ansible/roles/profile_aegis/tasks/main.yml
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
- name: Require Aegis iCloudPD Apple ID
|
||||
tags: [aegis, icloudpd]
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- aegis_icloudpd_apple_id | length > 0
|
||||
fail_msg: Define vault_aegis_icloudpd_apple_id before applying the Aegis profile.
|
||||
no_log: true
|
||||
|
||||
- name: Set Aegis hostname
|
||||
tags: [aegis, services]
|
||||
ansible.builtin.hostname:
|
||||
name: "{{ aegis_hostname }}"
|
||||
|
||||
- name: Create Aegis persistent container directories
|
||||
tags: [aegis, containers]
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
loop:
|
||||
- /var/lib/adguard/work
|
||||
- /var/lib/adguard/conf
|
||||
- /var/lib/icloudpd/data
|
||||
- /var/lib/icloudpd/config
|
||||
|
||||
- name: Create Quadlet configuration directory
|
||||
tags: [aegis, containers]
|
||||
ansible.builtin.file:
|
||||
path: /etc/containers/systemd
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Render Aegis Quadlet units
|
||||
tags: [aegis, containers]
|
||||
ansible.builtin.template:
|
||||
src: "{{ item.src }}"
|
||||
dest: "/etc/containers/systemd/{{ item.dest }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop:
|
||||
- src: adguardhome.container.j2
|
||||
dest: adguardhome.container
|
||||
- src: icloudpd.container.j2
|
||||
dest: icloudpd.container
|
||||
loop_control:
|
||||
label: "{{ item.dest }}"
|
||||
no_log: "{{ item.dest == 'icloudpd.container' }}"
|
||||
notify: Restart Aegis Quadlet services
|
||||
|
||||
- name: Install Wake-on-LAN helper for Ikaros
|
||||
tags: [aegis, wol]
|
||||
ansible.builtin.template:
|
||||
src: wake-ikaros.j2
|
||||
dest: /usr/local/bin/wake-ikaros
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Enable Aegis Quadlet services and automatic updates
|
||||
tags: [aegis, containers, services]
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
loop:
|
||||
- adguardhome.service
|
||||
- icloudpd.service
|
||||
- podman-auto-update.timer
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
@@ -0,0 +1,18 @@
|
||||
# Managed by Ansible. Do not edit manually.
|
||||
[Unit]
|
||||
Description=AdGuard Home DNS sinkhole
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Container]
|
||||
Image={{ aegis_adguard_image }}
|
||||
Network=host
|
||||
Volume=/var/lib/adguard/work:/opt/adguardhome/work:Z
|
||||
Volume=/var/lib/adguard/conf:/opt/adguardhome/conf:Z
|
||||
AutoUpdate=registry
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
20
ansible/roles/profile_aegis/templates/icloudpd.container.j2
Normal file
20
ansible/roles/profile_aegis/templates/icloudpd.container.j2
Normal file
@@ -0,0 +1,20 @@
|
||||
# Managed by Ansible. Do not edit manually.
|
||||
[Unit]
|
||||
Description=iCloud Photos Downloader
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Container]
|
||||
Image={{ aegis_icloudpd_image }}
|
||||
Environment=apple_id={{ aegis_icloudpd_apple_id }}
|
||||
Environment=folder_structure={{ aegis_icloudpd_folder_structure }}
|
||||
Environment=synchronisation_interval={{ aegis_icloudpd_synchronisation_interval }}
|
||||
Volume=/var/lib/icloudpd/data:/home/root/iCloud:Z
|
||||
Volume=/var/lib/icloudpd/config:/config:Z
|
||||
AutoUpdate=registry
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
21
ansible/roles/profile_aegis/templates/wake-ikaros.j2
Normal file
21
ansible/roles/profile_aegis/templates/wake-ikaros.j2
Normal file
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
# Managed by Ansible. Do not edit manually.
|
||||
set -euo pipefail
|
||||
|
||||
mac='{{ aegis_ikaros_mac_address }}'
|
||||
port='{{ aegis_wol_port }}'
|
||||
mac_hex="${mac//:/}"
|
||||
|
||||
if [[ ! $mac_hex =~ ^[[:xdigit:]]{12}$ ]]; then
|
||||
printf 'Indirizzo MAC non valido: %s\n' "$mac" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
packet='\xFF\xFF\xFF\xFF\xFF\xFF'
|
||||
for _ in {1..16}; do
|
||||
packet+="$(printf '\\x%s\\x%s\\x%s\\x%s\\x%s\\x%s' \
|
||||
"${mac_hex:0:2}" "${mac_hex:2:2}" "${mac_hex:4:2}" \
|
||||
"${mac_hex:6:2}" "${mac_hex:8:2}" "${mac_hex:10:2}")"
|
||||
done
|
||||
|
||||
printf '%b' "$packet" > "/dev/udp/255.255.255.255/${port}"
|
||||
@@ -33,7 +33,9 @@
|
||||
|
||||
roles:
|
||||
- role: dotfiles_common
|
||||
when: "'platform_rocky' not in group_names"
|
||||
when:
|
||||
- "'platform_rocky' not in group_names"
|
||||
- "'platform_fedora_coreos' not in group_names"
|
||||
|
||||
- name: Configure Void platform
|
||||
hosts: platform_void
|
||||
@@ -69,6 +71,13 @@
|
||||
- packages_rocky
|
||||
- services_systemd
|
||||
|
||||
- name: Configure Aegis Fedora CoreOS profile
|
||||
hosts: role_aegis
|
||||
become: true
|
||||
|
||||
roles:
|
||||
- profile_aegis
|
||||
|
||||
- name: Configure Atlas NAS profile
|
||||
hosts: atlas
|
||||
become: true
|
||||
|
||||
Reference in New Issue
Block a user