1{ config, lib, pkgs, ... }:
2
3let
4
5 inherit (builtins) toFile;
6 inherit (lib) concatMapStringsSep concatStringsSep mapAttrsToList
7 mkIf mkEnableOption mkOption types;
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 ${if managePlugins then "load_modular = no" else ""}
38 ${if managePlugins then ("load = " + (concatStringsSep " " enabledPlugins)) else ""}
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.path;
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 <literal>include</literal> 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 <filename>ipsec.conf</filename> file. Defines general
75 configuration parameters.
76 '';
77 };
78
79 connections = mkOption {
80 type = types.attrsOf (types.attrsOf types.str);
81 default = {};
82 example = {
83 "%default" = {
84 keyexchange = "ikev2";
85 keyingtries = "1";
86 };
87 roadwarrior = {
88 auto = "add";
89 leftcert = "/run/keys/moonCert.pem";
90 leftid = "@moon.strongswan.org";
91 leftsubnet = "10.1.0.0/16";
92 right = "%any";
93 };
94 };
95 description = ''
96 A set of connections and their options for the ‘conn xxx’
97 sections of the <filename>ipsec.conf</filename> file.
98 '';
99 };
100
101 ca = mkOption {
102 type = types.attrsOf (types.attrsOf types.str);
103 default = {};
104 example = {
105 strongswan = {
106 auto = "add";
107 cacert = "/run/keys/strongswanCert.pem";
108 crluri = "http://crl2.strongswan.org/strongswan.crl";
109 };
110 };
111 description = ''
112 A set of CAs (certification authorities) and their options for
113 the ‘ca xxx’ sections of the <filename>ipsec.conf</filename>
114 file.
115 '';
116 };
117
118 managePlugins = mkOption {
119 type = types.bool;
120 default = false;
121 description = ''
122 If set to true, this option will disable automatic plugin loading and
123 then tell strongSwan to enable the plugins specified in the
124 <option>enabledPlugins</option> option.
125 '';
126 };
127
128 enabledPlugins = mkOption {
129 type = types.listOf types.str;
130 default = [];
131 description = ''
132 A list of additional plugins to enable if
133 <option>managePlugins</option> is true.
134 '';
135 };
136 };
137
138
139 config = with cfg;
140 let
141 secretsFile = ipsecSecrets cfg.secrets;
142 in
143 mkIf enable
144 {
145
146 # here we should use the default strongswan ipsec.secrets and
147 # append to it (default one is empty so not a pb for now)
148 environment.etc."ipsec.secrets".source = secretsFile;
149
150 systemd.services.strongswan = {
151 description = "strongSwan IPSec Service";
152 wantedBy = [ "multi-user.target" ];
153 path = with pkgs; [ kmod iproute iptables utillinux ]; # XXX Linux
154 wants = [ "keys.target" ];
155 after = [ "network-online.target" "keys.target" ];
156 environment = {
157 STRONGSWAN_CONF = strongswanConf { inherit setup connections ca secretsFile managePlugins enabledPlugins; };
158 };
159 serviceConfig = {
160 ExecStart = "${pkgs.strongswan}/sbin/ipsec start --nofork";
161 };
162 preStart = ''
163 # with 'nopeerdns' setting, ppp writes into this folder
164 mkdir -m 700 -p /etc/ppp
165 '';
166 };
167 };
168}
169