| 1 | # One release-adoption pass WITHOUT the canary-TTL nag — the privileged half |
| 2 | # of fast release convergence. Started only by carlos-stage.path, which fires |
| 3 | # when the agent touches /data/stage-requested; it has no [Install] section |
| 4 | # because nothing else should ever want it. Same root + hardening shape as |
| 5 | # carlos-adopt.service, which is what writing /opt/carlos/versions, moving |
| 6 | # live/<host> and restarting units requires. |
| 7 | # |
| 8 | # Deliberately NOT carlos-adopt.service, and the reason is the nag alone: that |
| 9 | # unit prints one line per expired canary/* route, an HOURLY visibility |
| 10 | # feature, and this trigger fires on agent request (throttled to one a minute |
| 11 | # per loop, not once an hour). Pointing the relay at the full pass would flood |
| 12 | # the journal with the same expired-canary lines — exactly what |
| 13 | # NagExpiredCanaries' own doc comment says never to do. Everything else the |
| 14 | # hourly pass does is KEPT: config sync and the capability converge are both |
| 15 | # jobs only a root pass can perform, and a version staged now wants its unit |
| 16 | # shape and env file converged with it rather than an hour later. |
| 17 | [Unit] |
| 18 | Description=carlos release staging (on agent request, no canary nag) |
| 19 | After=network-online.target |
| 20 | Wants=network-online.target |
| 21 | # A path unit re-triggers as often as the agent asks; the default start rate |
| 22 | # limit would latch this unit into start-limit-hit after a burst and then |
| 23 | # ignore every later trigger until an operator reset it. ([Unit], not |
| 24 | # [Service] — systemd moved these two keys in v230.) |
| 25 | StartLimitIntervalSec=0 |
| 26 | |
| 27 | [Service] |
| 28 | Type=oneshot |
| 29 | # CARLOS_DEPLOYMENT_BUCKET/_DIR + CARLOS_RELEASE_PUBKEY live here. |
| 30 | EnvironmentFile=-/etc/carlos/host.env |
| 31 | ExecStart=/opt/carlos/carlos adopt --stage-only |
| 32 | |
| 33 | # --- hardening --- |
| 34 | # Runs as ROOT deliberately, exactly like carlos-adopt.service: a pass writes |
| 35 | # the version store (/opt/carlos) and restarts instance units via systemctl. |
| 36 | # NoNewPrivileges and the syscall filter are omitted for the systemctl child; |
| 37 | # the rest applies. |
| 38 | ProtectHome=yes |
| 39 | PrivateTmp=yes |
| 40 | PrivateDevices=yes |
| 41 | ProtectClock=yes |
| 42 | ProtectHostname=yes |
| 43 | ProtectKernelTunables=yes |
| 44 | ProtectKernelModules=yes |
| 45 | ProtectKernelLogs=yes |
| 46 | RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX |
| 47 | RestrictNamespaces=yes |
| 48 | RestrictRealtime=yes |
| 49 | LockPersonality=yes |
| 50 | MemoryDenyWriteExecute=yes |
| 51 | MemoryMax=256M |
| 52 | TasksMax=64 |
| 53 | |