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.

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