package ssh import ( "fmt" "os/exec" "slices" "strings" "github.com/spf13/cobra" ) // script is what runs on the host. It reads the key line from its own // standard input, so the line never appears on a command line, where // anyone else on the host could read it out of the process list. It // contains no single quote, so the whole of it travels through ssh // inside one pair of them. The umask keeps anything it makes to the // owner from the start; the modes are then set outright, whatever the // umask on the host turns out to be. A file whose last line has no // newline at its end gets one before the key line goes on, so that the // two do not run into each other. const script = ` set -e umask 077 directory="$HOME/.ssh" file="$directory/authorized_keys" if [ ! -d "$directory" ]; then mkdir -p "$directory" chmod 700 "$directory" fi if [ ! -f "$file" ]; then : > "$file" chmod 600 "$file" fi IFS= read -r line if grep -q -x -F -e "$line" "$file"; then echo "already present" else if [ -s "$file" ] && [ -n "$(tail -c 1 "$file")" ]; then printf "\n" >> "$file" fi printf "%s\n" "$line" >> "$file" echo "added" fi ` // install returns the command that adds the public key to a host. func install() *cobra.Command { cmd := &cobra.Command{ Use: "install <[user@]host> [-- ssh options...]", Short: "add the public key to a host's authorized_keys", Long: "Runs the system ssh to the host, which makes ~/.ssh and " + "~/.ssh/authorized_keys there if they are missing and adds " + "the public key unless the same line is already in the " + "file. Anything after -- is given to ssh unchanged.", Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { key, comment, err := derived(cmd) if err != nil { return err } line, err := key.Line(comment) if err != nil { return err } return send(cmd, args[0], args[1:], line) }, } addComment(cmd) return cmd } // send runs ssh to the host with the user's options, gives it the // script to run there, and writes the key line to its standard input. // What the host says, added or already present, is passed straight on. func send(cmd *cobra.Command, host string, options []string, line string) error { argv := slices.Concat(options, []string{ host, "/bin/sh -c '" + script + "'", }) //nolint:gosec // the options are the user's own, meant for ssh command := exec.CommandContext(cmd.Context(), "ssh", argv...) command.Stdin = strings.NewReader(line + "\n") command.Stdout = cmd.OutOrStdout() command.Stderr = cmd.ErrOrStderr() err := command.Run() if err != nil { return fmt.Errorf("running ssh: %w", err) } return nil }