1
0
Fork 0
mirror of https://github.com/Mic92/sops-nix.git synced 2024-12-14 11:57:52 +00:00

remove ssh-to-pgp from sops-nix

This commit is contained in:
Jörg Thalheim 2021-02-01 12:12:20 +01:00
parent d578742590
commit f540b74ced
No known key found for this signature in database
GPG key ID: B3F5D81B0C6967C4
11 changed files with 40 additions and 264 deletions

View file

@ -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

View file

@ -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 <nixpkgs> {};
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:

View file

@ -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 = ":";
});

View file

@ -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": {

View file

@ -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 {

View file

@ -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)
}

View file

@ -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;
};
}

View file

@ -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)
}
}

View file

@ -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())
}
}

View file

@ -1,38 +0,0 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEA0nkukhKbrSDotpkIS3iBCZzAIp5PinFL9/B52i2pN55y5lLGCZ12
AeSmRsWsVbSI6+fSfE53ZsJ4mfsHNK6peg5In/QrE14AI8az4pJ2TUOyUG4FlK9KZOI8fy
t8yw+ov1wEvEFskjSZmOWiVskmxfyuvO5FDCeapdAV+E7dEYli+KSMM2WZS8x+K0cksuM9
ZG4rjmX/IbVUbZRqAxqhlYiUabsm0iq5l23r0SO2lo4ppdgVUzJLT3pAD8fjAN7f/BDP7i
cbVqe4NwbJ3h0HSiI0dFhCgCE05rgyxBLSF1xFG4AVtFo2w+tp0X7fwOv4slspDyZpCwOF
p0i0tN3GnlMQiqBLYWdcXwTwkTcO8W8rEBfAhyc/HADI2RoARpTop/BCg4ZpZmuhWfeHAA
eU6+Bt0dIeJMu+2z5Nv+r72bclPwBZwz9h3xmkQgzRfkO/n0fWJisHFv7wmtiLSBF4DJgY
0vspdKfuH1WmOkO2wk263es52+oExqO5w/So/whlAAAFiHeHPDl3hzw5AAAAB3NzaC1yc2
EAAAGBANJ5LpISm60g6LaZCEt4gQmcwCKeT4pxS/fwedotqTeecuZSxgmddgHkpkbFrFW0
iOvn0nxOd2bCeJn7BzSuqXoOSJ/0KxNeACPGs+KSdk1DslBuBZSvSmTiPH8rfMsPqL9cBL
xBbJI0mZjlolbJJsX8rrzuRQwnmqXQFfhO3RGJYvikjDNlmUvMfitHJLLjPWRuK45l/yG1
VG2UagMaoZWIlGm7JtIquZdt69EjtpaOKaXYFVMyS096QA/H4wDe3/wQz+4nG1anuDcGyd
4dB0oiNHRYQoAhNOa4MsQS0hdcRRuAFbRaNsPradF+38Dr+LJbKQ8maQsDhadItLTdxp5T
EIqgS2FnXF8E8JE3DvFvKxAXwIcnPxwAyNkaAEaU6KfwQoOGaWZroVn3hwAHlOvgbdHSHi
TLvts+Tb/q+9m3JT8AWcM/Yd8ZpEIM0X5Dv59H1iYrBxb+8JrYi0gReAyYGNL7KXSn7h9V
pjpDtsJNut3rOdvqBMajucP0qP8IZQAAAAMBAAEAAAGAU5/wX/tivTQBImPFRu83HdGZCW
grJE+Fppp2X7iark2XS2oB41obw/7MDfyGT3sul8SA/gDTMhH8hvmVUFpBXgyE0IDcCJLl
rVFKsbANrv9BvvEn6H6JKXI2JTTrHWc4Xee6ve2krKaXjIdYq/C6JhoSd2CYMI8fw9fcks
8KyOf0WeRPDDDG6rXyP1HCBA2Dm/6l8asW5pa8V9mLEXaoUth0V1oTv5dYLBFxi6QL7N/J
LmqfdnHaOFbTUzHRQMxMK1L04ETDhIUn6C6hnJfXetRInii5s2l8xCJjkd3bBzrWUk7Csz
3nxBqVPWIFoPrhU5W7lEJRngQMYRLS0HHq2puxhgcpxgr183uSBoXTggIUs1KnutDizZXs
Ioh5JhR5DQmlilGfRsh9pk+WjpbWT2RtR44ugkvcbBLYkU24KYAyzgjM88Zq3yoJeHfFea
2osxpSY02CDdY4YGNO1x0vSSkE4nGeiV4n+EK7HxaVY4IN+8Pr/6a2PB3J43OZ2heBAAAA
wET9bby2qLJJOE6bpYezwlCt7ip2UM07z8LL0Za/qeCyCmsMGGyO4GAnXZW52IRQbotCh9
gYoW3HqBZHWTt+ptEOkSywdOdaqw7Ib0HSgjkTw1IHIr/ij3eqFa83SosAbmtrwdEhiue9
Hm0McAN8d/9lpFxjE9DAfqvZNn5Gsnv6rqPmuqKXcdEClGVj6ciUAsTinDs++FkBczRIfj
uRBwqJonXRh/Ts9EKo/+AGDtB1Wx8iAu9mmttlgcqY9OT4sAAAAMEA+Y/MT94gR9pS7YVf
nZHPAZ8QP0Mz6czcGYjWkO9SzfA118ZR4ZLFWBGw7dqmmFOtUG+EoGvaolWqRoNO8++MpD
TlmOU+NsHIsafuhsGVju+v6cI3TFnIvhkPUEaWInMaPH8rW6zxT/Fh8+pRgDdluRhyNLE2
fB2AcNUHlvfHe4d1BiohRrMMb7GdeRInhhO7+NQWUVGwflkY1wUALczuTguTqmpxX3EEed
Tc1OO9uRfImDBEQR+9TbEbJgZtqlMhAAAAwQDX5zqzktidmM2iZVQoo6PuORO5vm8PvFT4
kckH6mW+dNz3NP3gtsxkJih01EJo77tjHAfoI0WVmiQekHxnDfMRWBVRYi1uZcpvC/Zy3r
CH3waJE8h4cwRlge7gDc50k4tp5DDdeSoj8ud8911pvOLlAewtu/IQz67lvlvMI4LyvDwz
BT6RBfsv3esiWqYFzX/1+mpFu/VhQ8rIERh22Y8AMLHCTcwAXXfYB5TUSTBRLBmtrb+Qy2
GlNV/y/LNbEMUAAAATam9lcmdAdHVyaW5nbWFjaGluZQ==
-----END OPENSSH PRIVATE KEY-----