Wiki: Architecture/03-Deployment
Deployment and network
- Listeners
- Processes and accounts
- Filesystem
- Outbound connections from gitbayd
- Host firewall
- Change path
The reference deployment is one Linux host built from
deploy/cloud-init.yaml, with the daemon installed by make deploy
(deploy/install.sh) and the CI runner by make deploy-runner. The
statements in this document about the host rest on those files.
Listeners
| Port / path | Protocol | Owner | Default | Auth | Code |
|---|---|---|---|---|---|
| 22/tcp | SSH | gitbayd | on | public key; unknown keys only reach register when registration is open |
cmd/gitbayd/main.go, internal/sshd/sshd.go |
| 443/tcp | HTTPS | gitbayd | on | none for pages; session cookie; bearer token for the API | cmd/gitbayd/main.go |
| 80/tcp | HTTP | gitbayd | on with ACME | none; ACME HTTP-01 and redirect only | cmd/gitbayd/main.go |
| 9418/tcp | git:// | gitbayd | off | none; public repositories only | internal/gitd |
| 2222/tcp | SSH (operator) | host sshd | on | public key, no passwords, fail2ban | deploy/cloud-init.yaml |
<root>/hook.sock |
Unix socket | gitbayd | on | mode 0600; peer uid must be the daemon's (Linux); per-push token | internal/hookd/hookd.go |
With ssh.mode = system the host's sshd serves port 22 instead and
invokes gitbayd authorized-keys and gitbayd shell
(cmd/gitbayd/main.go).
HTTP server limits: ReadHeaderTimeout 10 s, IdleTimeout 2 min,
MaxHeaderBytes 64 KiB, no WriteTimeout so long git transfers and
live build logs can stream (cmd/gitbayd/main.go).
There is no metrics endpoint. /healthz reports the deployed commit and
a database check.
Processes and accounts
| Unit | User | Hardening (from the unit files) |
|---|---|---|
gitbayd.service |
gitbay |
CAP_NET_BIND_SERVICE only; NoNewPrivileges; ProtectSystem=strict with write access to /var/lib/gitbay and /var/backups/gitbay only; ProtectHome; PrivateTmp; PrivateDevices; kernel, clock and cgroup protections; RestrictNamespaces; MemoryDenyWriteExecute; RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX; SystemCallFilter=@system-service (deploy/cloud-init.yaml) |
gitbay-runner.service |
ci-runner |
MemoryMax=6G, CPUQuota=300%, Delegate=yes, KillMode=mixed, RestrictSUIDSGID=yes. NoNewPrivileges, ProtectKernelTunables and ProtectControlGroups are relaxed because rootless podman needs newuidmap, a proc mount and a writable delegated cgroup; the reasons are in deploy/gitbay-runner.override.conf |
| CI containers | subordinate uids of ci-runner |
rootless podman, --pull=never, operator-provisioned image; see 7. CI |
| backup, db-backup, gc, monitor timers | gitbay |
nightly full archive, hourly database snapshot, weekly git gc, hourly health heartbeat (deploy/cloud-init.yaml) |
Filesystem
| Path | Contents | Mode set by code / deploy |
|---|---|---|
/var/lib/gitbay (server.root) |
everything below | 0750 (cloud-init) |
<root>/gitbay.db |
SQLite database | 0640 (internal/store/store.go) |
<root>/repos/<owner>/<name>.git |
bare repositories | process umask |
<root>/lfs |
LFS objects, content-addressed | 0755 directories (internal/lfs/lfs.go) |
<root>/ssh/host_ed25519 |
SSH host key | 0600 in a 0700 directory (internal/sshd/sshd.go) |
<root>/acme |
ACME account key and certificates | autocert defaults |
<root>/hooks |
generated hook scripts | 0755 |
/etc/gitbay/config.toml |
configuration, including SMTP password | 0640 (cloud-init) |
/etc/gitbay/secret.key |
keys sealing secret columns | 0600, owner gitbay (deploy/install.sh) |
mail.inbound.password_file |
IMAP mailbox password | 0600 required; the daemon refuses to start otherwise |
/var/backups/gitbay |
backup archives | 0750 (cloud-init) |
Outbound connections from gitbayd
| Destination | Trigger | TLS | Guard |
|---|---|---|---|
| ACME directory | certificate issue and renewal | yes | host policy limits names to the site and claimed pages domains (main.go) |
| SMTP relay | queued mail | STARTTLS required for a non-local relay, or implicit TLS | mail.require_tls; Go's PlainAuth will not send credentials over plaintext to a non-local host (internal/mail/mail.go) |
| APNs | queued push | yes, HTTP/2 | provider token signed with the operator's .p8 key |
| IMAP server | mail.inbound.poll_interval |
implicit TLS or STARTTLS, certificate verified; no plaintext setting | operator-configured host only (internal/imapc) |
| DNS resolver | mail.inbound.require_dkim, a reply that passed the token and address checks |
no; the system resolver, no DNSSEC validation in gitbayd | name is <s>._domainkey.<d> from the signature; five-second timeout, fifteen-minute cache of at most 256 keys, first five signatures only (internal/mailin/dkim.go) |
| Webhook URLs | recorded events | yes when https; certificate verified | private, shared, loopback, link-local and multicast targets refused at save and again at connect time; no redirects (internal/webhook/webhook.go) |
| Mirror URLs | mirror schedule | per URL | address check at save and before each sync; git pinned to the checked addresses, no redirects (internal/mirror/mirror.go) |
| Package registries | dependency checks | yes | fixed hosts; only the package name varies (internal/deps/registry.go) |
Host firewall
deploy/cloud-init.yaml opens 22, 80, 443 and 2222 inbound with ufw.
Outbound traffic from the host is not restricted. CI builds are, by
two nftables tables on the runner's host: trusted builds keep the
internet, untrusted ones get TCP 80 and 443 and DNS, and neither
reaches private ranges or the host's loopback beyond DNS (the CI wiki page, #260).
Change path
- A signed commit merged to
mainthrough a merge request (direct pushes tomainare refused byrequire-mr). make deployrefuses a dirty tree (Makefilepreflight), builds with the commit stamped in, copies the binary over operator SSH, runsgitbayd check-config, restarts the unit (deploy/install.sh)./healthzreports the commit now serving.