cmd/gitbayd/system.go
106 lines · 3066 bytes
1package main
2
3import (
4 "fmt"
5 "os"
6
7 "github.com/spf13/cobra"
8 "golang.org/x/crypto/ssh"
9
10 "gitbay.org/gitbay/internal/config"
11 "gitbay.org/gitbay/internal/control"
12 "gitbay.org/gitbay/internal/protocol"
13 "gitbay.org/gitbay/internal/sshd"
14)
15
16// authorizedKeysCmd backs sshd's AuthorizedKeysCommand in system mode:
17//
18// AuthorizedKeysCommand /usr/bin/gitbayd --config /etc/gitbay/config.toml authorized-keys %t %k
19// AuthorizedKeysCommandUser git
20//
21// It prints a forced-command authorized_keys line for registered keys and
22// nothing for unknown ones — so unknown keys fail authentication inside
23// sshd, before any forge code runs. That is why system mode requires
24// registration = "closed".
25func authorizedKeysCmd() *cobra.Command {
26 return &cobra.Command{
27 Use: "authorized-keys <key-type> <key-base64>",
28 Hidden: true,
29 Args: cobra.ExactArgs(2),
30 RunE: func(cmd *cobra.Command, args []string) error {
31 cfg, err := config.Load(configPath)
32 if err != nil {
33 return err
34 }
35 st, err := openStore(cfg)
36 if err != nil {
37 return err
38 }
39 defer st.Close()
40
41 pub, _, _, _, err := ssh.ParseAuthorizedKey([]byte(args[0] + " " + args[1]))
42 if err != nil {
43 return nil // unparseable key: no output, auth fails
44 }
45 key, err := st.SSHKeyByFingerprint(ssh.FingerprintSHA256(pub))
46 if err != nil {
47 return nil // unknown key: no output, auth fails
48 }
49 self, err := os.Executable()
50 if err != nil {
51 return err
52 }
53 fmt.Printf("restrict,command=\"%s --config %s shell --key-id %d\" %s %s\n",
54 self, configPath, key.ID, args[0], args[1])
55 return nil
56 },
57 }
58}
59
60// shellCmd is the forced command sshd runs for an authenticated key. The
61// original client command arrives in SSH_ORIGINAL_COMMAND; dispatch is the
62// same code path as the embedded listener.
63func shellCmd() *cobra.Command {
64 var keyID int64
65 cmd := &cobra.Command{
66 Use: "shell",
67 Hidden: true,
68 Args: cobra.NoArgs,
69 RunE: func(cmd *cobra.Command, args []string) error {
70 cfg, err := config.Load(configPath)
71 if err != nil {
72 return err
73 }
74 st, err := openStore(cfg)
75 if err != nil {
76 return err
77 }
78 defer st.Close()
79
80 key, err := st.SSHKeyByID(keyID)
81 if err != nil {
82 fmt.Fprintln(os.Stderr, "key no longer registered")
83 os.Exit(protocol.ExitDenied)
84 }
85 user, err := st.UserByID(key.UserID)
86 if err != nil {
87 fmt.Fprintln(os.Stderr, "account no longer exists")
88 os.Exit(protocol.ExitDenied)
89 }
90 _ = st.TouchSSHKey(key.ID)
91
92 cmdline := os.Getenv("SSH_ORIGINAL_COMMAND")
93 if cmdline == "" {
94 fmt.Fprintf(os.Stderr, "gitbay control plane: interactive shells are not available.\nTry: ssh <host> help\n")
95 os.Exit(protocol.ExitUsage)
96 }
97 code := sshd.Exec(cfg, st, user, key.Scope, key.Fingerprint, control.ParseTerm(os.Getenv("GITBAY_TERM")), cmdline, os.Stdin, os.Stdout, os.Stderr, nil, nil)
98 st.Close()
99 os.Exit(code)
100 return nil
101 },
102 }
103 cmd.Flags().Int64Var(&keyID, "key-id", 0, "registered key id (set by authorized-keys)")
104 cmd.MarkFlagRequired("key-id")
105 return cmd
106}