Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Verify the runner's host and re-check the job it is given

f5456470d22fb7ca06197a8856ed0da7fc56fdd2 · Keenan Nemetz · 12d ago · parent edf9db6e

Verify the runner's host and re-check the job it is given

A runner executes repository code, so the instance it talks to decides what
runs on it. The client built ssh command lines with no host-key policy: a
man in the middle, or a loose known_hosts, could answer in the instance's
place and feed a runner any job it liked.

Host keys are now verified against known_hosts by default, for the
runner's control channel, for git's own clone/fetch/push and for the LFS
transfer. `--known-hosts` names a file instead of the default,
VOLE_KNOWN_HOSTS does the same for every command, and
`--insecure-host-key` / VOLE_INSECURE_HOST_KEY is the explicit override
for tests and throwaway instances, with a warning when it is used. The
options are placed before any configured ones because ssh takes the first
value for an option, so GIT_SSH_COMMAND or VOLE_SSH_COMMAND cannot
quietly turn the check off. The test package opts out once, in TestMain,
rather than every fixture learning to trust everything.

The runner also re-checks the job it leases before acting on it. The
server validated the workflow at push time, but the runner is the party
that turns the payload into paths, container arguments and scripts. Run
id, repository, attempt, commit, ref, branch, tag, workflow and job names,
image (or image-file plus its pinned digest), steps, artifact patterns,
pages path and environment are all validated; a malformed job stops the
runner with the reason instead of being built from.

The artifact and pages path rules move into ci/validate.go, so the
push-time check and the runner use one implementation rather than two
that can drift. Joining them also closes the gap the runner-side check
was for: `..`, absolute paths, `**`, globs where a directory belongs, and
control characters are refused identically on both sides.

Tests: strict-by-default and known_hosts options, the environment losing
to the explicit policy, the insecure override, and every job field
refused when it climbs out, is the wrong shape, or is missing.

docs/runner.md +37

@@ -30,6 +30,43 @@ vole runner --remote ssh://git@host:2222 --repos demo --repos docs
30 30 - `--once` leases one job and exits, which is useful for a runner that is
31 31 scheduled per job.
32 32
33 ## Host key verification
34
35 A runner executes repository code, so the instance it trusts is a security
36 boundary: a host that can answer in its place can send it any job. The SSH host
37 key is verified against your known_hosts, and an unknown or changed host is
38 refused. Populate the file once, out of band:
39
40 ```sh
41 ssh-keyscan -p 2222 git@host >> ~/.ssh/known_hosts
42 vole runner --remote ssh://git@host:2222 --known-hosts ~/.ssh/vole_known_hosts
43 ```
44
45 - `--known-hosts <path>` checks against that file instead of the default
46 `~/.ssh/known_hosts`.
47 - `--insecure-host-key` turns verification off and accepts any host that answers.
48 It exists for tests and throwaway instances; a real runner should not use it.
49 - `VOLE_KNOWN_HOSTS` and `VOLE_INSECURE_HOST_KEY` do the same for every command,
50 including `vole attach`, `vole admin --remote`, and the CLI verbs.
51
52 The policy covers git's own ssh calls as well as the runner's control channel, so
53 a clone, a fetch, a record push and the job stream all verify the same host. An
54 environment variable cannot loosen it: an explicit
55 `GIT_SSH_COMMAND=... StrictHostKeyChecking=no` is overridden by the runner's own
56 options, which come first.
57
58 ## What a job may name
59
60 The server validates a workflow when it is pushed, and the runner validates the
61 job again before it acts on it, because the runner is the party that turns the
62 payload into paths, container arguments and scripts. A job whose repository,
63 commit, ref, branch, tag, pages path, artifact pattern, step or environment does
64 not have the shape the instance documents is refused, and the runner stops rather
65 than running it. In particular a path that climbs out of the checkout (`..`), an
66 absolute path, a `:` or NUL in a value used to build a container argument, and a
67 commit id that is not a commit id are all refused at the runner, not only at the
68 server.
69
33 70 ## Images and image builds
34 71
35 72 Every job names its image in the workflow. A registry reference is used as