@crvouga/dev-fleet 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mise/locks/aws-cdk/2.1146.1/aube-lock.yaml +24 -0
- package/.mise/locks/aws-cdk/2.1146.1/package.json +7 -0
- package/.mise/locks/azure-cli/2.91.0~a837e014/pyproject.toml +5 -0
- package/.mise/locks/azure-cli/2.91.0~a837e014/uv.lock +2794 -0
- package/.mise/locks/gemini/0.66.0~84aac8e6/aube-lock.yaml +123 -0
- package/.mise/locks/gemini/0.66.0~84aac8e6/package.json +12 -0
- package/.mise/locks/npm-devcontainers-cli/0.90.0/aube-lock.yaml +24 -0
- package/.mise/locks/npm-devcontainers-cli/0.90.0/package.json +7 -0
- package/.mise/locks/npm-modelcontextprotocol-inspector/2.13.0~4c109bea/aube-lock.yaml +1432 -0
- package/.mise/locks/npm-modelcontextprotocol-inspector/2.13.0~4c109bea/package.json +7 -0
- package/.mise/locks/npm-vercel/65.8.0/aube-lock.yaml +3595 -0
- package/.mise/locks/npm-vercel/65.8.0/package.json +7 -0
- package/.mise/locks/npm-wrangler/4.156.0/aube-lock.yaml +895 -0
- package/.mise/locks/npm-wrangler/4.156.0/package.json +7 -0
- package/.mise/locks/npm-yarnpkg-cli-dist/4.18.1/aube-lock.yaml +24 -0
- package/.mise/locks/npm-yarnpkg-cli-dist/4.18.1/package.json +7 -0
- package/LICENSE +21 -0
- package/README.md +157 -2
- package/agents/antigravity-proxy.ts +3 -0
- package/agents/audit.md +89 -0
- package/agents/claude-proxy +5 -0
- package/agents/claude-proxy.ts +3 -0
- package/agents/codex-proxy.ts +3 -0
- package/agents/grok-proxy.ts +3 -0
- package/agents/managed-proxy.ts +55 -0
- package/agents/opencode-proxy.ts +3 -0
- package/agents/registry.json +209 -0
- package/agents/registry.schema.json +149 -0
- package/agents/setup.md +128 -0
- package/agents/t3.json +40 -0
- package/ansible/macos/manager.yml +30 -0
- package/ansible/macos/policy.json +17 -0
- package/ansible/monitoring/README.md +154 -0
- package/ansible/monitoring/agent-darwin.yml +198 -0
- package/ansible/monitoring/agent.yml +134 -0
- package/ansible/monitoring/beszel-agent.service +42 -0
- package/ansible/monitoring/compose.yml +74 -0
- package/ansible/monitoring/hub-darwin.yml +102 -0
- package/ansible/monitoring/lock.json +11 -0
- package/ansible/net/remote-probe.sh +13 -0
- package/ansible/ubuntu/authorize-sudo.sh +51 -0
- package/ansible/ubuntu/baseline.yml +89 -0
- package/ansible/ubuntu/disk-guard.py +104 -0
- package/ansible/ubuntu/policy.json +43 -0
- package/ansible/ubuntu/recovery.yml +15 -0
- package/ansible/ubuntu/t3/policy.json +7 -0
- package/ansible/ubuntu/t3-desktop.yml +220 -0
- package/ansible/ubuntu/t3-health.sh +33 -0
- package/ansible/ubuntu/t3-policy.yml +76 -0
- package/ansible/ubuntu/t3-route.yml +28 -0
- package/ansible/ubuntu/tailscale.yml +75 -0
- package/ansible/ubuntu/tasks/credentials.yml +47 -0
- package/ansible/ubuntu/tasks/docker.yml +31 -0
- package/ansible/ubuntu/tasks/packages.yml +56 -0
- package/ansible/ubuntu/tasks/power.yml +51 -0
- package/ansible/ubuntu/tasks/recovery.yml +39 -0
- package/ansible/ubuntu/tasks/resources.yml +252 -0
- package/ansible/ubuntu/tasks/services.yml +31 -0
- package/ansible/ubuntu/tasks/ssh.yml +71 -0
- package/ansible/ubuntu/tasks/user.yml +120 -0
- package/bin/devfleet.mjs +36 -0
- package/bootstrap/bootstrap.ps1 +50 -0
- package/bootstrap/bootstrap.sh +73 -0
- package/bootstrap/devfleet +7 -0
- package/bootstrap/docker-desktop.sh +33 -0
- package/bootstrap/doctor.ts +189 -0
- package/bootstrap/fleet-manager-run.sh +76 -0
- package/bootstrap/fleet-remote-bootstrap.sh +185 -0
- package/bootstrap/global-config.ts +113 -0
- package/bootstrap/install-launcher.sh +7 -0
- package/bootstrap/native-aws.ts +86 -0
- package/bootstrap/native-t3.ts +154 -0
- package/bootstrap/npm.ts +350 -0
- package/bootstrap/setup-status.ts +51 -0
- package/bootstrap/setup.ts +88 -0
- package/bootstrap/shell-path.ts +105 -0
- package/bootstrap/verify-t3-archive.py +49 -0
- package/bootstrap/worker-auth.ts +65 -0
- package/bootstrap/worker-github.ts +47 -0
- package/bootstrap/worker-tools.ts +24 -0
- package/cliproxy/AGENTS.md +11 -0
- package/cliproxy/README.md +269 -0
- package/cliproxy/clients/bootstrap.py +143 -0
- package/cliproxy/clients/launcher.py +57 -0
- package/cliproxy/compose.yaml +90 -0
- package/cliproxy/config.template.yaml +70 -0
- package/cliproxy/docs/cloud-adoption.md +16 -0
- package/cliproxy/docs/deployment.png +0 -0
- package/cliproxy/docs/deployment.svg +109 -0
- package/cliproxy/docs/health-workflow.example.yml +185 -0
- package/cliproxy/docs/migration.md +131 -0
- package/cliproxy/docs/model-health-report.schema.json +364 -0
- package/cliproxy/docs/model-health.md +393 -0
- package/cliproxy/image.lock.json +5 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-backup-check.plist +38 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-backup.plist +48 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-fleet.plist +32 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-gc.plist +38 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-monitor.plist +29 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-vault-renew.plist +31 -0
- package/cliproxy/models.json +1618 -0
- package/cliproxy/requirements.lock +78 -0
- package/cliproxy/scripts/accounts.py +38 -0
- package/cliproxy/scripts/audit.py +123 -0
- package/cliproxy/scripts/backup +234 -0
- package/cliproxy/scripts/check.py +34 -0
- package/cliproxy/scripts/checkpoint.py +226 -0
- package/cliproxy/scripts/cli.py +64 -0
- package/cliproxy/scripts/cliproxy_host.py +257 -0
- package/cliproxy/scripts/cloud_state.py +150 -0
- package/cliproxy/scripts/container-health +6 -0
- package/cliproxy/scripts/docker +24 -0
- package/cliproxy/scripts/external-health.py +79 -0
- package/cliproxy/scripts/fleet_config.py +43 -0
- package/cliproxy/scripts/fleet_sync.py +274 -0
- package/cliproxy/scripts/initialize +84 -0
- package/cliproxy/scripts/install-dependencies +48 -0
- package/cliproxy/scripts/install.py +406 -0
- package/cliproxy/scripts/model_health.py +16 -0
- package/cliproxy/scripts/modelhealth/__init__.py +5 -0
- package/cliproxy/scripts/modelhealth/checks.py +429 -0
- package/cliproxy/scripts/modelhealth/cli.py +117 -0
- package/cliproxy/scripts/modelhealth/config.py +186 -0
- package/cliproxy/scripts/modelhealth/emulator.py +536 -0
- package/cliproxy/scripts/modelhealth/expect.py +88 -0
- package/cliproxy/scripts/modelhealth/findings.py +183 -0
- package/cliproxy/scripts/modelhealth/protocol.py +330 -0
- package/cliproxy/scripts/modelhealth/redact.py +28 -0
- package/cliproxy/scripts/modelhealth/report.py +150 -0
- package/cliproxy/scripts/modelhealth/runner.py +390 -0
- package/cliproxy/scripts/modelhealth/scenario.py +131 -0
- package/cliproxy/scripts/modelhealth/schema.py +63 -0
- package/cliproxy/scripts/modelhealth/simulate.py +111 -0
- package/cliproxy/scripts/modelhealth/transport.py +213 -0
- package/cliproxy/scripts/monitor +68 -0
- package/cliproxy/scripts/ops +527 -0
- package/cliproxy/scripts/public-endpoint +127 -0
- package/cliproxy/scripts/recover_node.py +177 -0
- package/cliproxy/scripts/restore-verify +60 -0
- package/cliproxy/scripts/retention.py +144 -0
- package/cliproxy/scripts/service-start +7 -0
- package/cliproxy/scripts/validate-config +56 -0
- package/cliproxy/scripts/vault-access.py +57 -0
- package/cliproxy/scripts/vault-backup-login +55 -0
- package/cliproxy/scripts/vault-recovery +56 -0
- package/cliproxy/scripts/vault-renew +30 -0
- package/cliproxy/systemd/cliproxy-backup-check.service +10 -0
- package/cliproxy/systemd/cliproxy-backup-check.timer +8 -0
- package/cliproxy/systemd/cliproxy-backup.service +13 -0
- package/cliproxy/systemd/cliproxy-backup.timer +8 -0
- package/cliproxy/systemd/cliproxy-fleet.service +17 -0
- package/cliproxy/systemd/cliproxy-gc.service +13 -0
- package/cliproxy/systemd/cliproxy-gc.timer +8 -0
- package/cliproxy/systemd/cliproxy-monitor.service +8 -0
- package/cliproxy/systemd/cliproxy-monitor.timer +7 -0
- package/cliproxy/systemd/cliproxy-vault-renew.service +8 -0
- package/cliproxy/systemd/cliproxy-vault-renew.timer +7 -0
- package/cliproxy/vendor/LICENSE +22 -0
- package/cliproxy/vendor/config.upstream.yaml +1349 -0
- package/examples/fleet.json +95 -0
- package/examples/roster.json +13 -0
- package/mise.lock +1492 -0
- package/mise.toml +99 -0
- package/package.json +65 -4
- package/schema/fleet.schema.json +364 -0
- package/schema/roster.schema.json +77 -0
- package/src/agents/account-relay.ts +111 -0
- package/src/agents/accounts.ts +85 -0
- package/src/agents/audit.ts +77 -0
- package/src/agents/cli.ts +13 -0
- package/src/agents/command.ts +204 -0
- package/src/agents/converge.ts +123 -0
- package/src/agents/doctor.ts +107 -0
- package/src/agents/errors.ts +2 -0
- package/src/agents/files.ts +73 -0
- package/src/agents/fleet-verify.ts +156 -0
- package/src/agents/launch.ts +290 -0
- package/src/agents/native-verify.ts +226 -0
- package/src/agents/provider-errors.ts +50 -0
- package/src/agents/registry.ts +55 -0
- package/src/agents/render.ts +110 -0
- package/src/agents/round-trip.ts +192 -0
- package/src/agents/secrets.ts +13 -0
- package/src/agents/toolchain.ts +34 -0
- package/src/agents/verify-cache.ts +103 -0
- package/src/agents/verify.ts +127 -0
- package/src/auth-checks.ts +163 -0
- package/src/cli/agent-guide.ts +95 -0
- package/src/cli/app.ts +115 -0
- package/src/cli/cli.ts +21 -0
- package/src/cli/command-index.ts +50 -0
- package/src/cli/contracts.ts +25 -0
- package/src/cli/fleet-reference.ts +17 -0
- package/src/cli/fleet-sync-checkout.ts +91 -0
- package/src/cli/fleet-sync-enrollment.ts +144 -0
- package/src/cli/fleet-sync-forward.ts +134 -0
- package/src/cli/fleet-sync-manager.ts +193 -0
- package/src/cli/fleet-sync-monitoring.ts +185 -0
- package/src/cli/fleet-sync-output.ts +314 -0
- package/src/cli/fleet-sync-privilege.ts +27 -0
- package/src/cli/fleet-sync-progress.ts +109 -0
- package/src/cli/fleet-sync-remote.ts +236 -0
- package/src/cli/fleet-sync-report.ts +26 -0
- package/src/cli/fleet-sync-schedule.ts +138 -0
- package/src/cli/fleet-sync-vault.ts +166 -0
- package/src/cli/fleet-sync.ts +309 -0
- package/src/cli/fleet.ts +163 -0
- package/src/cli/managed-health.ts +219 -0
- package/src/cli/node-deployment.ts +34 -0
- package/src/cli/node-sync.ts +114 -0
- package/src/cli/node-t3.ts +31 -0
- package/src/cli/options.ts +141 -0
- package/src/cli/probe-source.ts +7 -0
- package/src/cli/program.ts +169 -0
- package/src/cli/register-fleet.ts +246 -0
- package/src/cli/registry.ts +158 -0
- package/src/cli/repair-apply.ts +67 -0
- package/src/cli/repair-guard.ts +11 -0
- package/src/cli/repair-inspect.ts +150 -0
- package/src/cli/repair-script.ts +76 -0
- package/src/cli/repair.ts +209 -0
- package/src/cli/roster.ts +89 -0
- package/src/cli/search.ts +35 -0
- package/src/cli/settings.ts +64 -0
- package/src/cli/source-operations.ts +78 -0
- package/src/cli/t3/command.ts +90 -0
- package/src/cli/t3/register.ts +55 -0
- package/src/cli/tailnet.ts +127 -0
- package/src/cli/ui-actions.ts +133 -0
- package/src/cli/ui-controller.ts +113 -0
- package/src/cli/ui-data.ts +106 -0
- package/src/cli/ui-execute.ts +163 -0
- package/src/cli/ui-repairs.ts +34 -0
- package/src/cli/ui-reports.ts +211 -0
- package/src/cli/ui-targets.ts +100 -0
- package/src/cli/ui-terminal.ts +109 -0
- package/src/cli/ui-types.ts +49 -0
- package/src/cli/ui-wizard.ts +101 -0
- package/src/cli/ui.ts +9 -0
- package/src/cli/worker-health-output.ts +37 -0
- package/src/config.ts +186 -0
- package/src/configure.ts +255 -0
- package/src/definition.ts +260 -0
- package/src/diagnostics.ts +92 -0
- package/src/doctor-check.ts +10 -0
- package/src/errors.ts +1 -0
- package/src/fingerprint.ts +120 -0
- package/src/fleet-config.ts +88 -0
- package/src/index.ts +6 -0
- package/src/input-hashes.ts +73 -0
- package/src/machine/local-source.ts +52 -0
- package/src/machine/lock-error.ts +2 -0
- package/src/machine/platform.ts +44 -0
- package/src/machine/source.ts +68 -0
- package/src/machine/state.ts +185 -0
- package/src/machine-health.ts +95 -0
- package/src/managed-source.ts +47 -0
- package/src/manager-failure.ts +48 -0
- package/src/manager-sync.ts +400 -0
- package/src/manager.ts +62 -0
- package/src/monitoring/apply.ts +171 -0
- package/src/monitoring/beszel.ts +191 -0
- package/src/monitoring/enroll.ts +110 -0
- package/src/monitoring/hub-access.ts +113 -0
- package/src/monitoring/hub-viewer.ts +142 -0
- package/src/monitoring/hub.ts +93 -0
- package/src/monitoring/manager-agent.ts +73 -0
- package/src/monitoring/manager-hub.ts +232 -0
- package/src/monitoring/remote.ts +65 -0
- package/src/network-definition.ts +83 -0
- package/src/node-converge-linux.ts +372 -0
- package/src/node-converge-shared.ts +83 -0
- package/src/node-converge.ts +40 -0
- package/src/onboard.ts +35 -0
- package/src/platform/ansible-receipt.ts +128 -0
- package/src/platform/ansible.ts +62 -0
- package/src/platform/darwin.ts +203 -0
- package/src/platform/detect.ts +11 -0
- package/src/platform/fallback.ts +110 -0
- package/src/platform/index.ts +38 -0
- package/src/platform/launchd.ts +98 -0
- package/src/platform/linux.ts +139 -0
- package/src/platform/ps.ts +45 -0
- package/src/platform/resource-verify.ts +128 -0
- package/src/platform/systemd-user.ts +95 -0
- package/src/platform/t3-policy.ts +137 -0
- package/src/platform/t3-route.ts +68 -0
- package/src/platform/t3-service.ts +75 -0
- package/src/platform/types.ts +123 -0
- package/src/platform/ubuntu-audit.ts +63 -0
- package/src/platform/ubuntu-verify.ts +105 -0
- package/src/platform/windows.ts +148 -0
- package/src/power-recovery.ts +55 -0
- package/src/probe.ts +51 -0
- package/src/protocol.ts +119 -0
- package/src/proxy-converge.ts +85 -0
- package/src/remote-bootstrap.ts +212 -0
- package/src/remote-entry.ts +89 -0
- package/src/remote-lock.ts +12 -0
- package/src/repair-catalog.ts +206 -0
- package/src/repair-plan.ts +78 -0
- package/src/repair-runner.ts +84 -0
- package/src/report.ts +71 -0
- package/src/runner.ts +104 -0
- package/src/secrets.ts +169 -0
- package/src/service-auth.ts +115 -0
- package/src/ssh.ts +135 -0
- package/src/sync.ts +104 -0
- package/src/t3/accounts.ts +81 -0
- package/src/t3/desired.ts +165 -0
- package/src/t3/files.ts +81 -0
- package/src/t3/names-check.ts +53 -0
- package/src/t3/names.ts +155 -0
- package/src/t3/plan.ts +82 -0
- package/src/t3/providers.ts +74 -0
- package/src/t3/reconcile.ts +123 -0
- package/src/t3-endpoint.ts +48 -0
- package/src/t3-linked-check.ts +101 -0
- package/src/tailnet-health.ts +231 -0
- package/src/tailnet-matching.ts +37 -0
- package/src/tailscale.ts +141 -0
- package/src/test-fixtures.ts +36 -0
- package/src/toolchain-checks.ts +83 -0
- package/src/topology.ts +24 -0
- package/src/transport.ts +122 -0
- package/src/types.ts +95 -0
- package/src/vault-config.ts +38 -0
- package/src/worker-access-checks.ts +115 -0
- package/src/worker-capacity.ts +34 -0
- package/src/worker-host-checks.ts +176 -0
- package/src/worker-policy.ts +64 -0
- package/src/worker-readiness.ts +64 -0
- package/src/workstation-checks.ts +29 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
- name: Converge native Tailscale without replacing its device identity
|
|
3
|
+
hosts: localhost
|
|
4
|
+
connection: local
|
|
5
|
+
become: true
|
|
6
|
+
gather_facts: true
|
|
7
|
+
tasks:
|
|
8
|
+
- name: Require supported Ubuntu
|
|
9
|
+
ansible.builtin.assert:
|
|
10
|
+
that:
|
|
11
|
+
- ansible_facts.distribution == 'Ubuntu'
|
|
12
|
+
- ansible_facts.distribution_version == '26.04'
|
|
13
|
+
- name: Inspect installed packages before possible network interruption
|
|
14
|
+
ansible.builtin.package_facts:
|
|
15
|
+
manager: apt
|
|
16
|
+
- name: Require maintenance for an installed version change
|
|
17
|
+
ansible.builtin.assert:
|
|
18
|
+
that: "'tailscale' not in ansible_facts.packages or ansible_facts.packages.tailscale[0].version == '1.104.5'"
|
|
19
|
+
fail_msg: TAILSCALE_UPGRADE_PENDING; upgrading an active VPN can disconnect SSH. Review an explicit maintenance operation.
|
|
20
|
+
- name: Inspect existing package-source ownership before writing
|
|
21
|
+
ansible.builtin.find:
|
|
22
|
+
paths: /etc/apt/sources.list.d
|
|
23
|
+
patterns: ['*.list', '*.sources']
|
|
24
|
+
contains: '.*pkgs.tailscale.com.*'
|
|
25
|
+
read_whole_file: true
|
|
26
|
+
register: existing_sources
|
|
27
|
+
- name: Refuse duplicate or custom sources instead of breaking APT
|
|
28
|
+
ansible.builtin.assert:
|
|
29
|
+
that:
|
|
30
|
+
- existing_sources.matched <= 1
|
|
31
|
+
- existing_sources.matched == 0 or existing_sources.files[0].path == '/etc/apt/sources.list.d/tailscale.list'
|
|
32
|
+
fail_msg: TAILSCALE_SOURCE_OWNER_CONFLICT; review the existing signed source and its migration before creating another repository entry.
|
|
33
|
+
- name: Install pinned upstream signing key
|
|
34
|
+
ansible.builtin.get_url:
|
|
35
|
+
url: https://pkgs.tailscale.com/stable/ubuntu/resolute.noarmor.gpg
|
|
36
|
+
dest: /usr/share/keyrings/tailscale-archive-keyring.gpg
|
|
37
|
+
checksum: sha256:3e03dacf222698c60b8e2f990b809ca1b3e104de127767864284e6c228f1fb39
|
|
38
|
+
owner: root
|
|
39
|
+
group: root
|
|
40
|
+
mode: '0644'
|
|
41
|
+
timeout: 30
|
|
42
|
+
- name: Declare the signed upstream package source
|
|
43
|
+
ansible.builtin.apt_repository:
|
|
44
|
+
repo: deb [signed-by=/usr/share/keyrings/tailscale-archive-keyring.gpg] https://pkgs.tailscale.com/stable/ubuntu resolute main
|
|
45
|
+
filename: tailscale
|
|
46
|
+
state: present
|
|
47
|
+
update_cache: false
|
|
48
|
+
register: tailscale_source
|
|
49
|
+
- name: Install the approved version only when absent
|
|
50
|
+
ansible.builtin.apt:
|
|
51
|
+
name: tailscale=1.104.5
|
|
52
|
+
state: present
|
|
53
|
+
update_cache: true
|
|
54
|
+
cache_valid_time: 0
|
|
55
|
+
lock_timeout: 120
|
|
56
|
+
allow_downgrade: false
|
|
57
|
+
allow_unauthenticated: false
|
|
58
|
+
when: "'tailscale' not in ansible_facts.packages"
|
|
59
|
+
- name: Enable and start the native daemon, preserving existing identity
|
|
60
|
+
ansible.builtin.systemd_service:
|
|
61
|
+
name: tailscaled.service
|
|
62
|
+
enabled: true
|
|
63
|
+
state: started
|
|
64
|
+
- name: Inspect native authentication without prompting or opening a browser
|
|
65
|
+
ansible.builtin.command:
|
|
66
|
+
argv: [tailscale, status, '--json']
|
|
67
|
+
changed_when: false
|
|
68
|
+
failed_when: false
|
|
69
|
+
register: tailscale_status
|
|
70
|
+
no_log: true
|
|
71
|
+
- name: Require genuine device authentication before claiming connectivity
|
|
72
|
+
ansible.builtin.assert:
|
|
73
|
+
that: (tailscale_status.stdout | from_json).BackendState == 'Running'
|
|
74
|
+
fail_msg: TAILSCALE_LOGIN_REQUIRED; authorize this device through its native login. Existing state is never copied or reset.
|
|
75
|
+
no_log: true
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
- name: Create persistent worker credential renewal units
|
|
3
|
+
ansible.builtin.file:
|
|
4
|
+
path: '{{ development_home }}/.config/systemd/user'
|
|
5
|
+
state: directory
|
|
6
|
+
owner: '{{ development_user }}'
|
|
7
|
+
mode: '0700'
|
|
8
|
+
- name: Declare native self-token renewal without copying credentials
|
|
9
|
+
ansible.builtin.copy:
|
|
10
|
+
dest: '{{ development_home }}/.config/systemd/user/personal-worker-auth.service'
|
|
11
|
+
owner: '{{ development_user }}'
|
|
12
|
+
mode: '0600'
|
|
13
|
+
content: |
|
|
14
|
+
[Unit]
|
|
15
|
+
Description=Renew this worker's own Vault authorization
|
|
16
|
+
[Service]
|
|
17
|
+
Type=oneshot
|
|
18
|
+
WorkingDirectory={{ development_source }}
|
|
19
|
+
ExecStart={{ development_home }}/.local/bin/mise exec -- bun bootstrap/worker-auth.ts
|
|
20
|
+
Environment="PATH={{ development_home }}/.local/share/mise/shims:{{ development_home }}/.local/bin:/usr/local/bin:/usr/bin:/bin"
|
|
21
|
+
UMask=0077
|
|
22
|
+
TimeoutStartSec=45
|
|
23
|
+
- name: Declare bounded persistent renewal schedule
|
|
24
|
+
ansible.builtin.copy:
|
|
25
|
+
dest: '{{ development_home }}/.config/systemd/user/personal-worker-auth.timer'
|
|
26
|
+
owner: '{{ development_user }}'
|
|
27
|
+
mode: '0600'
|
|
28
|
+
content: |
|
|
29
|
+
[Unit]
|
|
30
|
+
Description=Keep enrolled worker Vault authorization renewed
|
|
31
|
+
[Timer]
|
|
32
|
+
OnBootSec=2min
|
|
33
|
+
OnUnitActiveSec=10min
|
|
34
|
+
Persistent=true
|
|
35
|
+
[Install]
|
|
36
|
+
WantedBy=timers.target
|
|
37
|
+
- name: Enable this user's credential renewal timer
|
|
38
|
+
become: true
|
|
39
|
+
become_user: '{{ development_user }}'
|
|
40
|
+
environment:
|
|
41
|
+
XDG_RUNTIME_DIR: '/run/user/{{ development_uid }}'
|
|
42
|
+
ansible.builtin.systemd_service:
|
|
43
|
+
scope: user
|
|
44
|
+
name: personal-worker-auth.timer
|
|
45
|
+
daemon_reload: true
|
|
46
|
+
enabled: true
|
|
47
|
+
state: started
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
- name: Rotate container logs by default (new containers, after the next Docker restart)
|
|
3
|
+
ansible.builtin.copy:
|
|
4
|
+
dest: /etc/docker/daemon.json
|
|
5
|
+
force: false
|
|
6
|
+
owner: root
|
|
7
|
+
group: root
|
|
8
|
+
mode: '0644'
|
|
9
|
+
content: |
|
|
10
|
+
{
|
|
11
|
+
"log-driver": "json-file",
|
|
12
|
+
"log-opts": { "max-size": "{{ resourceSafety.dockerLogMaxSize }}", "max-file": "{{ resourceSafety.dockerLogMaxFiles }}" }
|
|
13
|
+
}
|
|
14
|
+
validate: dockerd --validate --config-file %s
|
|
15
|
+
- name: Start the native Docker engine without restarting existing workloads
|
|
16
|
+
ansible.builtin.systemd_service:
|
|
17
|
+
name: docker.service
|
|
18
|
+
enabled: true
|
|
19
|
+
state: started
|
|
20
|
+
- name: Grant the designated developer access to the local Docker engine
|
|
21
|
+
ansible.builtin.user:
|
|
22
|
+
name: '{{ development_user }}'
|
|
23
|
+
groups: docker
|
|
24
|
+
append: true
|
|
25
|
+
- name: Verify native Docker Compose and Buildx installation
|
|
26
|
+
ansible.builtin.command:
|
|
27
|
+
argv: '{{ item }}'
|
|
28
|
+
loop:
|
|
29
|
+
- [docker, compose, version]
|
|
30
|
+
- [docker, buildx, version]
|
|
31
|
+
changed_when: false
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
- name: Create owned APT configuration directory
|
|
3
|
+
ansible.builtin.file:
|
|
4
|
+
path: /etc/personal-fleet
|
|
5
|
+
state: directory
|
|
6
|
+
owner: root
|
|
7
|
+
group: root
|
|
8
|
+
mode: '0755'
|
|
9
|
+
- name: Declare snapshot sources used only for managed package transactions
|
|
10
|
+
ansible.builtin.copy:
|
|
11
|
+
dest: /etc/personal-fleet/ubuntu.sources
|
|
12
|
+
owner: root
|
|
13
|
+
group: root
|
|
14
|
+
mode: '0644'
|
|
15
|
+
backup: true
|
|
16
|
+
content: |
|
|
17
|
+
Types: deb
|
|
18
|
+
URIs: https://snapshot.ubuntu.com/ubuntu/{{ snapshot }}/
|
|
19
|
+
Suites: resolute resolute-updates resolute-security
|
|
20
|
+
Components: main universe restricted multiverse
|
|
21
|
+
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
|
|
22
|
+
register: snapshot_sources
|
|
23
|
+
- name: Isolate snapshot selection from unrelated APT operations
|
|
24
|
+
ansible.builtin.copy:
|
|
25
|
+
dest: /etc/personal-fleet/apt.conf
|
|
26
|
+
owner: root
|
|
27
|
+
group: root
|
|
28
|
+
mode: '0644'
|
|
29
|
+
content: |
|
|
30
|
+
Dir::Etc::sourcelist "/etc/personal-fleet/ubuntu.sources";
|
|
31
|
+
Dir::Etc::sourceparts "-";
|
|
32
|
+
DPkg::Lock::Timeout "{{ aptLockTimeoutSeconds }}";
|
|
33
|
+
- name: Inspect immutable snapshot package index
|
|
34
|
+
ansible.builtin.stat:
|
|
35
|
+
path: '/var/lib/apt/lists/snapshot.ubuntu.com_ubuntu_{{ snapshot }}_dists_resolute_InRelease'
|
|
36
|
+
register: snapshot_index
|
|
37
|
+
- name: Fetch signed snapshot indexes after source change or interrupted setup
|
|
38
|
+
ansible.builtin.apt:
|
|
39
|
+
update_cache: true
|
|
40
|
+
cache_valid_time: 0
|
|
41
|
+
lock_timeout: '{{ aptLockTimeoutSeconds }}'
|
|
42
|
+
environment:
|
|
43
|
+
APT_CONFIG: /etc/personal-fleet/apt.conf
|
|
44
|
+
when: snapshot_sources.changed or not snapshot_index.stat.exists
|
|
45
|
+
- name: Install exact managed packages without downgrading or restarting services
|
|
46
|
+
ansible.builtin.apt:
|
|
47
|
+
name: '{{ item.key }}={{ item.value }}'
|
|
48
|
+
state: present
|
|
49
|
+
lock_timeout: '{{ aptLockTimeoutSeconds }}'
|
|
50
|
+
allow_downgrade: false
|
|
51
|
+
allow_unauthenticated: false
|
|
52
|
+
fail_on_autoremove: true
|
|
53
|
+
policy_rc_d: 101
|
|
54
|
+
environment:
|
|
55
|
+
APT_CONFIG: /etc/personal-fleet/apt.conf
|
|
56
|
+
loop: '{{ packages | dict2items }}'
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
- name: Ensure systemd policy directories exist
|
|
3
|
+
ansible.builtin.file:
|
|
4
|
+
path: '{{ item }}'
|
|
5
|
+
state: directory
|
|
6
|
+
owner: root
|
|
7
|
+
group: root
|
|
8
|
+
mode: '0755'
|
|
9
|
+
loop:
|
|
10
|
+
- /etc/systemd/logind.conf.d
|
|
11
|
+
- /etc/systemd/sleep.conf.d
|
|
12
|
+
- name: Declare idle and lid policy without restarting login sessions
|
|
13
|
+
ansible.builtin.copy:
|
|
14
|
+
dest: /etc/systemd/logind.conf.d/60-personal-fleet.conf
|
|
15
|
+
owner: root
|
|
16
|
+
group: root
|
|
17
|
+
mode: '0644'
|
|
18
|
+
backup: true
|
|
19
|
+
content: |
|
|
20
|
+
# Managed by personal fleet-sync. Effective after logind reload/reboot.
|
|
21
|
+
[Login]
|
|
22
|
+
HandleLidSwitch=ignore
|
|
23
|
+
HandleLidSwitchExternalPower=ignore
|
|
24
|
+
HandleLidSwitchDocked=ignore
|
|
25
|
+
IdleAction=ignore
|
|
26
|
+
KillUserProcesses=no
|
|
27
|
+
StopIdleSessionSec=infinity
|
|
28
|
+
- name: Declare supported sleep policy
|
|
29
|
+
ansible.builtin.copy:
|
|
30
|
+
dest: /etc/systemd/sleep.conf.d/60-personal-fleet.conf
|
|
31
|
+
owner: root
|
|
32
|
+
group: root
|
|
33
|
+
mode: '0644'
|
|
34
|
+
backup: true
|
|
35
|
+
content: |
|
|
36
|
+
[Sleep]
|
|
37
|
+
AllowSuspend=no
|
|
38
|
+
AllowHibernation=no
|
|
39
|
+
AllowSuspendThenHibernate=no
|
|
40
|
+
AllowHybridSleep=no
|
|
41
|
+
- name: Mask sleep transitions without stopping running user sessions
|
|
42
|
+
ansible.builtin.systemd_service:
|
|
43
|
+
name: '{{ item }}'
|
|
44
|
+
masked: true
|
|
45
|
+
daemon_reload: true
|
|
46
|
+
loop:
|
|
47
|
+
- sleep.target
|
|
48
|
+
- suspend.target
|
|
49
|
+
- hibernate.target
|
|
50
|
+
- hybrid-sleep.target
|
|
51
|
+
- suspend-then-hibernate.target
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
- name: Declare bounded recovery of the worker T3 desktop app
|
|
3
|
+
ansible.builtin.copy:
|
|
4
|
+
dest: '{{ development_home }}/.config/systemd/user/personal-worker-recovery.service'
|
|
5
|
+
owner: '{{ development_user }}'
|
|
6
|
+
mode: '0600'
|
|
7
|
+
content: |
|
|
8
|
+
# Managed by personal fleet-sync: ansible/ubuntu/t3-health.sh
|
|
9
|
+
[Unit]
|
|
10
|
+
Description=Keep the worker T3 desktop app running and responsive
|
|
11
|
+
[Service]
|
|
12
|
+
Type=oneshot
|
|
13
|
+
ExecStart=/bin/sh {{ development_source }}/ansible/ubuntu/t3-health.sh
|
|
14
|
+
TimeoutStartSec=90
|
|
15
|
+
- name: Declare persistent service recovery checks
|
|
16
|
+
ansible.builtin.copy:
|
|
17
|
+
dest: '{{ development_home }}/.config/systemd/user/personal-worker-recovery.timer'
|
|
18
|
+
owner: '{{ development_user }}'
|
|
19
|
+
mode: '0600'
|
|
20
|
+
content: |
|
|
21
|
+
[Unit]
|
|
22
|
+
Description=Check the worker T3 desktop app every minute
|
|
23
|
+
[Timer]
|
|
24
|
+
OnBootSec=1min
|
|
25
|
+
OnUnitActiveSec=1min
|
|
26
|
+
Persistent=true
|
|
27
|
+
[Install]
|
|
28
|
+
WantedBy=timers.target
|
|
29
|
+
- name: Enable bounded T3 recovery without restarting active sessions
|
|
30
|
+
become: true
|
|
31
|
+
become_user: '{{ development_user }}'
|
|
32
|
+
environment:
|
|
33
|
+
XDG_RUNTIME_DIR: '/run/user/{{ development_uid }}'
|
|
34
|
+
ansible.builtin.systemd_service:
|
|
35
|
+
scope: user
|
|
36
|
+
name: personal-worker-recovery.timer
|
|
37
|
+
daemon_reload: true
|
|
38
|
+
enabled: true
|
|
39
|
+
state: started
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Memory and disk safety: a runaway workload is stopped on its own before the
|
|
3
|
+
# machine, SSH, Tailscale or the T3 server run out of memory or disk. These
|
|
4
|
+
# tasks run before the capacity preflight because they matter most when a disk
|
|
5
|
+
# is nearly full; they only write small files and never delete user data.
|
|
6
|
+
- name: Inspect active swap
|
|
7
|
+
ansible.builtin.command:
|
|
8
|
+
argv: [swapon, --show=NAME, --noheadings]
|
|
9
|
+
changed_when: false
|
|
10
|
+
check_mode: false
|
|
11
|
+
register: active_swap
|
|
12
|
+
- name: Measure root free space before creating swap
|
|
13
|
+
ansible.builtin.command:
|
|
14
|
+
argv: [df, --output=avail, -BM, /]
|
|
15
|
+
changed_when: false
|
|
16
|
+
check_mode: false
|
|
17
|
+
register: swap_capacity
|
|
18
|
+
when: active_swap.stdout | trim == ''
|
|
19
|
+
- name: Refuse to create swap on a nearly full disk
|
|
20
|
+
ansible.builtin.assert:
|
|
21
|
+
that: (swap_capacity.stdout_lines | last | trim | regex_replace('M$', '') | int) >= resourceSafety.swapFileMiB + minimumFreeDiskMiB
|
|
22
|
+
fail_msg: SWAP_CAPACITY_REQUIRED; free disk space so a swap file can be created. No swap is active.
|
|
23
|
+
when: active_swap.stdout | trim == ''
|
|
24
|
+
- name: Create a swap file when the node has no swap
|
|
25
|
+
ansible.builtin.command:
|
|
26
|
+
argv: [fallocate, -l, '{{ resourceSafety.swapFileMiB }}M', /swap.fleet]
|
|
27
|
+
creates: /swap.fleet
|
|
28
|
+
when: active_swap.stdout | trim == ''
|
|
29
|
+
- name: Restrict the fleet swap file
|
|
30
|
+
ansible.builtin.file:
|
|
31
|
+
path: /swap.fleet
|
|
32
|
+
owner: root
|
|
33
|
+
group: root
|
|
34
|
+
mode: '0600'
|
|
35
|
+
when: active_swap.stdout | trim == ''
|
|
36
|
+
- name: Format the fleet swap file
|
|
37
|
+
ansible.builtin.command:
|
|
38
|
+
argv: [mkswap, /swap.fleet]
|
|
39
|
+
when: active_swap.stdout | trim == ''
|
|
40
|
+
- name: Activate the fleet swap file
|
|
41
|
+
ansible.builtin.command:
|
|
42
|
+
argv: [swapon, /swap.fleet]
|
|
43
|
+
when: active_swap.stdout | trim == ''
|
|
44
|
+
- name: Activate the fleet swap file at boot
|
|
45
|
+
ansible.builtin.lineinfile:
|
|
46
|
+
path: /etc/fstab
|
|
47
|
+
line: /swap.fleet none swap sw 0 0
|
|
48
|
+
when: active_swap.stdout | trim == ''
|
|
49
|
+
|
|
50
|
+
- name: Install the systemd userspace OOM killer
|
|
51
|
+
ansible.builtin.apt:
|
|
52
|
+
name: systemd-oomd
|
|
53
|
+
state: present
|
|
54
|
+
- name: Create memory and disk policy directories
|
|
55
|
+
ansible.builtin.file:
|
|
56
|
+
path: '{{ item }}'
|
|
57
|
+
state: directory
|
|
58
|
+
owner: root
|
|
59
|
+
group: root
|
|
60
|
+
mode: '0755'
|
|
61
|
+
loop:
|
|
62
|
+
- /etc/systemd/oomd.conf.d
|
|
63
|
+
- /etc/systemd/system/-.slice.d
|
|
64
|
+
- /etc/systemd/system/user.slice.d
|
|
65
|
+
- /etc/systemd/user/t3code-.scope.d
|
|
66
|
+
- /etc/systemd/journald.conf.d
|
|
67
|
+
- /etc/systemd/system/systemd-tmpfiles-clean.timer.d
|
|
68
|
+
- /etc/tmpfiles.d
|
|
69
|
+
- name: Declare systemd-oomd limits
|
|
70
|
+
ansible.builtin.copy:
|
|
71
|
+
dest: /etc/systemd/oomd.conf.d/60-personal-fleet.conf
|
|
72
|
+
owner: root
|
|
73
|
+
group: root
|
|
74
|
+
mode: '0644'
|
|
75
|
+
content: |
|
|
76
|
+
# Managed by personal fleet-sync: kill one cgroup before swap or memory pressure stalls the node.
|
|
77
|
+
[OOM]
|
|
78
|
+
SwapUsedLimit=90%
|
|
79
|
+
DefaultMemoryPressureLimit=60%
|
|
80
|
+
DefaultMemoryPressureDurationSec=20s
|
|
81
|
+
register: oomd_config
|
|
82
|
+
- name: Declare memory reserve and OOM policy for system and user slices
|
|
83
|
+
ansible.builtin.copy:
|
|
84
|
+
dest: '{{ item.dest }}'
|
|
85
|
+
owner: root
|
|
86
|
+
group: root
|
|
87
|
+
mode: '0644'
|
|
88
|
+
content: '{{ item.content }}'
|
|
89
|
+
loop:
|
|
90
|
+
- dest: /etc/systemd/system/-.slice.d/60-personal-fleet.conf
|
|
91
|
+
content: |
|
|
92
|
+
# Managed by personal fleet-sync: when swap is exhausted, oomd kills the largest swap user.
|
|
93
|
+
[Slice]
|
|
94
|
+
ManagedOOMSwap=kill
|
|
95
|
+
- dest: /etc/systemd/system/user.slice.d/60-personal-fleet.conf
|
|
96
|
+
content: |
|
|
97
|
+
# Managed by personal fleet-sync: user workloads can never take the memory SSH, Tailscale and system services need.
|
|
98
|
+
[Slice]
|
|
99
|
+
MemoryMax={{ resourceSafety.userMemoryMax }}
|
|
100
|
+
loop_control:
|
|
101
|
+
label: '{{ item.dest }}'
|
|
102
|
+
register: slice_policy
|
|
103
|
+
- name: Bound each T3 agent so a runaway loses one process, not the session or the node
|
|
104
|
+
ansible.builtin.copy:
|
|
105
|
+
dest: /etc/systemd/user/t3code-.scope.d/60-personal-fleet.conf
|
|
106
|
+
owner: root
|
|
107
|
+
group: root
|
|
108
|
+
mode: '0644'
|
|
109
|
+
content: |
|
|
110
|
+
# Managed by personal fleet-sync. Applies to every t3code-*.scope T3 starts for an agent.
|
|
111
|
+
# OOMPolicy=continue: the kernel kills the largest process in the agent; the agent itself keeps running.
|
|
112
|
+
[Scope]
|
|
113
|
+
MemoryMax={{ resourceSafety.agentMemoryMax }}
|
|
114
|
+
MemorySwapMax={{ resourceSafety.agentSwapMax }}
|
|
115
|
+
OOMPolicy=continue
|
|
116
|
+
register: agent_policy
|
|
117
|
+
- name: Apply slice limits without restarting services
|
|
118
|
+
ansible.builtin.systemd_service:
|
|
119
|
+
daemon_reload: true
|
|
120
|
+
when: slice_policy.changed
|
|
121
|
+
- name: Load the agent limits into the running user manager for new agents
|
|
122
|
+
become: true
|
|
123
|
+
become_user: '{{ development_user }}'
|
|
124
|
+
environment:
|
|
125
|
+
XDG_RUNTIME_DIR: '/run/user/{{ development_uid }}'
|
|
126
|
+
ansible.builtin.systemd_service:
|
|
127
|
+
scope: user
|
|
128
|
+
daemon_reload: true
|
|
129
|
+
when: agent_policy.changed
|
|
130
|
+
failed_when: false
|
|
131
|
+
- name: Run systemd-oomd
|
|
132
|
+
ansible.builtin.systemd_service:
|
|
133
|
+
name: systemd-oomd.service
|
|
134
|
+
enabled: true
|
|
135
|
+
state: '{{ "restarted" if oomd_config.changed else "started" }}'
|
|
136
|
+
|
|
137
|
+
- name: Cap the system journal
|
|
138
|
+
ansible.builtin.copy:
|
|
139
|
+
dest: /etc/systemd/journald.conf.d/60-personal-fleet.conf
|
|
140
|
+
owner: root
|
|
141
|
+
group: root
|
|
142
|
+
mode: '0644'
|
|
143
|
+
content: |
|
|
144
|
+
# Managed by personal fleet-sync.
|
|
145
|
+
[Journal]
|
|
146
|
+
SystemMaxUse={{ resourceSafety.journalMaxUse }}
|
|
147
|
+
SystemKeepFree={{ resourceSafety.journalKeepFree }}
|
|
148
|
+
RuntimeMaxUse=256M
|
|
149
|
+
register: journal_policy
|
|
150
|
+
- name: Apply the journal cap
|
|
151
|
+
ansible.builtin.systemd_service:
|
|
152
|
+
name: systemd-journald.service
|
|
153
|
+
state: restarted
|
|
154
|
+
when: journal_policy.changed
|
|
155
|
+
- name: Age out abandoned temporary files
|
|
156
|
+
ansible.builtin.copy:
|
|
157
|
+
dest: /etc/tmpfiles.d/tmp.conf
|
|
158
|
+
owner: root
|
|
159
|
+
group: root
|
|
160
|
+
mode: '0644'
|
|
161
|
+
content: |
|
|
162
|
+
# Managed by personal fleet-sync; overrides /usr/lib/tmpfiles.d/tmp.conf.
|
|
163
|
+
q /tmp 1777 root root {{ resourceSafety.tmpAge }}
|
|
164
|
+
q /var/tmp 1777 root root {{ resourceSafety.varTmpAge }}
|
|
165
|
+
- name: Clean temporary files hourly instead of daily
|
|
166
|
+
ansible.builtin.copy:
|
|
167
|
+
dest: /etc/systemd/system/systemd-tmpfiles-clean.timer.d/60-personal-fleet.conf
|
|
168
|
+
owner: root
|
|
169
|
+
group: root
|
|
170
|
+
mode: '0644'
|
|
171
|
+
content: |
|
|
172
|
+
# Managed by personal fleet-sync.
|
|
173
|
+
[Timer]
|
|
174
|
+
OnUnitActiveSec=1h
|
|
175
|
+
register: tmpfiles_timer
|
|
176
|
+
- name: Inspect the RAM-backed /tmp unit
|
|
177
|
+
ansible.builtin.stat:
|
|
178
|
+
path: /usr/lib/systemd/system/tmp.mount
|
|
179
|
+
register: tmp_mount
|
|
180
|
+
- name: Create the /tmp mount policy directory
|
|
181
|
+
ansible.builtin.file:
|
|
182
|
+
path: /etc/systemd/system/tmp.mount.d
|
|
183
|
+
state: directory
|
|
184
|
+
owner: root
|
|
185
|
+
group: root
|
|
186
|
+
mode: '0755'
|
|
187
|
+
when: tmp_mount.stat.exists
|
|
188
|
+
- name: Bound RAM-backed /tmp (effective after the next reboot)
|
|
189
|
+
ansible.builtin.copy:
|
|
190
|
+
dest: /etc/systemd/system/tmp.mount.d/60-personal-fleet.conf
|
|
191
|
+
owner: root
|
|
192
|
+
group: root
|
|
193
|
+
mode: '0644'
|
|
194
|
+
content: |
|
|
195
|
+
# Managed by personal fleet-sync: /tmp is RAM; keep it from taking half of memory.
|
|
196
|
+
[Mount]
|
|
197
|
+
Options=mode=1777,strictatime,nosuid,nodev,size={{ resourceSafety.tmpMaxSize }},nr_inodes=1m,x-systemd.graceful-option=usrquota
|
|
198
|
+
when: tmp_mount.stat.exists
|
|
199
|
+
register: tmp_policy
|
|
200
|
+
- name: Reload the cleanup timer and /tmp definitions
|
|
201
|
+
ansible.builtin.systemd_service:
|
|
202
|
+
daemon_reload: true
|
|
203
|
+
when: tmpfiles_timer.changed or tmp_policy.changed
|
|
204
|
+
|
|
205
|
+
- name: Read the tracked disk guard
|
|
206
|
+
ansible.builtin.stat:
|
|
207
|
+
path: '{{ development_source }}/ansible/ubuntu/disk-guard.py'
|
|
208
|
+
checksum_algorithm: sha256
|
|
209
|
+
register: disk_guard_source
|
|
210
|
+
when: development_source is defined
|
|
211
|
+
- name: Declare the disk guard service
|
|
212
|
+
ansible.builtin.copy:
|
|
213
|
+
dest: /etc/systemd/system/personal-fleet-disk-guard.service
|
|
214
|
+
owner: root
|
|
215
|
+
group: root
|
|
216
|
+
mode: '0644'
|
|
217
|
+
content: |
|
|
218
|
+
# Managed by personal fleet-sync: ansible/ubuntu/disk-guard.py
|
|
219
|
+
# Source sha256 {{ disk_guard_source.stat.checksum }}
|
|
220
|
+
[Unit]
|
|
221
|
+
Description=Personal Fleet disk guard (stops a runaway user writer before the disk fills)
|
|
222
|
+
StartLimitIntervalSec=0
|
|
223
|
+
[Service]
|
|
224
|
+
Type=simple
|
|
225
|
+
ExecStart=/usr/bin/python3 -I -B {{ disk_guard_source.stat.path }} --path / --path {{ development_home }} --floor-mib {{ resourceSafety.diskGuardFloorMiB }} --write-mib {{ resourceSafety.diskGuardWriteMiB }} --interval {{ resourceSafety.diskGuardIntervalSeconds }}
|
|
226
|
+
Restart=always
|
|
227
|
+
RestartSec=5
|
|
228
|
+
OOMScoreAdjust=-1000
|
|
229
|
+
MemoryMax=64M
|
|
230
|
+
MemorySwapMax=0
|
|
231
|
+
CPUQuota=10%
|
|
232
|
+
TasksMax=4
|
|
233
|
+
NoNewPrivileges=true
|
|
234
|
+
PrivateTmp=true
|
|
235
|
+
PrivateNetwork=true
|
|
236
|
+
ProtectSystem=strict
|
|
237
|
+
ProtectHome=read-only
|
|
238
|
+
ReadWritePaths=/sys/fs/cgroup
|
|
239
|
+
ProtectKernelModules=true
|
|
240
|
+
LogRateLimitIntervalSec=60
|
|
241
|
+
LogRateLimitBurst=20
|
|
242
|
+
[Install]
|
|
243
|
+
WantedBy=multi-user.target
|
|
244
|
+
when: development_source is defined and disk_guard_source.stat.exists
|
|
245
|
+
register: disk_guard_unit
|
|
246
|
+
- name: Run the disk guard
|
|
247
|
+
ansible.builtin.systemd_service:
|
|
248
|
+
name: personal-fleet-disk-guard.service
|
|
249
|
+
daemon_reload: '{{ disk_guard_unit.changed }}'
|
|
250
|
+
enabled: true
|
|
251
|
+
state: '{{ "restarted" if disk_guard_unit.changed else "started" }}'
|
|
252
|
+
when: development_source is defined and disk_guard_source.stat.exists
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Always-on recovery for the system services every worker depends on. Drop-ins
|
|
3
|
+
# change restart policy only; running services are never restarted here.
|
|
4
|
+
- name: Create recovery drop-in directories for required system services
|
|
5
|
+
ansible.builtin.file:
|
|
6
|
+
path: '/etc/systemd/system/{{ item }}.service.d'
|
|
7
|
+
state: directory
|
|
8
|
+
owner: root
|
|
9
|
+
group: root
|
|
10
|
+
mode: '0755'
|
|
11
|
+
loop: '{{ alwaysOnServices }}'
|
|
12
|
+
- name: Declare unlimited automatic restart for required system services
|
|
13
|
+
ansible.builtin.copy:
|
|
14
|
+
dest: '/etc/systemd/system/{{ item }}.service.d/60-personal-fleet.conf'
|
|
15
|
+
owner: root
|
|
16
|
+
group: root
|
|
17
|
+
mode: '0644'
|
|
18
|
+
backup: true
|
|
19
|
+
content: |
|
|
20
|
+
# Managed by personal fleet-sync: keep this worker service always on.
|
|
21
|
+
[Unit]
|
|
22
|
+
StartLimitIntervalSec=0
|
|
23
|
+
[Service]
|
|
24
|
+
Restart=always
|
|
25
|
+
RestartSec=5
|
|
26
|
+
loop: '{{ alwaysOnServices }}'
|
|
27
|
+
register: recovery_dropins
|
|
28
|
+
- name: Reload unit definitions without restarting running services
|
|
29
|
+
ansible.builtin.systemd_service:
|
|
30
|
+
daemon_reload: true
|
|
31
|
+
when: recovery_dropins.changed
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
- name: Inspect effective SSH configuration before changes
|
|
3
|
+
ansible.builtin.command: /usr/sbin/sshd -T
|
|
4
|
+
changed_when: false
|
|
5
|
+
register: effective_ssh
|
|
6
|
+
- name: Inspect any existing managed SSH fragment
|
|
7
|
+
ansible.builtin.stat:
|
|
8
|
+
path: /etc/ssh/sshd_config.d/60-personal-fleet.conf
|
|
9
|
+
register: ssh_owned_file
|
|
10
|
+
- name: Read previous fragment for validation rollback
|
|
11
|
+
ansible.builtin.slurp:
|
|
12
|
+
src: /etc/ssh/sshd_config.d/60-personal-fleet.conf
|
|
13
|
+
when: ssh_owned_file.stat.exists
|
|
14
|
+
register: ssh_previous
|
|
15
|
+
no_log: true
|
|
16
|
+
- name: Refuse to overwrite unmanaged SSH configuration
|
|
17
|
+
ansible.builtin.assert:
|
|
18
|
+
that:
|
|
19
|
+
- not ssh_owned_file.stat.exists or (ssh_previous.content | b64decode).startswith('# Managed by personal Fleet')
|
|
20
|
+
fail_msg: SSH_UNMANAGED_CONFIG_CONFLICT; review the existing fragment before adoption.
|
|
21
|
+
- name: Stage and validate forwarding policy while preserving listeners and authentication
|
|
22
|
+
block:
|
|
23
|
+
- name: Write forwarding-only owned fragment
|
|
24
|
+
ansible.builtin.copy:
|
|
25
|
+
dest: /etc/ssh/sshd_config.d/60-personal-fleet.conf
|
|
26
|
+
owner: root
|
|
27
|
+
group: root
|
|
28
|
+
mode: '0644'
|
|
29
|
+
backup: true
|
|
30
|
+
content: |
|
|
31
|
+
# Managed by personal Fleet; listeners and authentication remain explicit policy.
|
|
32
|
+
AllowTcpForwarding yes
|
|
33
|
+
ClientAliveInterval 30
|
|
34
|
+
ClientAliveCountMax 3
|
|
35
|
+
register: ssh_fragment
|
|
36
|
+
- name: Validate complete SSH configuration
|
|
37
|
+
ansible.builtin.command: /usr/sbin/sshd -t
|
|
38
|
+
changed_when: false
|
|
39
|
+
rescue:
|
|
40
|
+
- name: Restore previous fragment after validation failure
|
|
41
|
+
ansible.builtin.copy:
|
|
42
|
+
content: '{{ ssh_previous.content | b64decode }}'
|
|
43
|
+
dest: /etc/ssh/sshd_config.d/60-personal-fleet.conf
|
|
44
|
+
owner: root
|
|
45
|
+
group: root
|
|
46
|
+
mode: '0644'
|
|
47
|
+
when: ssh_owned_file.stat.exists
|
|
48
|
+
no_log: true
|
|
49
|
+
- name: Remove rejected new fragment
|
|
50
|
+
ansible.builtin.file:
|
|
51
|
+
path: /etc/ssh/sshd_config.d/60-personal-fleet.conf
|
|
52
|
+
state: absent
|
|
53
|
+
when: not ssh_owned_file.stat.exists
|
|
54
|
+
- name: Report validation failure without reloading sshd
|
|
55
|
+
ansible.builtin.fail:
|
|
56
|
+
msg: SSH_CONFIG_REJECTED; previous configuration restored, active service untouched.
|
|
57
|
+
- name: Enable existing SSH service at boot
|
|
58
|
+
ansible.builtin.systemd_service:
|
|
59
|
+
name: ssh.service
|
|
60
|
+
enabled: true
|
|
61
|
+
state: started
|
|
62
|
+
- name: Reload validated forwarding policy without terminating sessions
|
|
63
|
+
ansible.builtin.systemd_service:
|
|
64
|
+
name: ssh.service
|
|
65
|
+
state: reloaded
|
|
66
|
+
when: ssh_fragment.changed
|
|
67
|
+
- name: Verify effective forwarding after reload
|
|
68
|
+
ansible.builtin.command: /usr/sbin/sshd -T
|
|
69
|
+
changed_when: false
|
|
70
|
+
register: ssh_verified
|
|
71
|
+
failed_when: "'allowtcpforwarding yes' not in ssh_verified.stdout"
|