aboutsummaryrefslogtreecommitdiff
path: root/tools/ssh-key-to-forge/main.go
diff options
context:
space:
mode:
authorFranck Cuny <franck@fcuny.net>2022-06-18 14:45:58 -0700
committerFranck Cuny <franck@fcuny.net>2022-06-18 14:50:46 -0700
commitb30dd0c0f9f3c55517434fdc7d37b34ef0c94664 (patch)
tree4a4cd5399cdea2e6918d6a574e70ede351d81752 /tools/ssh-key-to-forge/main.go
parentfeat(tools/music-organizer): add a CLI to organize my music (diff)
downloadinfra-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.go115
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
+ }
+ }
+}