Files
zooqueenandHanzo Dev f3554b2490 Egress carries AWS: it signs each request with SigV4 for an endpoint its -aws list admits, from a role it assumes with its own IAM identity or a sealed key pair, and carries non-JSON bodies and answers as bytes with their Content-Type.
The platform's account is read from cloud/aws/<label>/credential in egress's KMS org. A role descriptor there may be in the clear because it holds no secret; a key pair must be sealed. A caller cannot enrol a role, and a role at a caller's own path is refused. A JSON call and a JSON answer are unchanged, and a call with no body no longer sends the literal null.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-23 17:47:30 -07:00

89 lines
3.3 KiB
Desktop File

[Unit]
Description=Hanzo Egress — the outbound trust boundary
Documentation=https://git.hanzo.ai/hanzoai/egress
After=network-online.target
Wants=network-online.target
[Service]
Type=exec
ExecStart=/usr/local/bin/egress
Restart=always
RestartSec=2s
# Configuration arrives from this host and nowhere else, and it is only
# configuration: the identity moved to the sealed credential below, so this file
# now holds no secret at all. Root-owned 0400 regardless — an endpoint and an
# audience still describe where this process spends.
EnvironmentFile=/etc/egress/env
# THE IDENTITY, DECRYPTED INTO MEMORY AND NOWHERE ELSE.
#
# systemd decrypts this and places it in %d, a per-service directory that is
# memory-backed and readable only by this unit. So it is ciphertext on disk,
# plaintext only inside this process, and absent from the environment — where
# /proc/<pid>/environ would otherwise hand it to anything running as root.
#
# --with-key=host binds it to /var/lib/systemd/credential.secret, which lives on
# the LUKS root: ciphertext at rest twice over, and a disk read without the
# passphrase yields neither. Encrypt it with:
# systemd-creds encrypt --with-key=host --name=mnemonic - /etc/egress/mnemonic.cred
LoadCredentialEncrypted=mnemonic:/etc/egress/mnemonic.cred
# This service's IAM client secret, encrypted the same way. The https store
# transport signs in with it, and it buys the web identity an AWS role is assumed
# with; a missing one is a refusal rather than a fallback.
LoadCredentialEncrypted=client-secret:/etc/egress/client-secret.cred
# THE KEY THAT OPENS EVERY PROVIDER CREDENTIAL. Its public half, EGRESS_RECIPIENT
# in the environment file, seals them, and lives openly wherever it likes —
# nobody who holds it can read anything. This half is why: it exists on this
# machine, encrypted to this machine, and every credential in KMS is ciphertext
# to everyone who does not have it. Mint the pair once, keep this half here and
# nowhere else:
# systemd-creds encrypt --with-key=host --name=identity - /etc/egress/identity.cred
LoadCredentialEncrypted=identity:/etc/egress/identity.cred
User=egress
Group=egress
DynamicUser=no
# A provider key exists only in this process's memory. These stop it reaching
# anywhere it could be read back.
#
# LimitMEMLOCK is what lets the process pin every page it holds: without it
# mlockall fails, egress refuses to start, and it refuses on purpose — the
# kernel may page anonymous memory to swap whenever it likes, and a key that has
# been swapped is a key on a disk that outlives the process.
LimitMEMLOCK=infinity
LimitCORE=0
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=strict
ProtectHome=yes
ProtectProc=invisible
ProcSubset=pid
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
ProtectClock=yes
ProtectHostname=yes
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
RestrictAddressFamilies=AF_INET AF_INET6
LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
CapabilityBoundingSet=
AmbientCapabilities=
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@debug @mount @obsolete @privileged @resources
UMask=0077
# No writable path at all: egress writes no files by design, and a process that
# cannot write cannot spill a credential to disk.
ReadWritePaths=
[Install]
WantedBy=multi-user.target