From f540b74ceda9c11b940e6e5eca8a311a37065239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Mon, 1 Feb 2021 12:12:20 +0100 Subject: [PATCH] remove ssh-to-pgp from sops-nix --- .github/workflows/test.yml | 1 + README.md | 46 +++++---- default.nix | 11 +-- flake.lock | 6 +- flake.nix | 4 +- pkgs/sops-install-secrets/main.go | 14 +-- .../sshkeys/convert.go | 0 pkgs/ssh-to-pgp/default.nix | 26 ----- pkgs/ssh-to-pgp/main.go | 96 ------------------- pkgs/ssh-to-pgp/main_test.go | 62 ------------ pkgs/ssh-to-pgp/test-assets/id_rsa | 38 -------- 11 files changed, 40 insertions(+), 264 deletions(-) rename pkgs/{ => sops-install-secrets}/sshkeys/convert.go (100%) delete mode 100644 pkgs/ssh-to-pgp/default.nix delete mode 100644 pkgs/ssh-to-pgp/main.go delete mode 100644 pkgs/ssh-to-pgp/main_test.go delete mode 100644 pkgs/ssh-to-pgp/test-assets/id_rsa diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7715ca8..29b3208 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,6 +28,7 @@ jobs: if: matrix.os == 'ubuntu-latest' - name: Run lint run: nix-build --no-out-link default.nix -A lint + if: matrix.os == 'ubuntu-latest' - name: Build nix packages run: nix-build --no-out-link release.nix - name: Run unit tests diff --git a/README.md b/README.md index 4e806c2..b9e7ffa 100644 --- a/README.md +++ b/README.md @@ -122,39 +122,38 @@ If you use experimental nix flakes support: First generate yourself [a GPG key](https://docs.github.com/en/github/authenticating-to-github/generating-a-new-gpg-key) or use nix-sops conversion tool to convert an existing ssh key (we only support RSA keys right now): -``` -$ nix run -f https://github.com/Mic92/sops-nix/archive/master.tar.gz ssh-to-pgp -$ ssh-to-pgp -private-key -i $HOME/.ssh/id_rsa | gpg --import --quiet +```console +$ nix-shell -p gnupg -p ssh-to-pgp --run "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 +$ nix-shell -p ssh-to-pgp --run "ssh-to-pgp -i $HOME/.ssh/id_rsa -o $USER.asc" 2504791468b153b8a3963cc97ba53d1919c5dfd4 ``` If you get: -``` +```console ssh-to-pgp: failed to parse private ssh key: ssh: this private key is passphrase protected ``` then your ssh key is encrypted with your password and you need to create an unencrypted copy temporarily: -``` +```console $ cp $HOME/.ssh/id_rsa /tmp/id_rsa $ ssh-keygen -p -N "" -f /tmp/id_rsa -$ ssh-to-pgp -private-key -i /tmp/id_rsa | gpg --import --quiet +$ nix-shell -p gnupg -p ssh-to-pgp --run "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`. -``` -export SOPS_PGP_FP=2504791468b153b8a3963cc97ba53d1919c5dfd4 +```console +$ export SOPS_PGP_FP=2504791468b153b8a3963cc97ba53d1919c5dfd4 ``` If you have generated a GnuPG key directly you can get your fingerprint like this: -``` -gpg --list-secret-keys +```console +$ gpg --list-secret-keys /tmp/tmp.JA07D1aVRD/pubring.kbx ------------------------------- sec rsa2048 1970-01-01 [SCE] @@ -170,22 +169,21 @@ The easiest way to add new hosts is using ssh host keys (requires openssh to be 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-pgp -$ ssh root@server01 "cat /etc/ssh/ssh_host_rsa_key" | ssh-to-pgp -o server01.asc +```console +$ ssh root@server01 "cat /etc/ssh/ssh_host_rsa_key" | nix-shell -p ssh-to-pgp --run "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 +$ ssh youruser@server01 "sudo cat /etc/ssh/ssh_host_rsa_key" | nix-shell -p ssh-to-pgp --run "ssh-to-pgp -o server01.asc" 0fd60c8c3b664aceb1796ce02b318df330331003 # Or just read them locally (or in a ssh session) -$ ssh-to-pgp -i /etc/ssh/ssh_host_rsa_key -o server01.asc +$ nix-shell -p ssh-to-pgp --run "ssh-to-pgp -i /etc/ssh/ssh_host_rsa_key -o server01.asc" 0fd60c8c3b664aceb1796ce02b318df330331003 ``` Also the hex string here is the fingerprint of your server's gpg key that can be exported append to `SOPS_PGP_FP`: -``` -export SOPS_PGP_FP=${SOPS_PGP_FP}:2504791468b153b8a3963cc97ba53d1919c5dfd4 +```console +$ export SOPS_PGP_FP=${SOPS_PGP_FP}:2504791468b153b8a3963cc97ba53d1919c5dfd4 ``` If you prefer having a separate GnuPG key, see [Use with GnuPG instead of ssh keys](#use-with-gnupg-instead-of-ssh-keys). @@ -195,14 +193,14 @@ If you prefer having a separate GnuPG key, see [Use with GnuPG instead of ssh ke To create a sops file you need to set export `SOPS_PGP_FP` to include both the fingerprint of your personal gpg key (and your colleagues) and your servers: -``` -export SOPS_PGP_FP="2504791468b153b8a3963cc97ba53d1919c5dfd4,2504791468b153b8a3963cc97ba53d1919c5dfd4" +```console +$ export SOPS_PGP_FP="2504791468b153b8a3963cc97ba53d1919c5dfd4,2504791468b153b8a3963cc97ba53d1919c5dfd4" ``` sops-nix automates that with a hook for nix-shell and also takes care of importing all keys, allowing public keys to be stored in git: -``` +```nix # shell.nix with import {}; mkShell { @@ -236,8 +234,8 @@ $ tree . After that you can open a new file with sops -``` -nix-shell --run "sops secrets.yaml" +```console +$ nix-shell -p sops --run "sops secrets.yaml" ``` This will start your configured editor @@ -611,7 +609,7 @@ This is how it can be included in your configuration.nix: If you prefer having a separate GnuPG key, sops-nix also comes with a helper tool: -``` +```console $ nix-shell -p sops-init-gpg-key $ sops-init-gpg-key --hostname server01 --gpghome /tmp/newkey You can use the following command to save it to a file: diff --git a/default.nix b/default.nix index 55963df..9127ae5 100644 --- a/default.nix +++ b/default.nix @@ -9,11 +9,8 @@ in rec { sops-pgp-hook = pkgs.callPackage ./pkgs/sops-pgp-hook { }; inherit sops-install-secrets; - ssh-to-pgp = pkgs.callPackage ./pkgs/ssh-to-pgp { - inherit vendorSha256; - }; - - inherit (sops-install-secrets); + # backwards compatibility + inherit (pkgs) ssh-to-pgp; # used in the CI only sops-pgp-hook-test = pkgs.buildGoModule { @@ -28,7 +25,7 @@ in rec { unit-tests = pkgs.callPackage ./unit-tests.nix {}; - lint = ssh-to-pgp.overrideAttrs (old: { + lint = sops-install-secrets.overrideAttrs (old: { name = "golangci-lint"; nativeBuildInputs = old.nativeBuildInputs ++ [ pkgs.golangci-lint ]; buildPhase = '' @@ -36,7 +33,7 @@ in rec { ''; doCheck = false; installPhase = '' - touch $out + touch $out $unittest ''; fixupPhase = ":"; }); diff --git a/flake.lock b/flake.lock index 1739dd0..220b538 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1612101959, - "narHash": "sha256-7IsEL/6wpr1fI/XyIw26Rz+KjRCoJvoozIcTRpCFfMQ=", + "lastModified": 1613917044, + "narHash": "sha256-YvBBwtvrnove51SXQ67OVQHctYjEEpFu6GEzRe0pp5I=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "1b77b735ea70c3dfbdab3eaa79aee48d75d3e162", + "rev": "aed173ff9707387b238c1c7e143152ca9d8878e9", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index a3abc5c..5b82827 100644 --- a/flake.nix +++ b/flake.nix @@ -16,7 +16,9 @@ let localPkgs = import ./default.nix { pkgs = final; }; in { - inherit (localPkgs) sops-install-secrets sops-init-gpg-key sops-pgp-hook ssh-to-pgp; + inherit (localPkgs) sops-install-secrets sops-init-gpg-key sops-pgp-hook; + # backward compatibility + inherit (prev) ssh-to-pgp; }; nixosModules.sops = import ./modules/sops; packages = forAllSystems (system: import ./default.nix { diff --git a/pkgs/sops-install-secrets/main.go b/pkgs/sops-install-secrets/main.go index 1d683c2..4645bbb 100644 --- a/pkgs/sops-install-secrets/main.go +++ b/pkgs/sops-install-secrets/main.go @@ -17,7 +17,7 @@ import ( "strings" "syscall" - "github.com/Mic92/sops-nix/pkgs/sshkeys" + "github.com/Mic92/sops-nix/pkgs/sops-install-secrets/sshkeys" "github.com/mozilla-services/yaml" "go.mozilla.org/sops/v3/decrypt" @@ -106,16 +106,16 @@ type appContext struct { func secureSymlinkChown(symlinkToCheck, expectedTarget string, owner, group int) error { fd, err := unix.Open(symlinkToCheck, unix.O_CLOEXEC|unix.O_PATH|unix.O_NOFOLLOW, 0) if err != nil { - return fmt.Errorf("Failed to open %s: %w", symlinkToCheck, err) + return fmt.Errorf("Failed to open %s: %w", symlinkToCheck, err) } defer unix.Close(fd) - buf := make([]byte, len(expectedTarget) + 1) // oversize by one to detect trunc + buf := make([]byte, len(expectedTarget)+1) // oversize by one to detect trunc n, err := unix.Readlinkat(fd, "", buf) if err != nil { return fmt.Errorf("couldn't readlinkat %s", symlinkToCheck) } - if n > len(expectedTarget) || string(buf[:n]) != expectedTarget { + if n > len(expectedTarget) || string(buf[:n]) != expectedTarget { return fmt.Errorf("symlink %s does not point to %s", symlinkToCheck, expectedTarget) } err = unix.Fchownat(fd, "", owner, group, unix.AT_EMPTY_PATH) @@ -140,7 +140,7 @@ func readManifest(path string) (*manifest, error) { } func linksAreEqual(linkTarget, targetFile string, info os.FileInfo, secret *secret) bool { - validUG := true; + validUG := true if stat, ok := info.Sys().(*syscall.Stat_t); ok { validUG = validUG && int(stat.Uid) == secret.owner validUG = validUG && int(stat.Gid) == secret.group @@ -234,7 +234,7 @@ func decryptSecret(s *secret, sourceFiles map[string]plainData) error { strVal, ok := val.(string) if !ok { return fmt.Errorf("The value of key '%s' in '%s' is not a string", s.Key, s.SopsFile) - } + } s.value = []byte(strVal) } sourceFiles[s.SopsFile] = sourceFile @@ -258,7 +258,7 @@ func mountSecretFs(mountpoint string, keysGid int) error { return fmt.Errorf("Cannot create directory '%s': %w", mountpoint, err) } - buf := unix.Statfs_t {} + buf := unix.Statfs_t{} if err := unix.Statfs(mountpoint, &buf); err != nil { return fmt.Errorf("Cannot get statfs for directory '%s': %w", mountpoint, err) } diff --git a/pkgs/sshkeys/convert.go b/pkgs/sops-install-secrets/sshkeys/convert.go similarity index 100% rename from pkgs/sshkeys/convert.go rename to pkgs/sops-install-secrets/sshkeys/convert.go diff --git a/pkgs/ssh-to-pgp/default.nix b/pkgs/ssh-to-pgp/default.nix deleted file mode 100644 index 32a7f91..0000000 --- a/pkgs/ssh-to-pgp/default.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ stdenv, lib, buildGoModule, gnupg, vendorSha256, }: -buildGoModule { - pname = "ssh-to-pgp"; - version = "0.0.1"; - - src = ../..; - - subPackages = [ "pkgs/ssh-to-pgp" ]; - - checkInputs = [ gnupg ]; - checkPhase = '' - HOME=$TMPDIR go test ./pkgs/ssh-to-pgp - ''; - - doCheck = true; - - inherit vendorSha256; - - meta = with lib; { - description = "Convert ssh public/private keys to PGP"; - homepage = "https://github.com/Mic92/sops-nix"; - license = licenses.mit; - maintainers = with maintainers; [ mic92 ]; - platforms = platforms.unix; - }; -} diff --git a/pkgs/ssh-to-pgp/main.go b/pkgs/ssh-to-pgp/main.go deleted file mode 100644 index b921053..0000000 --- a/pkgs/ssh-to-pgp/main.go +++ /dev/null @@ -1,96 +0,0 @@ -package main - -import ( - "encoding/hex" - "flag" - "fmt" - "io" - "io/ioutil" - "os" - - "github.com/Mic92/sops-nix/pkgs/sshkeys" - "golang.org/x/crypto/openpgp" - "golang.org/x/crypto/openpgp/armor" -) - -type options struct { - format, out, in string - privateKey bool -} - -func parseFlags(args []string) options { - var opts options - f := flag.NewFlagSet(args[0], flag.ExitOnError) - 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") - if err := f.Parse(args[1:]); err != nil { - // should never happen since flag.ExitOnError - panic(err) - } - - return opts -} - -func convertKeys(args []string) error { - opts := parseFlags(args) - var err error - var sshKey []byte - if opts.in == "-" { - sshKey, _ = ioutil.ReadAll(os.Stdin) - if err != nil { - return fmt.Errorf("error reading stdin: %w", err) - } - } else { - sshKey, err = ioutil.ReadFile(opts.in) - if err != nil { - return fmt.Errorf("error reading %s: %w", opts.in, err) - } - } - - writer := io.WriteCloser(os.Stdout) - if opts.out != "-" { - writer, err = os.Create(opts.out) - if err != nil { - return fmt.Errorf("failed to create %s: %w", opts.out, err) - } - defer writer.Close() - } - - if opts.format == "armor" { - keyType := openpgp.PublicKeyType - if opts.privateKey { - keyType = openpgp.PrivateKeyType - } - writer, err = armor.Encode(writer, keyType, make(map[string]string)) - if err != nil { - return fmt.Errorf("failed to encode armor writer: %w", err) - } - } - - gpgKey, err := sshkeys.SSHPrivateKeyToPGP(sshKey) - if err != nil { - return err - } - - if opts.privateKey { - err = gpgKey.SerializePrivate(writer, nil) - } else { - err = gpgKey.Serialize(writer) - } - if err == nil { - if opts.format == "armor" { - writer.Close() - } - fmt.Fprintf(os.Stderr, "%s\n", hex.EncodeToString(gpgKey.PrimaryKey.Fingerprint[:])) - } - return err -} - -func main() { - if err := convertKeys(os.Args); err != nil { - fmt.Fprintf(os.Stderr, "%s: %s\n", os.Args[0], err) - os.Exit(1) - } -} diff --git a/pkgs/ssh-to-pgp/main_test.go b/pkgs/ssh-to-pgp/main_test.go deleted file mode 100644 index 1b01103..0000000 --- a/pkgs/ssh-to-pgp/main_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "os" - "os/exec" - "path" - "path/filepath" - "runtime" - "testing" -) - -// ok fails the test if an err is not nil. -func ok(tb testing.TB, err error) { - if err != nil { - _, file, line, _ := runtime.Caller(1) - fmt.Printf("\033[31m%s:%d: unexpected error: %s\033[39m\n\n", filepath.Base(file), line, err.Error()) - tb.FailNow() - } -} - -func TempRoot() string { - if runtime.GOOS == "darwin" { - // macOS make its TEMPDIR long enough for unix socket to break - return "/tmp" - } else { - return os.TempDir() - } -} - -func TestCli(t *testing.T) { - assets := os.Getenv("TEST_ASSETS") - if assets == "" { - assets = "test-assets" - } - tempdir, err := ioutil.TempDir(TempRoot(), "testdir") - ok(t, err) - defer os.RemoveAll(tempdir) - - gpgHome := path.Join(tempdir, "gpg-home") - gpgEnv := append(os.Environ(), fmt.Sprintf("GNUPGHOME=%s", gpgHome)) - ok(t, os.Mkdir(gpgHome, os.FileMode(0700))) - - out := path.Join(tempdir, "out") - privKey := path.Join(assets, "id_rsa") - cmds := [][]string{ - {"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) - ok(t, err) - cmd := exec.Command("gpg", "--with-fingerprint", "--show-key", out) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Env = gpgEnv - ok(t, cmd.Run()) - } -} diff --git a/pkgs/ssh-to-pgp/test-assets/id_rsa b/pkgs/ssh-to-pgp/test-assets/id_rsa deleted file mode 100644 index 7b631e7..0000000 --- a/pkgs/ssh-to-pgp/test-assets/id_rsa +++ /dev/null @@ -1,38 +0,0 @@ ------BEGIN OPENSSH PRIVATE KEY----- -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 ------END OPENSSH PRIVATE KEY-----