about summary refs log tree commit diff
path: root/tests
diff options
context:
space:
mode:
authoraszlig <aszlig@redmoonstudios.org>2016-04-02 11:13:27 +0200
committeraszlig <aszlig@redmoonstudios.org>2016-04-02 14:08:51 +0200
commit8db1803b5d9865b2355fabdb6bb974d879ce57cc (patch)
tree7bac4a2bcf3fa1773df040fb113901e64d2625b7 /tests
parent157f16889edab556dd54cf9f367a021119019ed4 (diff)
Add a new module and test for gpg-agent
Since NixOS/nixpkgs@5391882 there no longer is the option to start the
agent during X session startup, which prompted me to write this module.

I was unhappy how GnuPG is handled in NixOS since a long time and wanted
to OCD all the configuration files directly into the module.

Unfortunately, this is something I eventually gave up because GnuPG's
design makes it very hard to preseed configuration. My first attempt was
to provide default configuration files in /etc/gnupg, but that wasn't
properly picked up by GnuPG.

Another way would have been to change the default configuration files,
but that would have the downside that we could only override those
configurations using command line options for each individual GnuPG
component.

The approach I tried to go for was to patch GnuPG so that all the
defaults are directly set in the source code using a giant sed
expression. It turned out that this approach doesn't work very well,
because every component has implemented its own ways how to handle
commandline arguments versus (default) configuration files.

In the end I gave up trying to OCD anything related to GnuPG
configuration and concentrated just on the agent.

And that's another beast, which unfortunately doesn't work very well
with systemd.

While searching the net for existing patches I stumbled upon one done by
@shlevy:

https://lists.gnupg.org/pipermail/gnupg-devel/2014-November/029092.html

Unfortunately, the upstream author seems to be quite anti-systemd and
didn't want to accept that into the upstream project.

Because of this I went for using LD_PRELOAD to pick up the file
descriptors provided by the systemd sockets, because in the end I don't
want to constantly catch up with upstream and rebase the patch on every
new release.

Apart from just wrapping the agent to be socket activated, we also wrap
the pinentry program, so that we can inject a _CLIENT_PID environment
variable from the LD_PRELOAD wrapper that is picked up by the pinentry
wrapper to determine the TTY and/or display of the client communicating
with the agent.

The wrapper uses the proc filesystem to get all the relevant information
and passes it to the real pinentry.

The advantage of this is that we don't need to do things such as
"gpg-connect-agent updatestartuptty /bye" or any other workarounds and
even if we connect via SSH the agent should be able to correctly pick up
the TTY and/or display.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
Diffstat (limited to 'tests')
-rw-r--r--tests/default.nix3
-rw-r--r--tests/programs/gpg-agent/default.nix127
-rw-r--r--tests/programs/gpg-agent/snakeoil.asc59
3 files changed, 189 insertions, 0 deletions
diff --git a/tests/default.nix b/tests/default.nix
index 54b130a3..29fdb973 100644
--- a/tests/default.nix
+++ b/tests/default.nix
@@ -12,6 +12,9 @@ in {
   games = {
     starbound = callTest ./games/starbound.nix;
   };
+  programs = {
+    gpg-agent = callTest ./programs/gpg-agent;
+  };
   richi235 = {
     # Currently broken
     #multipath-vpn = callTest ./richi235/multipath-vpn.nix;
diff --git a/tests/programs/gpg-agent/default.nix b/tests/programs/gpg-agent/default.nix
new file mode 100644
index 00000000..d10fdbfe
--- /dev/null
+++ b/tests/programs/gpg-agent/default.nix
@@ -0,0 +1,127 @@
+{ pkgs, ... }:
+
+let
+  mkExpect = expectScript: script: pkgs.writeScript "test-gnupg-cli" ''
+    #!${pkgs.expect}/bin/expect -f
+    set timeout 20
+    spawn ${pkgs.writeScript "cli-testscript.sh" ''
+      #!${pkgs.stdenv.shell} -ex
+      ${script}
+    ''}
+    ${expectScript}
+    set ret [wait]
+    exit [lindex $ret 3]
+  '';
+
+  cliTestWithPassphrase = mkExpect ''
+    expect -regexp ---+.*Please.enter
+    send supersecret\r
+  '';
+
+  cliTest = mkExpect "";
+
+in {
+  name = "gpg-agent";
+
+  enableOCR = true;
+
+  machine = { lib, ... }: {
+    imports = map (what:
+      "${import ../../../nixpkgs-path.nix}/nixos/tests/common/${what}.nix"
+    ) [ "user-account" "x11" ];
+
+    services.openssh.enable = true;
+    services.xserver.displayManager.auto.user = "alice";
+
+    vuizvui.programs.gpg-agent.enable = true;
+    vuizvui.programs.gpg-agent.sshSupport = true;
+    programs.ssh.startAgent = false;
+  };
+
+  testScript = ''
+    $machine->waitForUnit("sshd.service");
+    $machine->succeed("ssh-keygen -t ed25519 -f /root/id_ed25519 -N '''");
+    my $cmd = 'mkdir -p ~/.ssh && cat > ~/.ssh/authorized_keys';
+    $machine->succeed("su -c 'umask 0077; $cmd' alice < /root/id_ed25519.pub");
+
+    $machine->waitForX;
+
+    sub ssh ($) {
+      my $esc = $_[0] =~ s/'/'\\${"'"}'/gr;
+      return "ssh -q -i /root/id_ed25519".
+             " -o StrictHostKeyChecking=no".
+             " alice\@127.0.0.1 -- '$esc'";
+    }
+
+    sub xsu ($) {
+      my $esc = $_[0] =~ s/'/'\\${"'"}'/gr;
+      return "DISPLAY=:0 su alice -c '$esc'";
+    }
+
+    $machine->nest("import snakeoil key", sub {
+      $machine->succeed(ssh "${cliTestWithPassphrase ''
+        gpg2 --import ${./snakeoil.asc}
+      ''}");
+      $machine->succeed(ssh "${mkExpect ''
+        expect gpg>
+        send trust\r
+        expect decision?
+        send 5\r
+        expect "Do you really want"
+        send y\r
+        expect gpg>
+        send save\r
+      '' "gpg2 --edit-key ECC15FE1"}");
+    });
+
+    subtest "test SSH agent support", sub {
+      $machine->succeed(ssh 'ssh-keygen -t ed25519 -f ~/testkey -N ""');
+      $machine->succeed(ssh '${mkExpect ''
+        expect -regexp ---+.*Please.enter
+        send supersecret\r
+        expect -regexp ---+.*Please.re-en
+        send supersecret\r
+      '' "ssh-add ~/testkey"}');
+
+      $machine->succeed("umask 0077; $cmd < ~alice/testkey.pub");
+      $machine->succeed(ssh 'rm ~/testkey*');
+
+      $machine->succeed(ssh 'ssh -o StrictHostKeyChecking=no root@127.0.0.1'.
+                            ' touch /i_have_thu_powarr');
+      $machine->succeed("test -e /i_have_thu_powarr");
+
+      $machine->succeed(ssh "systemctl --user reload gpg-agent");
+
+      $machine->succeed(ssh "${cliTestWithPassphrase ''
+        ssh -o StrictHostKeyChecking=no root@127.0.0.1 \
+          touch /i_still_have_thu_powarr
+      ''}");
+      $machine->succeed("test -e /i_still_have_thu_powarr");
+    };
+
+    subtest "test from SSH", sub {
+      $machine->succeed(ssh "systemctl --user reload gpg-agent");
+      $machine->succeed(ssh "${cliTestWithPassphrase ''
+        echo encrypt me > to_encrypt
+        gpg2 -sea -r ECC15FE1 to_encrypt
+        rm to_encrypt
+      ''}");
+      $machine->succeed(ssh "${cliTest ''
+        [ "$(gpg2 -d to_encrypt.asc)" = "encrypt me" ]
+      ''}");
+    };
+
+    subtest "test from X", sub {
+      $machine->succeed(ssh "systemctl --user reload gpg-agent");
+      my $pid = $machine->succeed(xsu
+        'echo encrypt me | gpg2 -sea -r ECC15FE1 > encrypted_x.asc & echo $!'
+      );
+      chomp $pid;
+      $machine->waitForText(qr/Passphrase/);
+      $machine->screenshot("passphrase_dialog");
+      $machine->sendChars("supersecret\n");
+      $machine->waitUntilFails("kill -0 $pid");
+      $machine->succeed(xsu '[ "$(gpg2 -d encrypted_x.asc)" = "encrypt me" ]');
+    };
+  '';
+}
diff --git a/tests/programs/gpg-agent/snakeoil.asc b/tests/programs/gpg-agent/snakeoil.asc
new file mode 100644
index 00000000..59c07011
--- /dev/null
+++ b/tests/programs/gpg-agent/snakeoil.asc
@@ -0,0 +1,59 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v2
+
+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
+75FOlpj5VS82Sl0=
+=3HD3
+-----END PGP PRIVATE KEY BLOCK-----