about summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
authorAntoine Eiche <lewo@abesis.fr>2016-12-10 23:14:50 +0100
committerJörg Thalheim <joerg@higgsboson.tk>2016-12-16 20:53:32 +0100
commita932f68d9c2a020e04b49f225310decf39bb34d0 (patch)
treed3da8cd0d0ce5439f556f16a1a898d5d45808f0a /nixos/modules
parent415c9ff90b4aa9f6452f618e60aa948ab94a93fb (diff)
downloadnixlib-a932f68d9c2a020e04b49f225310decf39bb34d0.tar
nixlib-a932f68d9c2a020e04b49f225310decf39bb34d0.tar.gz
nixlib-a932f68d9c2a020e04b49f225310decf39bb34d0.tar.bz2
nixlib-a932f68d9c2a020e04b49f225310decf39bb34d0.tar.lz
nixlib-a932f68d9c2a020e04b49f225310decf39bb34d0.tar.xz
nixlib-a932f68d9c2a020e04b49f225310decf39bb34d0.tar.zst
nixlib-a932f68d9c2a020e04b49f225310decf39bb34d0.zip
nixos/keystone: secrets can be read from files
A secret can be stored in a file. It is written at runtime in the
configuration file.
Note it is also possible to write them in the nix store for dev
purposes.
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/virtualisation/openstack/common.nix54
-rw-r--r--nixos/modules/virtualisation/openstack/keystone.nix63
2 files changed, 100 insertions, 17 deletions
diff --git a/nixos/modules/virtualisation/openstack/common.nix b/nixos/modules/virtualisation/openstack/common.nix
new file mode 100644
index 000000000000..3fce54a2fa59
--- /dev/null
+++ b/nixos/modules/virtualisation/openstack/common.nix
@@ -0,0 +1,54 @@
+{ lib }:
+
+with lib;
+
+rec {
+  # A shell script string helper to get the value of a secret at
+  # runtime.
+  getSecret = secretOption:
+    if secretOption.storage == "fromFile"
+    then ''$(cat ${secretOption.value})''
+    else ''${secretOption.value}'';
+
+
+  # A shell script string help to replace at runtime in a file the
+  # pattern of a secret by its value.
+  replaceSecret = secretOption: filename: ''
+    sed -i "s/${secretOption.pattern}/${getSecret secretOption}/g" ${filename}
+    '';
+
+  # This generates an option that can be used to declare secrets which
+  # can be stored in the nix store, or not. A pattern is written in
+  # the nix store to represent the secret. The pattern can
+  # then be overwritten with the value of the secret at runtime.
+  mkSecretOption = {name, description ? ""}:
+    mkOption {
+      description = description;
+      type = types.submodule ({
+        options = {
+          pattern = mkOption {
+            type = types.str;
+            default = "##${name}##";
+            description = "The pattern that represent the secret.";
+            };
+          storage = mkOption {
+            type = types.enum [ "fromNixStore" "fromFile" ];
+            description = ''
+            Choose the way the password is provisionned. If
+            fromNixStore is used, the value is the password and it is
+            written in the nix store. If fromFile is used, the value
+            is a path from where the password will be read at
+            runtime. This is generally used with <link
+            xlink:href="https://nixos.org/nixops/manual/#opt-deployment.keys">
+            deployment keys</link> of Nixops.
+           '';};
+            value = mkOption {
+              type = types.str;
+	      description = ''
+	      If the storage is fromNixStore, the value is the password itself,
+	      otherwise it is a path to the file that contains the password.
+	      '';
+	      };
+            };});
+  };
+}
diff --git a/nixos/modules/virtualisation/openstack/keystone.nix b/nixos/modules/virtualisation/openstack/keystone.nix
index 30bdb8690462..e32c5a4cae1b 100644
--- a/nixos/modules/virtualisation/openstack/keystone.nix
+++ b/nixos/modules/virtualisation/openstack/keystone.nix
@@ -1,22 +1,25 @@
 { config, lib, pkgs, ... }:
 
-with lib;
+with lib; with import ./common.nix {inherit lib;};
 
 let
   cfg = config.virtualisation.openstack.keystone;
-  keystoneConf = pkgs.writeText "keystone.conf" ''
+  keystoneConfTpl = pkgs.writeText "keystone.conf" ''
     [DEFAULT]
-    admin_token = ${cfg.adminToken}
+    admin_token = ${cfg.adminToken.pattern}
     policy_file=${cfg.package}/etc/policy.json
 
     [database]
-    connection = ${cfg.databaseConnection}
+
+    connection = "mysql://${cfg.database.user}:${cfg.database.password.pattern}@${cfg.database.host}/${cfg.database.name}"
 
     [paste_deploy]
     config_file = ${cfg.package}/etc/keystone-paste.ini
 
     ${cfg.extraConfig}
   '';
+  keystoneConf = "/var/lib/keystone/keystone.conf";
+
 in {
   options.virtualisation.openstack.keystone = {
     package = mkOption {
@@ -44,9 +47,8 @@ in {
       '';
     };
 
-    adminToken = mkOption {
-      type = types.str;
-      default = "mySuperToken";
+    adminToken = mkSecretOption {
+      name = "adminToken";
       description = ''
         This is the admin token used to boostrap keystone,
         ie. to provision first resources.
@@ -87,9 +89,8 @@ in {
         '';
       };
 
-      adminPassword = mkOption {
-        type = types.str;
-        default = "admin";
+      adminPassword = mkSecretOption {
+        name = "keystoneAdminPassword";
         description = ''
           The keystone admin user's password.
         '';
@@ -104,13 +105,34 @@ in {
       };
     };
 
-    databaseConnection = mkOption {
+    database = {
+      host = mkOption {
+        type = types.str;
+        default = "localhost";
+        description = ''
+          Host of the database.
+        '';
+      };
+
+      name = mkOption {
         type = types.str;
-        default = mysql://keystone:keystone@localhost/keystone;
+        default = "keystone";
         description = ''
-          The SQLAlchemy connection string to use to connect to the
-          Keystone database.
+          Name of the existing database.
         '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "keystone";
+        description = ''
+          The database user. The user must exist and has access to
+          the specified database.
+        '';
+      };
+      password = mkSecretOption {
+        name = "mysqlPassword";
+        description = "The database user's password";};
     };
   };
 
@@ -132,12 +154,19 @@ in {
 
     systemd.services.keystone-all = {
         description = "OpenStack Keystone Daemon";
-	packages = [ mysql ];
         after = [ "network.target"];
         path = [ cfg.package pkgs.mysql pkgs.curl pkgs.pythonPackages.keystoneclient pkgs.gawk ];
         wantedBy = [ "multi-user.target" ];
         preStart = ''
           mkdir -m 755 -p /var/lib/keystone
+
+          cp ${keystoneConfTpl} ${keystoneConf};
+          chown keystone:keystone ${keystoneConf};
+          chmod 640 ${keystoneConf}
+
+          ${replaceSecret cfg.database.password keystoneConf}
+          ${replaceSecret cfg.adminToken keystoneConf}
+
           # Initialise the database
           ${cfg.package}/bin/keystone-manage --config-file=${keystoneConf} db_sync
           # Set up the keystone's PKI infrastructure
@@ -162,7 +191,7 @@ in {
 
           # We use the service token to create a first admin user
           export OS_SERVICE_ENDPOINT=http://localhost:35357/v2.0
-          export OS_SERVICE_TOKEN=${cfg.adminToken}
+          export OS_SERVICE_TOKEN=${getSecret cfg.adminToken}
 
           # If the tenant service doesn't exist, we consider
           # keystone is not initialized
@@ -170,7 +199,7 @@ in {
           then
               keystone tenant-create --name service
               keystone tenant-create --name ${cfg.bootstrap.adminTenant}
-              keystone user-create --name ${cfg.bootstrap.adminUsername} --tenant ${cfg.bootstrap.adminTenant} --pass ${cfg.bootstrap.adminPassword}
+              keystone user-create --name ${cfg.bootstrap.adminUsername} --tenant ${cfg.bootstrap.adminTenant} --pass ${getSecret cfg.bootstrap.adminPassword}
               keystone role-create --name admin
               keystone role-create --name Member
               keystone user-role-add --tenant ${cfg.bootstrap.adminTenant} --user ${cfg.bootstrap.adminUsername} --role admin