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