vuls: init at 0.27.0
[NixPkgs.git] / nixos / tests / custom-ca.nix
blob0fcdf81022d72cea6585ff8cad2362eab5147aed
1 # Checks that `security.pki` options are working in curl and the main browser
2 # engines: Gecko (via Firefox), Chromium, QtWebEngine (via qutebrowser) and
3 # WebKitGTK (via Midori). The test checks that certificates issued by a custom
4 # trusted CA are accepted but those from an unknown CA are rejected.
6 { system ? builtins.currentSystem,
7   config ? {},
8   pkgs ? import ../.. { inherit system config; }
9 }:
11 with import ../lib/testing-python.nix { inherit system pkgs; };
13 let
14   inherit (pkgs) lib;
16   makeCert = { caName, domain }: pkgs.runCommand "example-cert"
17   { buildInputs = [ pkgs.gnutls ]; }
18   ''
19     mkdir $out
21     # CA cert template
22     cat >ca.template <<EOF
23     organization = "${caName}"
24     cn = "${caName}"
25     expiration_days = 365
26     ca
27     cert_signing_key
28     crl_signing_key
29     EOF
31     # server cert template
32     cat >server.template <<EOF
33     organization = "An example company"
34     cn = "${domain}"
35     expiration_days = 30
36     dns_name = "${domain}"
37     encryption_key
38     signing_key
39     EOF
41     # generate CA keypair
42     certtool                \
43       --generate-privkey    \
44       --key-type rsa        \
45       --sec-param High      \
46       --outfile $out/ca.key
47     certtool                     \
48       --generate-self-signed     \
49       --load-privkey $out/ca.key \
50       --template ca.template     \
51       --outfile $out/ca.crt
53     # generate server keypair
54     certtool                    \
55       --generate-privkey        \
56       --key-type rsa            \
57       --sec-param High          \
58       --outfile $out/server.key
59     certtool                            \
60       --generate-certificate            \
61       --load-privkey $out/server.key    \
62       --load-ca-privkey $out/ca.key     \
63       --load-ca-certificate $out/ca.crt \
64       --template server.template        \
65       --outfile $out/server.crt
66   '';
68   example-good-cert = makeCert
69     { caName = "Example good CA";
70       domain = "good.example.com";
71     };
73   example-bad-cert = makeCert
74     { caName = "Unknown CA";
75       domain = "bad.example.com";
76     };
78   webserverConfig =
79     { networking.hosts."127.0.0.1" = [ "good.example.com" "bad.example.com" ];
80       security.pki.certificateFiles = [ "${example-good-cert}/ca.crt" ];
82       services.nginx.enable = true;
83       services.nginx.virtualHosts."good.example.com" =
84         { onlySSL = true;
85           sslCertificate = "${example-good-cert}/server.crt";
86           sslCertificateKey = "${example-good-cert}/server.key";
87           locations."/".extraConfig = ''
88             add_header Content-Type text/plain;
89             return 200 'It works!';
90           '';
91         };
92       services.nginx.virtualHosts."bad.example.com" =
93         { onlySSL = true;
94           sslCertificate = "${example-bad-cert}/server.crt";
95           sslCertificateKey = "${example-bad-cert}/server.key";
96           locations."/".extraConfig = ''
97             add_header Content-Type text/plain;
98             return 200 'It does not work!';
99           '';
100         };
101     };
103   curlTest = makeTest {
104     name = "custom-ca-curl";
105     meta.maintainers = with lib.maintainers; [ rnhmjoj ];
106     nodes.machine = { ... }: webserverConfig;
107     testScript = ''
108         with subtest("Good certificate is trusted in curl"):
109             machine.wait_for_unit("nginx")
110             machine.wait_for_open_port(443)
111             machine.succeed("curl -fv https://good.example.com")
113         with subtest("Unknown CA is untrusted in curl"):
114             machine.fail("curl -fv https://bad.example.com")
115     '';
116   };
118   mkBrowserTest = browser: testParams: makeTest {
119     name = "custom-ca-${browser}";
120     meta.maintainers = with lib.maintainers; [ rnhmjoj ];
122     enableOCR = true;
124     nodes.machine = { pkgs, ... }:
125       { imports =
126           [ ./common/user-account.nix
127             ./common/x11.nix
128             webserverConfig
129           ];
131         # chromium-based browsers refuse to run as root
132         test-support.displayManager.auto.user = "alice";
134         # machine often runs out of memory with less
135         virtualisation.memorySize = 1024;
137         environment.systemPackages = [ pkgs.xdotool pkgs.${browser} ];
138       };
140     testScript = ''
141       from typing import Tuple
142       def execute_as(user: str, cmd: str) -> Tuple[int, str]:
143           """
144           Run a shell command as a specific user.
145           """
146           return machine.execute(f"sudo -u {user} {cmd}")
149       def wait_for_window_as(user: str, cls: str) -> None:
150           """
151           Wait until a X11 window of a given user appears.
152           """
154           def window_is_visible(last_try: bool) -> bool:
155               ret, stdout = execute_as(user, f"xdotool search --onlyvisible --class {cls}")
156               if last_try:
157                   machine.log(f"Last chance to match {cls} on the window list")
158               return ret == 0
160           with machine.nested("Waiting for a window to appear"):
161               retry(window_is_visible)
164       machine.start()
165       machine.wait_for_x()
167       command = "${browser} ${testParams.args or ""}"
168       with subtest("Good certificate is trusted in ${browser}"):
169           execute_as(
170               "alice", f"{command} https://good.example.com >&2 &"
171           )
172           wait_for_window_as("alice", "${browser}")
173           machine.sleep(4)
174           execute_as("alice", "xdotool key ctrl+r")  # reload to be safe
175           machine.wait_for_text("It works!")
176           machine.screenshot("good${browser}")
177           execute_as("alice", "xdotool key ctrl+w")  # close tab
179       with subtest("Unknown CA is untrusted in ${browser}"):
180           execute_as("alice", f"{command} https://bad.example.com >&2 &")
181           machine.wait_for_text("${testParams.error}")
182           machine.screenshot("bad${browser}")
183     '';
184   };
189   curl = curlTest;
190 } // pkgs.lib.mapAttrs mkBrowserTest {
191   firefox = { error = "Security Risk"; };
192   chromium = { error = "not private"; };
193   qutebrowser = { args = "-T"; error = "Certificate error"; };
194   midori = { args = "-p"; error = "Security"; };