diff --git a/README.md b/README.md index 0eb5d22..a581f27 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,10 @@ conversion tool to convert an existing ssh key (we only support RSA keys right n ``` $ nix-shell -p ssh-to-pgp -$ ssh-to-pgp -privkey $HOME/.ssh/id_rsa | gpg --import --quiet +$ ssh-to-pgp -private-key -i $HOME/.ssh/id_rsa | gpg --import --quiet +2504791468b153b8a3963cc97ba53d1919c5dfd4 +# This exports the public key +$ ssh-to-pgp -i $HOME/.ssh/id_rsa -o $USER.asc 2504791468b153b8a3963cc97ba53d1919c5dfd4 ``` @@ -48,7 +51,7 @@ then your ssh key is encrypted with your password and you need to create a encry ``` $ cp $HOME/.ssh/id_rsa /tmp/id_rsa $ ssh-keygen -p -N "" -f /tmp/id_rsa -$ ssh-to-pgp -privkey /tmp/id_rsa | gpg --import --quiet +$ ssh-to-pgp -private-key -i /tmp/id_rsa | gpg --import --quiet ``` The hex string printed here is your GPG fingerprint that can be exported to `SOPS_PGP_FP`. @@ -71,23 +74,20 @@ uid [ unknown] root The fingerprint here is `9F89 C5F6 9A10 281A 8350 14B0 9C3D C61F 7520 87EF`, you need to remove the space in-between manually. -### 3. Get a GPG key for your machine +### 3. Get a PGP Public key for your machine The easiest way to add new hosts is using ssh host keys (requires openssh to be enabled). Since sops does not natively supports ssh keys yet, nix-sops supports a conversion tool to store them as gpg keys. ``` -$ nix-shell -p ssh-to-gpg -# One can use ssh-keyscan over the network -$ ssh-keyscan -t rsa server01 | ssh-to-pgp -pubkey - > server01.asc -# server01:22 SSH-2.0-OpenSSH_8.2 -0fd60c8c3b664aceb1796ce02b318df330331003 -# via ssh command: -$ ssh server01 "cat /etc/ssh/ssh_host_rsa_key.pub" | ssh-to-gpg -pubkey - > hosts/server01.asc +$ nix-shell -p ssh-to-pgp +$ ssh root@server01 "cat /etc/ssh/ssh_host_rsa_key" | ssh-to-pgp -o server01.asc +# or with sudo +$ ssh youruser@server01 "sudo cat /etc/ssh/ssh_host_rsa_key" | ssh-to-pgp -o server01.asc 0fd60c8c3b664aceb1796ce02b318df330331003 # Or just read them locally (or in a ssh session) -$ ssh-to-pgp -pubkey /etc/ssh/ssh_host_rsa_key.pub > server01.asc +$ ssh-to-pgp -i /etc/ssh/ssh_host_rsa_key -o server01.asc 0fd60c8c3b664aceb1796ce02b318df330331003 ``` diff --git a/pkgs/ssh-to-pgp/main.go b/pkgs/ssh-to-pgp/main.go index 5562781..4cb269c 100644 --- a/pkgs/ssh-to-pgp/main.go +++ b/pkgs/ssh-to-pgp/main.go @@ -14,28 +14,19 @@ import ( ) type options struct { - publicKey, privateKey, format, out string + format, out, in string + privateKey bool } func parseFlags(args []string) options { var opts options f := flag.NewFlagSet(args[0], flag.ExitOnError) - f.StringVar(&opts.publicKey, "pubkey", "", "Path to public key. Reads from standard input if equal to '-'") - f.StringVar(&opts.privateKey, "privkey", "", "Path to private key. Reads from standard input if equal to '-'") + f.BoolVar(&opts.privateKey, "private-key", false, "Export private key instead of public key") f.StringVar(&opts.format, "format", "armor", "GPG format encoding (binary|armor)") + f.StringVar(&opts.in, "i", "-", "Input path. Reads by default from standard output") f.StringVar(&opts.out, "o", "-", "Output path. Prints by default to standard output") f.Parse(args[1:]) - if opts.publicKey != "" && opts.privateKey != "" { - fmt.Fprintln(os.Stderr, "-pubkey and -privkey are mutual exclusive") - os.Exit(1) - } - - if opts.publicKey == "" && opts.privateKey == "" { - fmt.Fprintln(os.Stderr, "Either -pubkey and -privkey must be specified") - os.Exit(1) - } - return opts } @@ -43,19 +34,15 @@ func convertKeys(args []string) error { opts := parseFlags(args) var err error var sshKey []byte - keyPath := opts.privateKey - if opts.publicKey != "" { - keyPath = opts.publicKey - } - if keyPath == "-" { + if opts.in == "-" { sshKey, _ = ioutil.ReadAll(os.Stdin) if err != nil { return fmt.Errorf("error reading stdin: %s", err) } } else { - sshKey, err = ioutil.ReadFile(keyPath) + sshKey, err = ioutil.ReadFile(opts.in) if err != nil { - return fmt.Errorf("error reading %s: %s", opts.privateKey, err) + return fmt.Errorf("error reading %s: %s", opts.in, err) } } @@ -69,9 +56,9 @@ func convertKeys(args []string) error { } if opts.format == "armor" { - keyType := openpgp.PrivateKeyType - if opts.publicKey != "" { - keyType = openpgp.PublicKeyType + keyType := openpgp.PublicKeyType + if opts.privateKey { + keyType = openpgp.PrivateKeyType } writer, err = armor.Encode(writer, keyType, make(map[string]string)) if err != nil { @@ -79,28 +66,21 @@ func convertKeys(args []string) error { } } - var fingerprint [20]byte + gpgKey, err := sshkeys.SSHPrivateKeyToPGP(sshKey) + if err != nil { + return err + } - if opts.publicKey != "" { - gpgKey, err := sshkeys.SSHPublicKeyToPGP(sshKey) - if err != nil { - return err - } - err = gpgKey.Serialize(writer) - fingerprint = gpgKey.Fingerprint - } else { - gpgKey, err := sshkeys.SSHPrivateKeyToPGP(sshKey) - if err != nil { - return err - } + if opts.privateKey { err = gpgKey.SerializePrivate(writer, nil) - fingerprint = gpgKey.PrimaryKey.Fingerprint + } else { + err = gpgKey.Serialize(writer) } if err == nil { if opts.format == "armor" { writer.Close() } - fmt.Fprintf(os.Stderr, "%s\n", hex.EncodeToString(fingerprint[:])) + fmt.Fprintf(os.Stderr, "%s\n", hex.EncodeToString(gpgKey.PrimaryKey.Fingerprint[:])) } return err } diff --git a/pkgs/ssh-to-pgp/main_test.go b/pkgs/ssh-to-pgp/main_test.go index 14e7296..bc39c78 100644 --- a/pkgs/ssh-to-pgp/main_test.go +++ b/pkgs/ssh-to-pgp/main_test.go @@ -28,13 +28,12 @@ func TestCli(t *testing.T) { defer os.RemoveAll(tempdir) out := path.Join(tempdir, "out") - pubKey := path.Join(assets, "id_rsa.pub") privKey := path.Join(assets, "id_rsa") cmds := [][]string{ - {"ssh-to-pgp", "-pubkey", pubKey, "-o", out}, - {"ssh-to-pgp", "-format=armor", "-pubkey", pubKey, "-o", out}, - {"ssh-to-pgp", "-privkey", privKey, "-o", out}, - {"ssh-to-pgp", "-format=armor", "-privkey", privKey, "-o", out}, + {"ssh-to-pgp", "-i", privKey, "-o", out}, + {"ssh-to-pgp", "-format=binary", "-i", privKey, "-o", out}, + {"ssh-to-pgp", "-private-key", "-i", privKey, "-o", out}, + {"ssh-to-pgp", "-format=binary", "-private-key", "-i", privKey, "-o", out}, } for _, cmd := range cmds { err = convertKeys(cmd) diff --git a/pkgs/ssh-to-pgp/test-assets/id_rsa.pub b/pkgs/ssh-to-pgp/test-assets/id_rsa.pub deleted file mode 100644 index 330534f..0000000 --- a/pkgs/ssh-to-pgp/test-assets/id_rsa.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDSeS6SEputIOi2mQhLeIEJnMAink+KcUv38HnaLak3nnLmUsYJnXYB5KZGxaxVtIjr59J8TndmwniZ+wc0rql6Dkif9CsTXgAjxrPiknZNQ7JQbgWUr0pk4jx/K3zLD6i/XAS8QWySNJmY5aJWySbF/K687kUMJ5ql0BX4Tt0RiWL4pIwzZZlLzH4rRySy4z1kbiuOZf8htVRtlGoDGqGViJRpuybSKrmXbevRI7aWjiml2BVTMktPekAPx+MA3t/8EM/uJxtWp7g3BsneHQdKIjR0WEKAITTmuDLEEtIXXEUbgBW0WjbD62nRft/A6/iyWykPJmkLA4WnSLS03caeUxCKoEthZ1xfBPCRNw7xbysQF8CHJz8cAMjZGgBGlOin8EKDhmlma6FZ94cAB5Tr4G3R0h4ky77bPk2/6vvZtyU/AFnDP2HfGaRCDNF+Q7+fR9YmKwcW/vCa2ItIEXgMmBjS+yl0p+4fVaY6Q7bCTbrd6znb6gTGo7nD9Kj/CGU= joerg@turingmachine diff --git a/pkgs/sshkeys/convert.go b/pkgs/sshkeys/convert.go index 723b265..cccb686 100644 --- a/pkgs/sshkeys/convert.go +++ b/pkgs/sshkeys/convert.go @@ -33,14 +33,6 @@ func parsePublicKey(publicKey []byte) (*rsa.PublicKey, error) { return rsaKey, nil } -func SSHPublicKeyToPGP(sshPublicKey []byte) (*packet.PublicKey, error) { - rsaKey, err := parsePublicKey(sshPublicKey) - if err != nil { - return nil, err - } - return packet.NewRSAPublicKey(time.Unix(0, 0), rsaKey), nil -} - func parsePrivateKey(sshPrivateKey []byte) (*rsa.PrivateKey, error) { privateKey, err := ssh.ParseRawPrivateKey(sshPrivateKey) if err != nil { @@ -70,7 +62,7 @@ func SSHPrivateKeyToPGP(sshPrivateKey []byte) (*openpgp.Entity, error) { PrivateKey: packet.NewRSAPrivateKey(timeNull, key), Identities: make(map[string]*openpgp.Identity), } - uid := packet.NewUserId("root", "", "root@localhost") + uid := packet.NewUserId("root", "Imported from SSH", "root@localhost") isPrimaryID := true gpgKey.Identities[uid.Id] = &openpgp.Identity{ Name: uid.Id, @@ -89,6 +81,10 @@ func SSHPrivateKeyToPGP(sshPrivateKey []byte) (*openpgp.Entity, error) { IssuerKeyId: &gpgKey.PrimaryKey.KeyId, }, } + err = gpgKey.Identities[uid.Id].SelfSignature.SignUserId(uid.Id, gpgKey.PrimaryKey, gpgKey.PrivateKey, nil) + if err != nil { + return nil, err + } return gpgKey, nil }