1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8let
9
10 inherit (builtins) toFile;
11 inherit (lib)
12 concatMapStrings
13 concatStringsSep
14 mapAttrsToList
15 mkIf
16 mkEnableOption
17 mkOption
18 types
19 literalExpression
20 optionalString
21 ;
22
23 cfg = config.services.strongswan;
24
25 ipsecSecrets = secrets: concatMapStrings (f: "include ${f}\n") secrets;
26
27 ipsecConf =
28 {
29 setup,
30 connections,
31 ca,
32 }:
33 let
34 # https://wiki.strongswan.org/projects/strongswan/wiki/IpsecConf
35 makeSections =
36 type: sections:
37 concatStringsSep "\n\n" (
38 mapAttrsToList (
39 sec: attrs:
40 "${type} ${sec}\n" + (concatStringsSep "\n" (mapAttrsToList (k: v: " ${k}=${v}") attrs))
41 ) sections
42 );
43 setupConf = makeSections "config" { inherit setup; };
44 connectionsConf = makeSections "conn" connections;
45 caConf = makeSections "ca" ca;
46
47 in
48 builtins.toFile "ipsec.conf" ''
49 ${setupConf}
50 ${connectionsConf}
51 ${caConf}
52 '';
53
54 strongswanConf =
55 {
56 setup,
57 connections,
58 ca,
59 secretsFile,
60 managePlugins,
61 enabledPlugins,
62 }:
63 toFile "strongswan.conf" ''
64 charon {
65 ${optionalString managePlugins "load_modular = no"}
66 ${optionalString managePlugins ("load = " + (concatStringsSep " " enabledPlugins))}
67 plugins {
68 stroke {
69 secrets_file = ${secretsFile}
70 }
71 }
72 }
73
74 starter {
75 config_file = ${ipsecConf { inherit setup connections ca; }}
76 }
77 '';
78
79in
80{
81 options.services.strongswan = {
82 enable = mkEnableOption "strongSwan";
83
84 secrets = mkOption {
85 type = types.listOf types.str;
86 default = [ ];
87 example = [ "/run/keys/ipsec-foo.secret" ];
88 description = ''
89 A list of paths to IPSec secret files. These
90 files will be included into the main ipsec.secrets file with
91 the `include` directive. It is safer if these
92 paths are absolute.
93 '';
94 };
95
96 setup = mkOption {
97 type = types.attrsOf types.str;
98 default = { };
99 example = {
100 cachecrls = "yes";
101 strictcrlpolicy = "yes";
102 };
103 description = ''
104 A set of options for the ‘config setup’ section of the
105 {file}`ipsec.conf` file. Defines general
106 configuration parameters.
107 '';
108 };
109
110 connections = mkOption {
111 type = types.attrsOf (types.attrsOf types.str);
112 default = { };
113 example = literalExpression ''
114 {
115 "%default" = {
116 keyexchange = "ikev2";
117 keyingtries = "1";
118 };
119 roadwarrior = {
120 auto = "add";
121 leftcert = "/run/keys/moonCert.pem";
122 leftid = "@moon.strongswan.org";
123 leftsubnet = "10.1.0.0/16";
124 right = "%any";
125 };
126 }
127 '';
128 description = ''
129 A set of connections and their options for the ‘conn xxx’
130 sections of the {file}`ipsec.conf` file.
131 '';
132 };
133
134 ca = mkOption {
135 type = types.attrsOf (types.attrsOf types.str);
136 default = { };
137 example = {
138 strongswan = {
139 auto = "add";
140 cacert = "/run/keys/strongswanCert.pem";
141 crluri = "http://crl2.strongswan.org/strongswan.crl";
142 };
143 };
144 description = ''
145 A set of CAs (certification authorities) and their options for
146 the ‘ca xxx’ sections of the {file}`ipsec.conf`
147 file.
148 '';
149 };
150
151 managePlugins = mkOption {
152 type = types.bool;
153 default = false;
154 description = ''
155 If set to true, this option will disable automatic plugin loading and
156 then tell strongSwan to enable the plugins specified in the
157 {option}`enabledPlugins` option.
158 '';
159 };
160
161 enabledPlugins = mkOption {
162 type = types.listOf types.str;
163 default = [ ];
164 description = ''
165 A list of additional plugins to enable if
166 {option}`managePlugins` is true.
167 '';
168 };
169 };
170
171 config =
172 with cfg;
173 mkIf enable {
174
175 # here we should use the default strongswan ipsec.secrets and
176 # append to it (default one is empty so not a pb for now)
177 environment.etc."ipsec.secrets".text = ipsecSecrets cfg.secrets;
178
179 systemd.services.strongswan = {
180 description = "strongSwan IPSec Service";
181 wantedBy = [ "multi-user.target" ];
182 path = with pkgs; [
183 kmod
184 iproute2
185 iptables
186 util-linux
187 ]; # XXX Linux
188 wants = [ "network-online.target" ];
189 after = [ "network-online.target" ];
190 environment = {
191 STRONGSWAN_CONF = strongswanConf {
192 inherit
193 setup
194 connections
195 ca
196 managePlugins
197 enabledPlugins
198 ;
199 secretsFile = "/etc/ipsec.secrets";
200 };
201 };
202 serviceConfig = {
203 ExecStart = "${pkgs.strongswan}/sbin/ipsec start --nofork";
204 };
205 preStart = ''
206 # with 'nopeerdns' setting, ppp writes into this folder
207 mkdir -m 700 -p /etc/ppp
208 '';
209 };
210 };
211}