diff options
| author | Franck Cuny <franck@fcuny.net> | 2022-06-18 14:45:58 -0700 |
|---|---|---|
| committer | Franck Cuny <franck@fcuny.net> | 2022-06-18 14:50:46 -0700 |
| commit | b30dd0c0f9f3c55517434fdc7d37b34ef0c94664 (patch) | |
| tree | 4a4cd5399cdea2e6918d6a574e70ede351d81752 /tools/ssh-key-to-forge/main.go | |
| parent | feat(tools/music-organizer): add a CLI to organize my music (diff) | |
| download | infra-b30dd0c0f9f3c55517434fdc7d37b34ef0c94664.tar.gz | |
feat(tools/ssh-key-to-forge): import a SSH key to a forge
This can be useful if I need to import a SSH key to one of the
forge (only GitHub is supported for now).
Change-Id: Ieb5143a670cd75f1fbe51c0f3ae763dd1d667bef
Reviewed-on: https://cl.fcuny.net/c/world/+/449
Tested-by: CI
Reviewed-by: Franck Cuny <franck@fcuny.net>
Diffstat (limited to '')
| -rw-r--r-- | tools/ssh-key-to-forge/main.go | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/tools/ssh-key-to-forge/main.go b/tools/ssh-key-to-forge/main.go new file mode 100644 index 0000000..efea398 --- /dev/null +++ b/tools/ssh-key-to-forge/main.go @@ -0,0 +1,115 @@ +package main + +import ( + "context" + "encoding/base64" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + + "github.com/google/go-github/github" + "github.com/tcnksm/go-gitconfig" + "golang.org/x/crypto/ssh" + "golang.org/x/oauth2" +) + +var ( + defaultPublicKeyPath = filepath.Join(os.Getenv("HOME"), ".ssh", "id_rsa.pub") +) + +func main() { + sshkey := flag.String("ssh-key", defaultPublicKeyPath, "Path to the ssh public key to upload") + + flag.Parse() + + keyContent, keyTitle, err := readPublicKey(sshkey) + if err != nil { + log.Fatal(err) + } + + keyToGitHub(keyContent, keyTitle) +} + +// readPublicKey reads the public key, ensure it's in the proper +// format to prevent sending incorrect data, and returns the content +// of the key (the whole content) and the last field of the key that +// will be used as the title. +func readPublicKey(sshkey *string) (string, string, error) { + // first, let's ensure it's a public key, so we don't upload + // something incorrect. + data, err := ioutil.ReadFile(*sshkey) + if err != nil { + return "", "", fmt.Errorf("failed to open %s: %v", *sshkey, err) + } + + // we only want the content of the key to be parsed, not all the fields + fields := strings.Fields(string(data)) + if len(fields) < 2 { + return "", "", fmt.Errorf("not enough fields in public key %s (%d)", *sshkey, len(fields)) + } + + raw, err := base64.StdEncoding.DecodeString(fields[1]) + if err != nil { + return "", "", fmt.Errorf("failed to read the second field in the public key %s: %v", *sshkey, err) + } + _, err = ssh.ParsePublicKey(raw) + if err != nil { + return "", "", fmt.Errorf("%s is not a valid public key: %v", *sshkey, err) + } + + return strings.TrimSuffix(string(data), "\n"), fields[2], nil +} + +// If the key does not exists already on GitHub, upload it, using the +// signature (last part of the key) as the title. +func keyToGitHub(keyContent, keyTitle string) { + user, err := gitconfig.Global("github.user") + if err != nil { + fmt.Fprintf(os.Stderr, "github: failed to get the username: %v", err) + return + } + + token, err := gitconfig.Global("github.tokensshtoforge") + if err != nil { + fmt.Fprintf(os.Stderr, "github: failed to get the token: %v", err) + return + } + + ctx := context.Background() + ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}) + tc := oauth2.NewClient(ctx, ts) + + client := github.NewClient(tc) + + keys, _, err := client.Users.ListKeys(ctx, user, nil) + if err != nil { + fmt.Fprintf(os.Stderr, "github: failed to get the list of existing keys: %v", err) + return + } + + skip := false + for _, k := range keys { + ghKey := *k.Key + " " + keyTitle + c := strings.Compare(ghKey, keyContent) + if c == 0 { + skip = true + break + } + } + + if !skip { + key := github.Key{ + Key: &keyContent, + Title: &keyTitle, + } + _, _, err := client.Users.CreateKey(ctx, &key) + if err != nil { + fmt.Fprintf(os.Stderr, "github: failed to upload the ssh public key: %+v", err) + return + } + } +} |
