summary refs log tree commit diff
diff options
context:
space:
mode:
authorrnhmjoj <rnhmjoj@inventati.org>2021-08-17 00:05:01 +0200
committerrnhmjoj <rnhmjoj@inventati.org>2021-09-29 09:10:39 +0200
commit52b9dd7bf69f2b333bfd1a1e6cbb7d187471054b (patch)
tree914926edce05f4f2cfea2c409bcd998bba732bd4
parentd24ebde667ab885de4f6cd81bd4f2916f8621f9c (diff)
downloadnixpkgs-52b9dd7bf69f2b333bfd1a1e6cbb7d187471054b.tar
nixpkgs-52b9dd7bf69f2b333bfd1a1e6cbb7d187471054b.tar.gz
nixpkgs-52b9dd7bf69f2b333bfd1a1e6cbb7d187471054b.tar.bz2
nixpkgs-52b9dd7bf69f2b333bfd1a1e6cbb7d187471054b.tar.lz
nixpkgs-52b9dd7bf69f2b333bfd1a1e6cbb7d187471054b.tar.xz
nixpkgs-52b9dd7bf69f2b333bfd1a1e6cbb7d187471054b.tar.zst
nixpkgs-52b9dd7bf69f2b333bfd1a1e6cbb7d187471054b.zip
nixos/wpa_supplicant: add safe secret handling
-rw-r--r--nixos/modules/services/networking/wpa_supplicant.nix110
1 files changed, 96 insertions, 14 deletions
diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix
index 155c6fdd0ab..904a3db493b 100644
--- a/nixos/modules/services/networking/wpa_supplicant.nix
+++ b/nixos/modules/services/networking/wpa_supplicant.nix
@@ -20,10 +20,16 @@ let
     ++ optional cfg.scanOnLowSignal ''bgscan="simple:30:-70:3600"''
     ++ optional (cfg.extraConfig != "") cfg.extraConfig);
 
+  configIsGenerated = with cfg;
+    networks != {} || extraConfig != "" || userControlled.enable;
+
+  # the original configuration file
   configFile =
-    if cfg.networks != {} || cfg.extraConfig != "" || cfg.userControlled.enable
+    if configIsGenerated
       then pkgs.writeText "wpa_supplicant.conf" generatedConfig
       else "/etc/wpa_supplicant.conf";
+  # the config file with environment variables replaced
+  finalConfig = ''"$RUNTIME_DIRECTORY"/wpa_supplicant.conf'';
 
   # Creates a network block for wpa_supplicant.conf
   mkNetwork = ssid: opts:
@@ -56,8 +62,8 @@ let
     let
       deviceUnit = optional (iface != null) "sys-subsystem-net-devices-${utils.escapeSystemdPath iface}.device";
       configStr = if cfg.allowAuxiliaryImperativeNetworks
-        then "-c /etc/wpa_supplicant.conf -I ${configFile}"
-        else "-c ${configFile}";
+        then "-c /etc/wpa_supplicant.conf -I ${finalConfig}"
+        else "-c ${finalConfig}";
     in {
       description = "WPA Supplicant instance" + optionalString (iface != null) " for interface ${iface}";
 
@@ -69,12 +75,25 @@ let
       stopIfChanged = false;
 
       path = [ package ];
+      serviceConfig.RuntimeDirectory = "wpa_supplicant";
+      serviceConfig.RuntimeDirectoryMode = "700";
+      serviceConfig.EnvironmentFile = mkIf (cfg.environmentFile != null)
+        (builtins.toString cfg.environmentFile);
 
       script =
       ''
-        if [ -f /etc/wpa_supplicant.conf -a "/etc/wpa_supplicant.conf" != "${configFile}" ]; then
-          echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead."
-        fi
+        ${optionalString configIsGenerated ''
+          if [ -f /etc/wpa_supplicant.conf ]; then
+            echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead."
+          fi
+        ''}
+
+        # substitute environment variables
+        ${pkgs.gawk}/bin/awk '{
+          for(varname in ENVIRON)
+            gsub("@"varname"@", ENVIRON[varname])
+          print
+        }' "${configFile}" > "${finalConfig}"
 
         iface_args="-s ${optionalString cfg.dbusControlled "-u"} -D${cfg.driver} ${configStr}"
 
@@ -155,6 +174,44 @@ in {
         '';
       };
 
+      environmentFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/run/secrets/wireless.env";
+        description = ''
+          File consisting of lines of the form <literal>varname=value</literal>
+          to define variables for the wireless configuration.
+
+          See section "EnvironmentFile=" in <citerefentry>
+          <refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum>
+          </citerefentry> for a syntax reference.
+
+          Secrets (PSKs, passwords, etc.) can be provided without adding them to
+          the world-readable Nix store by defining them in the environment file and
+          referring to them in option <option>networking.wireless.networks</option>
+          with the syntax <literal>@varname@</literal>. Example:
+
+          <programlisting>
+          # content of /run/secrets/wireless.env
+          PSK_HOME=mypassword
+          PASS_WORK=myworkpassword
+          </programlisting>
+
+          <programlisting>
+          # wireless-related configuration
+          networking.wireless.environmentFile = "/run/secrets/wireless.env";
+          networking.wireless.networks = {
+            home.psk = "@PSK_HOME@";
+            work.auth = '''
+              eap=PEAP
+              identity="my-user@example.com"
+              password="@PASS_WORK@"
+            ''';
+          };
+          </programlisting>
+        '';
+      };
+
       networks = mkOption {
         type = types.attrsOf (types.submodule {
           options = {
@@ -165,10 +222,14 @@ in {
                 The network's pre-shared key in plaintext defaulting
                 to being a network without any authentication.
 
-                Be aware that these will be written to the nix store
-                in plaintext!
+                <warning><para>
+                  Be aware that this will be written to the nix store
+                  in plaintext! Use an environment variable instead.
+                </para></warning>
 
-                Mutually exclusive with <varname>pskRaw</varname>.
+                <note><para>
+                  Mutually exclusive with <varname>pskRaw</varname>.
+                </para></note>
               '';
             };
 
@@ -179,7 +240,14 @@ in {
                 The network's pre-shared key in hex defaulting
                 to being a network without any authentication.
 
-                Mutually exclusive with <varname>psk</varname>.
+                <warning><para>
+                  Be aware that this will be written to the nix store
+                  in plaintext! Use an environment variable instead.
+                </para></warning>
+
+                <note><para>
+                  Mutually exclusive with <varname>psk</varname>.
+                </para></note>
               '';
             };
 
@@ -231,7 +299,7 @@ in {
               example = ''
                 eap=PEAP
                 identity="user@example.com"
-                password="secret"
+                password="@EXAMPLE_PASSWORD@"
               '';
               description = ''
                 Use this option to configure advanced authentication methods like EAP.
@@ -242,7 +310,15 @@ in {
                 </citerefentry>
                 for example configurations.
 
-                Mutually exclusive with <varname>psk</varname> and <varname>pskRaw</varname>.
+                <warning><para>
+                  Be aware that this will be written to the nix store
+                  in plaintext! Use an environment variable for secrets.
+                </para></warning>
+
+                <note><para>
+                  Mutually exclusive with <varname>psk</varname> and
+                  <varname>pskRaw</varname>.
+                </para></note>
               '';
             };
 
@@ -303,11 +379,17 @@ in {
         default = {};
         example = literalExample ''
           { echelon = {                   # SSID with no spaces or special characters
-              psk = "abcdefgh";
+              psk = "abcdefgh";           # (password will be written to /nix/store!)
             };
+
+            echelon = {                   # safe version of the above: read PSK from the
+              psk = "@PSK_ECHELON@";      # variable PSK_ECHELON, defined in environmentFile,
+            };                            # this won't leak into /nix/store
+
             "echelon's AP" = {            # SSID with spaces and/or special characters
-               psk = "ijklmnop";
+               psk = "ijklmnop";          # (password will be written to /nix/store!)
             };
+
             "free.wifi" = {};             # Public wireless network
           }
         '';