1# The certificate for the ACME service is exported as:
2#
3# config.test-support.acme.caCert
4#
5# This value can be used inside the configuration of other test nodes to inject
6# the test certificate into security.pki.certificateFiles or into package
7# overlays.
8#
9# Another value that's needed if you don't use a custom resolver (see below for
10# notes on that) is to add the acme node as a nameserver to every node
11# that needs to acquire certificates using ACME, because otherwise the API host
12# for acme.test can't be resolved.
13#
14# A configuration example of a full node setup using this would be this:
15#
16# {
17# acme = import ./common/acme/server;
18#
19# example = { nodes, ... }: {
20# networking.nameservers = [
21# nodes.acme.networking.primaryIPAddress
22# ];
23# security.pki.certificateFiles = [
24# nodes.acme.test-support.acme.caCert
25# ];
26# };
27# }
28#
29# By default, this module runs a local resolver, generated using resolver.nix
30# from the parent directory to automatically discover all zones in the network.
31#
32# If you do not want this and want to use your own resolver, you can just
33# override networking.nameservers like this:
34#
35# {
36# acme = { nodes, lib, ... }: {
37# imports = [ ./common/acme/server ];
38# networking.nameservers = lib.mkForce [
39# nodes.myresolver.networking.primaryIPAddress
40# ];
41# };
42#
43# myresolver = ...;
44# }
45#
46# Keep in mind, that currently only _one_ resolver is supported, if you have
47# more than one resolver in networking.nameservers only the first one will be
48# used.
49#
50# Also make sure that whenever you use a resolver from a different test node
51# that it has to be started _before_ the ACME service.
52{ config, pkgs, lib, ... }:
53let
54 testCerts = import ./snakeoil-certs.nix;
55 domain = testCerts.domain;
56
57 resolver = let
58 message = "You need to define a resolver for the acme test module.";
59 firstNS = lib.head config.networking.nameservers;
60 in if config.networking.nameservers == [] then throw message else firstNS;
61
62 pebbleConf.pebble = {
63 listenAddress = "0.0.0.0:443";
64 managementListenAddress = "0.0.0.0:15000";
65 # These certs and keys are used for the Web Front End (WFE)
66 certificate = testCerts.${domain}.cert;
67 privateKey = testCerts.${domain}.key;
68 httpPort = 80;
69 tlsPort = 443;
70 ocspResponderURL = "http://${domain}:4002";
71 strict = true;
72 };
73
74 pebbleConfFile = pkgs.writeText "pebble.conf" (builtins.toJSON pebbleConf);
75
76in {
77 imports = [ ../../resolver.nix ];
78
79 options.test-support.acme = {
80 caDomain = lib.mkOption {
81 type = lib.types.str;
82 readOnly = true;
83 default = domain;
84 description = lib.mdDoc ''
85 A domain name to use with the `nodes` attribute to
86 identify the CA server.
87 '';
88 };
89 caCert = lib.mkOption {
90 type = lib.types.path;
91 readOnly = true;
92 default = testCerts.ca.cert;
93 description = lib.mdDoc ''
94 A certificate file to use with the `nodes` attribute to
95 inject the test CA certificate used in the ACME server into
96 {option}`security.pki.certificateFiles`.
97 '';
98 };
99 };
100
101 config = {
102 test-support = {
103 resolver.enable = let
104 isLocalResolver = config.networking.nameservers == [ "127.0.0.1" ];
105 in lib.mkOverride 900 isLocalResolver;
106 };
107
108 # This has priority 140, because modules/testing/test-instrumentation.nix
109 # already overrides this with priority 150.
110 networking.nameservers = lib.mkOverride 140 [ "127.0.0.1" ];
111 networking.firewall.allowedTCPPorts = [ 80 443 15000 4002 ];
112
113 networking.extraHosts = ''
114 127.0.0.1 ${domain}
115 ${config.networking.primaryIPAddress} ${domain}
116 '';
117
118 systemd.services = {
119 pebble = {
120 enable = true;
121 description = "Pebble ACME server";
122 wantedBy = [ "network.target" ];
123 environment = {
124 # We're not testing lego, we're just testing our configuration.
125 # No need to sleep.
126 PEBBLE_VA_NOSLEEP = "1";
127 };
128
129 serviceConfig = {
130 RuntimeDirectory = "pebble";
131 WorkingDirectory = "/run/pebble";
132
133 # Required to bind on privileged ports.
134 AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
135
136 ExecStart = "${pkgs.pebble}/bin/pebble -config ${pebbleConfFile}";
137 };
138 };
139 };
140 };
141}