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.
Let remote admin create a bot account
338a19dd7e42c2bf42c206318197f4b53c3e9fd7 · Keenan Nemetz · 6d ago · parent 70291860
server/admin_test.go +65
| @@ -210,6 +210,52 @@ func TestRemoteAdminManagesAccountsAndRepos(t *testing.T) { | |||
| 210 | 210 | } | |
| 211 | 211 | } | |
| 212 | 212 | ||
| 213 | // A runner's account is a bot. The admin API creates one the same way the local | ||
| 214 | // CLI does: --bot with a public key. The keyless form is refused because the | ||
| 215 | // enrollment link a keyless account would get is a secret a bot cannot use. | ||
| 216 | func TestRemoteAdminCreatesBots(t *testing.T) { | ||
| 217 | t.Parallel() | ||
| 218 | f := newFixture(t) | ||
| 219 | if _, err := f.srv.Identity.SetAdmin("keenan", true, "test"); err != nil { | ||
| 220 | t.Fatal(err) | ||
| 221 | } | ||
| 222 | |||
| 223 | pub, _ := keypair(t, filepath.Join(f.cfg.DataDir, "id_ci_runner")) | ||
| 224 | out, errOut, err := f.adminSSH(f.keyPath, []byte(pub), "account", "create", "ci-runner", "--bot", "--role", "read") | ||
| 225 | if err != nil { | ||
| 226 | t.Fatalf("remote bot create: %v\nstdout: %s\nstderr: %s", err, out, errOut) | ||
| 227 | } | ||
| 228 | a, err := f.srv.Identity.Find("ci-runner") | ||
| 229 | if err != nil { | ||
| 230 | t.Fatal(err) | ||
| 231 | } | ||
| 232 | if !a.Bot() || len(a.Keys) != 1 || a.Role != access.Read { | ||
| 233 | t.Fatalf("remote bot = %+v", a) | ||
| 234 | } | ||
| 235 | |||
| 236 | // An enrollment link is the keyless path, and a bot has no browser to use it. | ||
| 237 | out, errOut, err = f.adminSSH(f.keyPath, nil, "account", "create", "keyless-bot", "--bot", "--no-key") | ||
| 238 | if err == nil { | ||
| 239 | t.Fatalf("a bot was created with an enrollment link: %s%s", out, errOut) | ||
| 240 | } | ||
| 241 | if !strings.Contains(out+errOut, "needs a key") { | ||
| 242 | t.Fatalf("refusal did not explain the key requirement:\nstdout: %s\nstderr: %s", out, errOut) | ||
| 243 | } | ||
| 244 | if a, err := f.srv.Identity.Find("keyless-bot"); err == nil && a != nil { | ||
| 245 | t.Fatal("the refused bot was created anyway") | ||
| 246 | } | ||
| 247 | |||
| 248 | // A bot cannot hold instance admin, so the create fails rather than making a | ||
| 249 | // machine that reaches every repository. | ||
| 250 | out, errOut, err = f.adminSSH(f.keyPath, []byte(pub), "account", "create", "admin-bot", "--bot", "--role", "admin") | ||
| 251 | if err == nil { | ||
| 252 | t.Fatalf("a bot was created as an instance admin: %s%s", out, errOut) | ||
| 253 | } | ||
| 254 | if a, err := f.srv.Identity.Find("admin-bot"); err == nil && a != nil { | ||
| 255 | t.Fatal("the refused admin bot was created anyway") | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 213 | 259 | func TestAdminCLIForwardsToRemote(t *testing.T) { | |
| 214 | 260 | t.Parallel() | |
| 215 | 261 | f := newFixture(t) | |
| @@ -258,6 +304,25 @@ func TestAdminCLIForwardsToRemote(t *testing.T) { | |||
| 258 | 304 | if a, err := f.srv.Identity.Find("alice"); err != nil || len(a.Keys) != 2 { | |
| 259 | 305 | t.Fatalf("alice's keys did not land: %+v, %v", a, err) | |
| 260 | 306 | } | |
| 307 | |||
| 308 | // The client reads --key locally and still forwards --bot, so a runner on | ||
| 309 | // another machine can be given an account without a local instance. | ||
| 310 | botPub, _ := keypair(t, filepath.Join(f.cfg.DataDir, "id_cli_ci_runner")) | ||
| 311 | botPubPath := filepath.Join(f.cfg.DataDir, "id_cli_ci_runner.pub") | ||
| 312 | if err := os.WriteFile(botPubPath, []byte(botPub), 0o600); err != nil { | ||
| 313 | t.Fatal(err) | ||
| 314 | } | ||
| 315 | cmd = exec.Command(binPath, "admin", "--remote", "ssh://git@"+f.sshAddr, "account", "create", "ci-runner", "--bot", "--key", botPubPath) | ||
| 316 | cmd.Env = append(cmd.Environ(), | ||
| 317 | "VOLE_SSH_COMMAND=ssh -i "+f.keyPath+" -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR", | ||
| 318 | "GIT_CONFIG_GLOBAL=/dev/null", "GIT_CONFIG_SYSTEM=/dev/null", | ||
| 319 | ) | ||
| 320 | if err := cmd.Run(); err != nil { | ||
| 321 | t.Fatalf("remote bot create via the CLI: %v", err) | ||
| 322 | } | ||
| 323 | if a, err := f.srv.Identity.Find("ci-runner"); err != nil || !a.Bot() || len(a.Keys) != 1 { | ||
| 324 | t.Fatalf("the remotely created bot = %+v, %v", a, err) | ||
| 325 | } | ||
| 261 | 326 | } | |
| 262 | 327 | ||
| 263 | 328 | // An administrative change that cannot be recorded is not made. The log is the | |