summary refs log tree commit diff
path: root/nixos/modules/services/networking/strongswan.nix
blob: e3a97207be7f52f70826207ada6e85ce4edb7eeb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
{ config, lib, pkgs, ... }:

let

  inherit (builtins) toFile;
  inherit (lib) concatMapStringsSep concatStringsSep mapAttrsToList
                mkIf mkEnableOption mkOption types literalExpression;

  cfg = config.services.strongswan;

  ipsecSecrets = secrets: toFile "ipsec.secrets" (
    concatMapStringsSep "\n" (f: "include ${f}") secrets
  );

  ipsecConf = {setup, connections, ca}:
    let
      # https://wiki.strongswan.org/projects/strongswan/wiki/IpsecConf
      makeSections = type: sections: concatStringsSep "\n\n" (
        mapAttrsToList (sec: attrs:
          "${type} ${sec}\n" +
            (concatStringsSep "\n" ( mapAttrsToList (k: v: "  ${k}=${v}") attrs ))
        ) sections
      );
      setupConf       = makeSections "config" { inherit setup; };
      connectionsConf = makeSections "conn" connections;
      caConf          = makeSections "ca" ca;

    in
    builtins.toFile "ipsec.conf" ''
      ${setupConf}
      ${connectionsConf}
      ${caConf}
    '';

  strongswanConf = {setup, connections, ca, secretsFile, managePlugins, enabledPlugins}: toFile "strongswan.conf" ''
    charon {
      ${if managePlugins then "load_modular = no" else ""}
      ${if managePlugins then ("load = " + (concatStringsSep " " enabledPlugins)) else ""}
      plugins {
        stroke {
          secrets_file = ${secretsFile}
        }
      }
    }

    starter {
      config_file = ${ipsecConf { inherit setup connections ca; }}
    }
  '';

in
{
  options.services.strongswan = {
    enable = mkEnableOption "strongSwan";

    secrets = mkOption {
      type = types.listOf types.str;
      default = [];
      example = [ "/run/keys/ipsec-foo.secret" ];
      description = ''
        A list of paths to IPSec secret files. These
        files will be included into the main ipsec.secrets file with
        the <literal>include</literal> directive. It is safer if these
        paths are absolute.
      '';
    };

    setup = mkOption {
      type = types.attrsOf types.str;
      default = {};
      example = { cachecrls = "yes"; strictcrlpolicy = "yes"; };
      description = ''
        A set of options for the ‘config setup’ section of the
        <filename>ipsec.conf</filename> file. Defines general
        configuration parameters.
      '';
    };

    connections = mkOption {
      type = types.attrsOf (types.attrsOf types.str);
      default = {};
      example = literalExpression ''
        {
          "%default" = {
            keyexchange = "ikev2";
            keyingtries = "1";
          };
          roadwarrior = {
            auto       = "add";
            leftcert   = "/run/keys/moonCert.pem";
            leftid     = "@moon.strongswan.org";
            leftsubnet = "10.1.0.0/16";
            right      = "%any";
          };
        }
      '';
      description = ''
        A set of connections and their options for the ‘conn xxx’
        sections of the <filename>ipsec.conf</filename> file.
      '';
    };

    ca = mkOption {
      type = types.attrsOf (types.attrsOf types.str);
      default = {};
      example = {
        strongswan = {
          auto   = "add";
          cacert = "/run/keys/strongswanCert.pem";
          crluri = "http://crl2.strongswan.org/strongswan.crl";
        };
      };
      description = ''
        A set of CAs (certification authorities) and their options for
        the ‘ca xxx’ sections of the <filename>ipsec.conf</filename>
        file.
      '';
    };

    managePlugins = mkOption {
      type = types.bool;
      default = false;
      description = ''
        If set to true, this option will disable automatic plugin loading and
        then tell strongSwan to enable the plugins specified in the
        <option>enabledPlugins</option> option.
      '';
    };

    enabledPlugins = mkOption {
      type = types.listOf types.str;
      default = [];
      description = ''
        A list of additional plugins to enable if
        <option>managePlugins</option> is true.
      '';
    };
  };


  config = with cfg;
  let
    secretsFile = ipsecSecrets cfg.secrets;
  in
  mkIf enable
    {

    # here we should use the default strongswan ipsec.secrets and
    # append to it (default one is empty so not a pb for now)
    environment.etc."ipsec.secrets".source = secretsFile;

    systemd.services.strongswan = {
      description = "strongSwan IPSec Service";
      wantedBy = [ "multi-user.target" ];
      path = with pkgs; [ kmod iproute2 iptables util-linux ]; # XXX Linux
      after = [ "network-online.target" ];
      environment = {
        STRONGSWAN_CONF = strongswanConf { inherit setup connections ca secretsFile managePlugins enabledPlugins; };
      };
      serviceConfig = {
        ExecStart  = "${pkgs.strongswan}/sbin/ipsec start --nofork";
      };
      preStart = ''
        # with 'nopeerdns' setting, ppp writes into this folder
        mkdir -m 700 -p /etc/ppp
      '';
    };
  };
}