cmd/gitbayd/system.go

dd06e80a071d451ebb7b083363e0580550481114
gitbay/cmd/gitbayd/system.go history · blame · raw

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}