diff --git a/modules/gitea/README.md b/modules/gitea/README.md index cb97c96f..fb7b319d 100644 --- a/modules/gitea/README.md +++ b/modules/gitea/README.md @@ -221,3 +221,15 @@ whenever the tools' account is set up, with the administrator's login (`GiteaAdm account is no repository's administrator, so no tool can. A rule found keeps the statuses it requires; a repository with none is given one that requires none. `judgeProtectionChange` refuses, for `main`, a change that enables push, drops the merge whitelist or widens it past that account. + +## The forge account for git over ssh (novox/hq ADR 0399, ADR 0400, issue 955) + +Gitea provides `forge-account`, and `cmd/forge-account-provider` answers its consumers (the `ssh-client` of each +machine). The provision has no credential: a public key is no secret. For a machine whose ssh-client has reported its +key in the state `keys` (read, `ssh-client.keys`), the provider makes the account `agent-` in the agents team +(`internal/forgeaccount`, the code `cmd/agent-token` shares), registers the key under the title `mesh:.`, +and serves the entry values `user`, `ssh_host` (the forge machine's mesh name, from the fact `forge-host`), `ssh_port`, +`known_host` (read from the forge's own sshd with `ssh-keyscan`), `ssh_base` and `https_base`. A machine that has +reported no key gets no entry, and its client says it waits. Keys titled `mesh:` that are no longer wanted are removed, +a key added by hand never is. Every consumer is made again every ten minutes and at once when a key changes. +`go test ./cmd/forge-account-provider ./internal/forgeaccount` runs it against a fake forge. diff --git a/modules/gitea/files/consumers.json b/modules/gitea/files/consumers.json new file mode 100644 index 00000000..790b51f9 --- /dev/null +++ b/modules/gitea/files/consumers.json @@ -0,0 +1 @@ +${consumers:forge-account} diff --git a/modules/gitea/module.json b/modules/gitea/module.json index a086f1d3..6277d97e 100644 --- a/modules/gitea/module.json +++ b/modules/gitea/module.json @@ -177,6 +177,12 @@ "default": "mesh-tools mesh-admin jschoubben" } }, + "facts": { + "forge-host": { + "path": "/etc/gitea/forge-host", + "template": "{{range .Machines}}{{if eq .Name $.Node}}{{.FQDN}}{{end}}{{end}}\n" + } + }, "resources": [ { "id": "mesh-state", @@ -313,6 +319,13 @@ "agent-read-teams" ] }, + { + "id": "consumers", + "type": "file", + "path": "${dir:state}/consumers.json", + "mode": "0600", + "template": "files/consumers.json" + }, { "id": "agent-teams", "type": "process", @@ -353,6 +366,10 @@ "issued-by": "software", "tool": "gitea_agent_token" } + }, + { + "name": "forge-account", + "scope": "mesh" } ], "build": { @@ -451,6 +468,25 @@ "loads": [ "agent-teams" ] + }, + { + "name": "forge-account-provider", + "kind": "bundle", + "language": "go", + "system": "arch", + "from": "cmd/forge-account-provider", + "binary": "forge-account-provider", + "loads": [ + "forge-account-provider" + ], + "env": { + "MESH_GITEA_URL": "http://127.0.0.1:${port:3000}", + "MESH_GITEA_ADMIN_USER": "mesh-admin", + "MESH_GITEA_ADMIN_PASSWORD_FILE": "${dir:state}/admin.secret", + "MESH_GITEA_ORIGINS_FILE": "${dir:mesh-state}/origins", + "MESH_GITEA_SSH_PORT": "222", + "MESH_FORGE_HOST_FILE": "/etc/gitea/forge-host" + } } ] }, @@ -460,5 +496,11 @@ "failregex": "^.*Failed authentication attempt for .* from (?::\\d+)?\\s*$\n ^.*Invalid user .* from port \\d+\\s*$\n ^.*User \\S+ from not allowed because .*$", "jail": "backend = systemd\njournalmatch = CONTAINER_NAME=gitea\nport = http,https,222\nmaxretry = 3\nfindtime = 1d\nbantime = 1d" } + ], + "state": [ + "bound" + ], + "reads": [ + "ssh-client.keys" ] }