1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8
9 cfg = config.services.libreswan;
10
11 libexec = "${pkgs.libreswan}/libexec/ipsec";
12 ipsec = "${pkgs.libreswan}/sbin/ipsec";
13
14 trim =
15 chars: str:
16 let
17 nonchars = lib.filter (x: !(lib.elem x.value chars)) (
18 lib.imap0 (i: v: {
19 ind = i;
20 value = v;
21 }) (lib.stringToCharacters str)
22 );
23 in
24 lib.optionalString (nonchars != [ ]) (
25 lib.substring (lib.head nonchars).ind (lib.add 1 (
26 lib.sub (lib.last nonchars).ind (lib.head nonchars).ind
27 )) str
28 );
29 indent =
30 str:
31 lib.concatStrings (
32 lib.concatMap (s: [
33 " "
34 (trim [ " " "\t" ] s)
35 "\n"
36 ]) (lib.splitString "\n" str)
37 );
38 configText = indent (toString cfg.configSetup);
39 connectionText = lib.concatStrings (
40 lib.mapAttrsToList (n: v: ''
41 conn ${n}
42 ${indent v}
43 '') cfg.connections
44 );
45
46 configFile = pkgs.writeText "ipsec-nixos.conf" ''
47 config setup
48 ${configText}
49
50 ${connectionText}
51 '';
52
53 policyFiles = lib.mapAttrs' (name: text: {
54 name = "ipsec.d/policies/${name}";
55 value.source = pkgs.writeText "ipsec-policy-${name}" text;
56 }) cfg.policies;
57
58in
59
60{
61
62 ###### interface
63
64 options = {
65
66 services.libreswan = {
67
68 enable = lib.mkEnableOption "Libreswan IPsec service";
69
70 configSetup = lib.mkOption {
71 type = lib.types.lines;
72 default = ''
73 protostack=netkey
74 virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:25.0.0.0/8,%v4:100.64.0.0/10,%v6:fd00::/8,%v6:fe80::/10
75 '';
76 example = ''
77 secretsfile=/root/ipsec.secrets
78 protostack=netkey
79 virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:25.0.0.0/8,%v4:100.64.0.0/10,%v6:fd00::/8,%v6:fe80::/10
80 '';
81 description = "Options to go in the 'config setup' section of the Libreswan IPsec configuration";
82 };
83
84 connections = lib.mkOption {
85 type = lib.types.attrsOf lib.types.lines;
86 default = { };
87 example = lib.literalExpression ''
88 { myconnection = '''
89 auto=add
90 left=%defaultroute
91 leftid=@user
92
93 right=my.vpn.com
94
95 ikev2=no
96 ikelifetime=8h
97 ''';
98 }
99 '';
100 description = "A set of connections to define for the Libreswan IPsec service";
101 };
102
103 policies = lib.mkOption {
104 type = lib.types.attrsOf lib.types.lines;
105 default = { };
106 example = lib.literalExpression ''
107 { private-or-clear = '''
108 # Attempt opportunistic IPsec for the entire Internet
109 0.0.0.0/0
110 ::/0
111 ''';
112 }
113 '';
114 description = ''
115 A set of policies to apply to the IPsec connections.
116
117 ::: {.note}
118 The policy name must match the one of connection it needs to apply to.
119 :::
120 '';
121 };
122
123 disableRedirects = lib.mkOption {
124 type = lib.types.bool;
125 default = true;
126 description = ''
127 Whether to disable send and accept redirects for all network interfaces.
128 See the Libreswan [
129 FAQ](https://libreswan.org/wiki/FAQ#Why_is_it_recommended_to_disable_send_redirects_in_.2Fproc.2Fsys.2Fnet_.3F) page for why this is recommended.
130 '';
131 };
132
133 };
134
135 };
136
137 ###### implementation
138
139 config = lib.mkIf cfg.enable {
140
141 # Install package, systemd units, etc.
142 environment.systemPackages = [
143 pkgs.libreswan
144 pkgs.iproute2
145 ];
146 systemd.packages = [ pkgs.libreswan ];
147 systemd.tmpfiles.packages = [ pkgs.libreswan ];
148
149 # Install configuration files
150 environment.etc = {
151 "ipsec.secrets".text = ''
152 include ${pkgs.libreswan}/etc/ipsec.secrets
153 '';
154 "ipsec.conf".source = "${pkgs.libreswan}/etc/ipsec.conf";
155 "ipsec.d/01-nixos.conf".source = configFile;
156 } // policyFiles;
157
158 systemd.services.ipsec = {
159 description = "Internet Key Exchange (IKE) Protocol Daemon for IPsec";
160 wantedBy = [ "multi-user.target" ];
161 restartTriggers = [ configFile ] ++ lib.mapAttrsToList (n: v: v.source) policyFiles;
162 path = with pkgs; [
163 libreswan
164 iproute2
165 procps
166 nssTools
167 iptables
168 nettools
169 ];
170 preStart = lib.optionalString cfg.disableRedirects ''
171 # Disable send/receive redirects
172 echo 0 | tee /proc/sys/net/ipv4/conf/*/send_redirects
173 echo 0 | tee /proc/sys/net/ipv{4,6}/conf/*/accept_redirects
174 '';
175 serviceConfig = {
176 StateDirectory = "ipsec/nss";
177 StateDirectoryMode = 700;
178 };
179 };
180
181 };
182
183}