at 17.09-beta 5.1 kB view raw
1{ config, lib, pkgs, ... }: 2with lib; 3 4let 5 cfg = config.services.dnscrypt-wrapper; 6 dataDir = "/var/lib/dnscrypt-wrapper"; 7 8 daemonArgs = with cfg; [ 9 "--listen-address=${address}:${toString port}" 10 "--resolver-address=${upstream.address}:${toString upstream.port}" 11 "--provider-name=${providerName}" 12 "--provider-publickey-file=public.key" 13 "--provider-secretkey-file=secret.key" 14 "--provider-cert-file=${providerName}.crt" 15 "--crypt-secretkey-file=${providerName}.key" 16 ]; 17 18 genKeys = '' 19 # generates time-limited keypairs 20 keyGen() { 21 dnscrypt-wrapper --gen-crypt-keypair \ 22 --crypt-secretkey-file=${cfg.providerName}.key 23 24 dnscrypt-wrapper --gen-cert-file \ 25 --crypt-secretkey-file=${cfg.providerName}.key \ 26 --provider-cert-file=${cfg.providerName}.crt \ 27 --provider-publickey-file=public.key \ 28 --provider-secretkey-file=secret.key \ 29 --cert-file-expire-days=${toString cfg.keys.expiration} 30 } 31 32 cd ${dataDir} 33 34 # generate provider keypair (first run only) 35 if [ ! -f public.key ] || [ ! -f secret.key ]; then 36 dnscrypt-wrapper --gen-provider-keypair 37 fi 38 39 # generate new keys for rotation 40 if [ ! -f ${cfg.providerName}.key ] || [ ! -f ${cfg.providerName}.crt ]; then 41 keyGen 42 fi 43 ''; 44 45 rotateKeys = '' 46 # check if keys are not expired 47 keyValid() { 48 fingerprint=$(dnscrypt-wrapper --show-provider-publickey-fingerprint | awk '{print $(NF)}') 49 dnscrypt-proxy --test=${toString (cfg.keys.checkInterval + 1)} \ 50 --resolver-address=127.0.0.1:${toString cfg.port} \ 51 --provider-name=${cfg.providerName} \ 52 --provider-key=$fingerprint 53 } 54 55 cd ${dataDir} 56 57 # archive old keys and restart the service 58 if ! keyValid; then 59 mkdir -p oldkeys 60 mv ${cfg.providerName}.key oldkeys/${cfg.providerName}-$(date +%F-%T).key 61 mv ${cfg.providerName}.crt oldkeys/${cfg.providerName}-$(date +%F-%T).crt 62 systemctl restart dnscrypt-wrapper 63 fi 64 ''; 65 66in { 67 68 69 ###### interface 70 71 options.services.dnscrypt-wrapper = { 72 enable = mkEnableOption "DNSCrypt wrapper"; 73 74 address = mkOption { 75 type = types.str; 76 default = "127.0.0.1"; 77 description = '' 78 The DNSCrypt wrapper will bind to this IP address. 79 ''; 80 }; 81 82 port = mkOption { 83 type = types.int; 84 default = 5353; 85 description = '' 86 The DNSCrypt wrapper will listen for DNS queries on this port. 87 ''; 88 }; 89 90 providerName = mkOption { 91 type = types.str; 92 default = "2.dnscrypt-cert.${config.networking.hostName}"; 93 example = "2.dnscrypt-cert.myresolver"; 94 description = '' 95 The name that will be given to this DNSCrypt resolver. 96 Note: the resolver name must start with <literal>2.dnscrypt-cert.</literal>. 97 ''; 98 }; 99 100 upstream.address = mkOption { 101 type = types.str; 102 default = "127.0.0.1"; 103 description = '' 104 The IP address of the upstream DNS server DNSCrypt will "wrap". 105 ''; 106 }; 107 108 upstream.port = mkOption { 109 type = types.int; 110 default = 53; 111 description = '' 112 The port of the upstream DNS server DNSCrypt will "wrap". 113 ''; 114 }; 115 116 keys.expiration = mkOption { 117 type = types.int; 118 default = 30; 119 description = '' 120 The duration (in days) of the time-limited secret key. 121 This will be automatically rotated before expiration. 122 ''; 123 }; 124 125 keys.checkInterval = mkOption { 126 type = types.int; 127 default = 1440; 128 description = '' 129 The time interval (in minutes) between key expiration checks. 130 ''; 131 }; 132 133 }; 134 135 136 ###### implementation 137 138 config = mkIf cfg.enable { 139 140 users.users.dnscrypt-wrapper = { 141 description = "dnscrypt-wrapper daemon user"; 142 home = "${dataDir}"; 143 createHome = true; 144 }; 145 users.groups.dnscrypt-wrapper = { }; 146 147 148 systemd.services.dnscrypt-wrapper = { 149 description = "dnscrypt-wrapper daemon"; 150 after = [ "network.target" ]; 151 wantedBy = [ "multi-user.target" ]; 152 path = [ pkgs.dnscrypt-wrapper ]; 153 154 serviceConfig = { 155 User = "dnscrypt-wrapper"; 156 WorkingDirectory = dataDir; 157 Restart = "on-failure"; 158 ExecStart = "${pkgs.dnscrypt-wrapper}/bin/dnscrypt-wrapper ${toString daemonArgs}"; 159 }; 160 161 preStart = genKeys; 162 }; 163 164 165 systemd.services.dnscrypt-wrapper-rotate = { 166 after = [ "network.target" ]; 167 requires = [ "dnscrypt-wrapper.service" ]; 168 description = "Rotates DNSCrypt wrapper keys if soon to expire"; 169 170 path = with pkgs; [ dnscrypt-wrapper dnscrypt-proxy gawk ]; 171 script = rotateKeys; 172 }; 173 174 175 systemd.timers.dnscrypt-wrapper-rotate = { 176 description = "Periodically check DNSCrypt wrapper keys for expiration"; 177 wantedBy = [ "multi-user.target" ]; 178 179 timerConfig = { 180 Unit = "dnscrypt-wrapper-rotate.service"; 181 OnBootSec = "1min"; 182 OnUnitActiveSec = cfg.keys.checkInterval * 60; 183 }; 184 }; 185 186 }; 187}