[Unit] Description=Syncova Backup Agent Documentation=file:///usr/share/doc/syncova/agent.md # Ohne Netz ist die Anmeldung am Control Server zwecklos. After=network-online.target Wants=network-online.target [Service] Type=simple # --state erwartet eine **Datei**, kein Verzeichnis. # # Stand hier zuvor ein Verzeichnispfad. Der Agent haette beim Start gemeldet, # er sei bereits registriert (ein vorhandenes Verzeichnis sieht fuer ihn wie # eine Zustandsdatei aus) und waere ohne Token gelaufen. ExecStart=/usr/local/bin/syncova-agent run --state /var/lib/syncova-agent/agent.json # Der Verschlüsselungsschlüssel steht in einer Datei, nicht in Environment=. # Ein Environment= im Unit-File wäre für jeden Benutzer über # "systemctl show" lesbar (PROMPT.md §141: keine Secrets im Klartext dort, wo # sie nicht hingehören). Die Datei gehört root und trägt Modus 0600. EnvironmentFile=/etc/syncova/agent.env # Ein abgelehntes Token behebt sich nicht durch Warten: der Agent beendet sich # dann mit einem von Null verschiedenen Status. "on-failure" startete ihn in # einer Endlosschleife neu und füllte nur das Journal. Deshalb wird ausdrücklich # ein Startversuchslimit gesetzt. Restart=on-failure RestartSec=15s StartLimitBurst=5 StartLimitIntervalSec=300 # --- Härtung ------------------------------------------------------------- # Der Agent liest fremde Daten und spricht mit dem Netz. Er ist damit genau die # Art Dienst, die man einsperrt. # Ein eigenes Systemkonto; angelegt mit: # useradd --system --home /var/lib/syncova-agent --shell /usr/sbin/nologin syncova-agent # # Das Konto braucht Leserechte auf alles, was gesichert werden soll, und # Schreibrechte auf das Repository. Fehlt eines davon, meldet der Agent das als # uebergangenes Objekt — der Lauf wird zum Teilfehler, nicht zum stillen Erfolg. User=syncova-agent Group=syncova-agent NoNewPrivileges=true PrivateTmp=true PrivateDevices=true ProtectSystem=strict ProtectHome=read-only ProtectKernelTunables=true ProtectKernelModules=true ProtectKernelLogs=true ProtectControlGroups=true ProtectClock=true ProtectHostname=true ProtectProc=invisible RestrictSUIDSGID=true RestrictRealtime=true RestrictNamespaces=true LockPersonality=true MemoryDenyWriteExecute=true RemoveIPC=true # Der Agent schreibt an genau zwei Stellen: seinen Zustand und das Repository. # # **Der Repositorypfad ist Pflicht.** Bei ProtectSystem=strict ist das gesamte # Dateisystem nur lesbar; ohne diesen Eintrag koennte der Agent zwar lesen und # hashen, aber keinen einzigen Block ablegen — die Sicherung schluege mit einem # Schreibfehler fehl, und zwar erst nach dem Lesen der ganzen Quelle. # # Der Pfad ist an die eigene Anlage anzupassen. Liegt das Repository auf einer # Freigabe, gehoert deren Einhaengepunkt hierher. ReadWritePaths=/var/lib/syncova-agent ReadWritePaths=/srv/syncova-repository # IP-Sockets fuer den Control Server — und AF_UNIX fuer die Namensaufloesung. # # AF_UNIX stand hier zuvor nicht. Auf Systemen mit systemd-resolved oder nscd # laeuft die Namensaufloesung ueber einen Unix-Socket: Ohne AF_UNIX findet der # Agent seinen Server nicht und meldet einen Netzfehler, waehrend das Netz # einwandfrei arbeitet. RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX # DAC_READ_SEARCH erlaubt das Lesen fremder Dateien, ohne dem Agent root zu # geben. Ohne diese Fähigkeit müsste er als root laufen — der Unterschied ist # beträchtlich: Lesen ist etwas anderes als Alleskönnen. AmbientCapabilities=CAP_DAC_READ_SEARCH CapabilityBoundingSet=CAP_DAC_READ_SEARCH # Systemaufrufe auf das Nötige beschränken. SystemCallFilter=@system-service SystemCallFilter=~@privileged @resources @obsolete @mount @swap @reboot SystemCallArchitectures=native # Ein Backup soll dem laufenden Betrieb nicht die Maschine wegnehmen. Nice=10 IOSchedulingClass=idle IOSchedulingPriority=7 # Das Journal ist die Protokollsenke; der Agent schreibt strukturiert nach stderr. StandardOutput=journal StandardError=journal SyslogIdentifier=syncova-agent [Install] WantedBy=multi-user.target