Loading key/key.go +23 −111 Original line number Diff line number Diff line Loading @@ -22,8 +22,11 @@ import ( var keytemplate = `command="%s --user %s",restrict,pty %s` // Add adds an ssh key to the `authorized_keys` file. func Add(login, public string) error { // ErrDuplicateKey error when an existing key is added to the db. var ErrDuplicateKey = errors.New("key already exists") // addToFile adds an ssh key to the `authorized_keys` file. func addToFile(login, public string) error { // Parse and verify the key. public = strings.TrimSpace(public) theKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(public)) Loading Loading @@ -63,7 +66,7 @@ func Add(login, public string) error { return err } if bytes.Contains(keycontent, ssh.MarshalAuthorizedKey(theKey)) { return errors.New("key already exists") return ErrDuplicateKey } // Generate and write the key. Loading @@ -80,69 +83,8 @@ func Add(login, public string) error { return nil } // List returns a list of ssh keys for this user. func List(login string) ([]string, error) { keys := []string{} // Find the bulletin binary. bulletin, err := os.Executable() if err != nil { return keys, err } // File system management. sshdir := path.Join(xdg.Home, ".ssh") err = os.MkdirAll(sshdir, 0700) if err != nil { return keys, err } keyfile := path.Join(sshdir, "authorized_keys") // Open the authorized_keys file. f, err := os.OpenFile(keyfile, os.O_RDWR|os.O_CREATE, 0600) // #nosec G304 -- path is constructed from xdg.Home if err != nil { return keys, err } defer f.Close() // look for lines. scanner := bufio.NewScanner(f) for scanner.Scan() { keyline := bytes.TrimSpace(scanner.Bytes()) if len(keyline) == 0 { continue } public, _, options, _, err := ssh.ParseAuthorizedKey([]byte(keyline)) if err != nil { return keys, err } for i := range options { opts := strings.SplitN(options[i], "=", 2) if len(opts) != 2 || opts[0] != "command" { continue } cmd := strings.Split(strings.Trim(opts[1], "\" "), " ") if len(cmd) != 3 { return keys, fmt.Errorf("unexpected command in authorized keys file (%s)", opts[1]) } if cmd[0] != bulletin { return keys, fmt.Errorf("unexpected bulletin in authorized keys file (%s)", opts[1]) } if cmd[1] != "-u" { return keys, fmt.Errorf("unexpected flag in authorized keys file (%s)", opts[1]) } if cmd[2] == login { keys = append(keys, strings.Trim(string(ssh.MarshalAuthorizedKey(public)), "\n")) } break } } return keys, nil } // Delete removes the key. func Delete(public string) error { // deleteFromFile removes a key from the `authorized_keys` file. func deleteFromFile(public string) error { keys := []string{} // Parse and verify the key. Loading Loading @@ -201,46 +143,9 @@ func Delete(public string) error { return nil } // Fetch fetches keys and adds them. func Fetch(login, nickname, username string) string { sites := map[string]string{ "codeberg": "https://codeberg.org/%s.keys", "gitlab": "https://gitlab.com/%s.keys", "github": "https://github.com/%s.keys", } site := sites[strings.ToLower(nickname)] if site == "" { return fmt.Sprintln("ERROR: site nickname unknown.") } url := fmt.Sprintf(site, username) resp, err := http.Get(url) // #nosec G107 -- URL is constructed from a hardcoded allowlist of sites if err != nil { return fmt.Sprintf("ERROR: Failed to fetch ssh keys (%s).\n", err) } defer resp.Body.Close() scanner := bufio.NewScanner(resp.Body) keys := 0 for scanner.Scan() { keyline := string(bytes.TrimSpace(scanner.Bytes())) if err := Add(strings.ToUpper(login), keyline); err != nil { return fmt.Sprintf("ERROR: Failed to add key (%s).\n", err) } keys++ } switch keys { case 0: return fmt.Sprintln("No keys added.") case 1: return fmt.Sprintln("Key is added.") default: return fmt.Sprintf("%d keys added.\n", keys) } } // AddDB adds an SSH key to both the authorized_keys file and the database. func AddDB(q *storage.Queries, login, public string) error { // First add to authorized_keys file (existing behavior). if err := Add(login, public); err != nil { if err := addToFile(login, public); err != nil { return err } Loading Loading @@ -284,12 +189,10 @@ func ListDB(q *storage.Queries, login string) ([]storage.SshKey, error) { // DeleteDB removes an SSH key from both the authorized_keys file and the database. func DeleteDB(q *storage.Queries, fingerprint, public string) error { // Delete from authorized_keys file. if err := Delete(public); err != nil { if err := deleteFromFile(public); err != nil { return err } // Delete from database. ctx := storage.Context() return q.DeleteSSHKey(ctx, fingerprint) } Loading Loading @@ -317,7 +220,7 @@ func FetchDB(q *storage.Queries, login, nickname, username string) string { for scanner.Scan() { keyline := string(bytes.TrimSpace(scanner.Bytes())) if err := AddDB(q, strings.ToUpper(login), keyline); err != nil { if err.Error() == "key already exists" { if errors.Is(err, ErrDuplicateKey) { dups++ } else { return fmt.Sprintf("ERROR: Failed to add key (%s).\n", err) Loading @@ -326,12 +229,21 @@ func FetchDB(q *storage.Queries, login, nickname, username string) string { keys++ } } var dupsStr string switch dups { case 0: dupsStr = ".\n" case 1: dupsStr = " (1 duplicate skipped).\n" default: dupsStr = fmt.Sprintf(" (%d duplicates skipped).\n", dups) } switch keys { case 0: return fmt.Sprintln("No keys added.") return fmt.Sprintf("No keys added%s", dupsStr) case 1: return fmt.Sprintln("Key is added.") return fmt.Sprintf("Key is added%s", dupsStr) default: return fmt.Sprintf("%d new keys added (%d duplicates skipped).\n", keys, dups) return fmt.Sprintf("%d new keys added%s", keys, dupsStr) } } Loading
key/key.go +23 −111 Original line number Diff line number Diff line Loading @@ -22,8 +22,11 @@ import ( var keytemplate = `command="%s --user %s",restrict,pty %s` // Add adds an ssh key to the `authorized_keys` file. func Add(login, public string) error { // ErrDuplicateKey error when an existing key is added to the db. var ErrDuplicateKey = errors.New("key already exists") // addToFile adds an ssh key to the `authorized_keys` file. func addToFile(login, public string) error { // Parse and verify the key. public = strings.TrimSpace(public) theKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(public)) Loading Loading @@ -63,7 +66,7 @@ func Add(login, public string) error { return err } if bytes.Contains(keycontent, ssh.MarshalAuthorizedKey(theKey)) { return errors.New("key already exists") return ErrDuplicateKey } // Generate and write the key. Loading @@ -80,69 +83,8 @@ func Add(login, public string) error { return nil } // List returns a list of ssh keys for this user. func List(login string) ([]string, error) { keys := []string{} // Find the bulletin binary. bulletin, err := os.Executable() if err != nil { return keys, err } // File system management. sshdir := path.Join(xdg.Home, ".ssh") err = os.MkdirAll(sshdir, 0700) if err != nil { return keys, err } keyfile := path.Join(sshdir, "authorized_keys") // Open the authorized_keys file. f, err := os.OpenFile(keyfile, os.O_RDWR|os.O_CREATE, 0600) // #nosec G304 -- path is constructed from xdg.Home if err != nil { return keys, err } defer f.Close() // look for lines. scanner := bufio.NewScanner(f) for scanner.Scan() { keyline := bytes.TrimSpace(scanner.Bytes()) if len(keyline) == 0 { continue } public, _, options, _, err := ssh.ParseAuthorizedKey([]byte(keyline)) if err != nil { return keys, err } for i := range options { opts := strings.SplitN(options[i], "=", 2) if len(opts) != 2 || opts[0] != "command" { continue } cmd := strings.Split(strings.Trim(opts[1], "\" "), " ") if len(cmd) != 3 { return keys, fmt.Errorf("unexpected command in authorized keys file (%s)", opts[1]) } if cmd[0] != bulletin { return keys, fmt.Errorf("unexpected bulletin in authorized keys file (%s)", opts[1]) } if cmd[1] != "-u" { return keys, fmt.Errorf("unexpected flag in authorized keys file (%s)", opts[1]) } if cmd[2] == login { keys = append(keys, strings.Trim(string(ssh.MarshalAuthorizedKey(public)), "\n")) } break } } return keys, nil } // Delete removes the key. func Delete(public string) error { // deleteFromFile removes a key from the `authorized_keys` file. func deleteFromFile(public string) error { keys := []string{} // Parse and verify the key. Loading Loading @@ -201,46 +143,9 @@ func Delete(public string) error { return nil } // Fetch fetches keys and adds them. func Fetch(login, nickname, username string) string { sites := map[string]string{ "codeberg": "https://codeberg.org/%s.keys", "gitlab": "https://gitlab.com/%s.keys", "github": "https://github.com/%s.keys", } site := sites[strings.ToLower(nickname)] if site == "" { return fmt.Sprintln("ERROR: site nickname unknown.") } url := fmt.Sprintf(site, username) resp, err := http.Get(url) // #nosec G107 -- URL is constructed from a hardcoded allowlist of sites if err != nil { return fmt.Sprintf("ERROR: Failed to fetch ssh keys (%s).\n", err) } defer resp.Body.Close() scanner := bufio.NewScanner(resp.Body) keys := 0 for scanner.Scan() { keyline := string(bytes.TrimSpace(scanner.Bytes())) if err := Add(strings.ToUpper(login), keyline); err != nil { return fmt.Sprintf("ERROR: Failed to add key (%s).\n", err) } keys++ } switch keys { case 0: return fmt.Sprintln("No keys added.") case 1: return fmt.Sprintln("Key is added.") default: return fmt.Sprintf("%d keys added.\n", keys) } } // AddDB adds an SSH key to both the authorized_keys file and the database. func AddDB(q *storage.Queries, login, public string) error { // First add to authorized_keys file (existing behavior). if err := Add(login, public); err != nil { if err := addToFile(login, public); err != nil { return err } Loading Loading @@ -284,12 +189,10 @@ func ListDB(q *storage.Queries, login string) ([]storage.SshKey, error) { // DeleteDB removes an SSH key from both the authorized_keys file and the database. func DeleteDB(q *storage.Queries, fingerprint, public string) error { // Delete from authorized_keys file. if err := Delete(public); err != nil { if err := deleteFromFile(public); err != nil { return err } // Delete from database. ctx := storage.Context() return q.DeleteSSHKey(ctx, fingerprint) } Loading Loading @@ -317,7 +220,7 @@ func FetchDB(q *storage.Queries, login, nickname, username string) string { for scanner.Scan() { keyline := string(bytes.TrimSpace(scanner.Bytes())) if err := AddDB(q, strings.ToUpper(login), keyline); err != nil { if err.Error() == "key already exists" { if errors.Is(err, ErrDuplicateKey) { dups++ } else { return fmt.Sprintf("ERROR: Failed to add key (%s).\n", err) Loading @@ -326,12 +229,21 @@ func FetchDB(q *storage.Queries, login, nickname, username string) string { keys++ } } var dupsStr string switch dups { case 0: dupsStr = ".\n" case 1: dupsStr = " (1 duplicate skipped).\n" default: dupsStr = fmt.Sprintf(" (%d duplicates skipped).\n", dups) } switch keys { case 0: return fmt.Sprintln("No keys added.") return fmt.Sprintf("No keys added%s", dupsStr) case 1: return fmt.Sprintln("Key is added.") return fmt.Sprintf("Key is added%s", dupsStr) default: return fmt.Sprintf("%d new keys added (%d duplicates skipped).\n", keys, dups) return fmt.Sprintf("%d new keys added%s", keys, dupsStr) } }