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.
This commit is contained in:
parent
415c9ff90b
commit
a932f68d9c
54
nixos/modules/virtualisation/openstack/common.nix
Normal file
54
nixos/modules/virtualisation/openstack/common.nix
Normal file
@ -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.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};});
|
||||||
|
};
|
||||||
|
}
|
@ -1,22 +1,25 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
with lib;
|
with lib; with import ./common.nix {inherit lib;};
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.virtualisation.openstack.keystone;
|
cfg = config.virtualisation.openstack.keystone;
|
||||||
keystoneConf = pkgs.writeText "keystone.conf" ''
|
keystoneConfTpl = pkgs.writeText "keystone.conf" ''
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
admin_token = ${cfg.adminToken}
|
admin_token = ${cfg.adminToken.pattern}
|
||||||
policy_file=${cfg.package}/etc/policy.json
|
policy_file=${cfg.package}/etc/policy.json
|
||||||
|
|
||||||
[database]
|
[database]
|
||||||
connection = ${cfg.databaseConnection}
|
|
||||||
|
connection = "mysql://${cfg.database.user}:${cfg.database.password.pattern}@${cfg.database.host}/${cfg.database.name}"
|
||||||
|
|
||||||
[paste_deploy]
|
[paste_deploy]
|
||||||
config_file = ${cfg.package}/etc/keystone-paste.ini
|
config_file = ${cfg.package}/etc/keystone-paste.ini
|
||||||
|
|
||||||
${cfg.extraConfig}
|
${cfg.extraConfig}
|
||||||
'';
|
'';
|
||||||
|
keystoneConf = "/var/lib/keystone/keystone.conf";
|
||||||
|
|
||||||
in {
|
in {
|
||||||
options.virtualisation.openstack.keystone = {
|
options.virtualisation.openstack.keystone = {
|
||||||
package = mkOption {
|
package = mkOption {
|
||||||
@ -44,9 +47,8 @@ in {
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
adminToken = mkOption {
|
adminToken = mkSecretOption {
|
||||||
type = types.str;
|
name = "adminToken";
|
||||||
default = "mySuperToken";
|
|
||||||
description = ''
|
description = ''
|
||||||
This is the admin token used to boostrap keystone,
|
This is the admin token used to boostrap keystone,
|
||||||
ie. to provision first resources.
|
ie. to provision first resources.
|
||||||
@ -87,9 +89,8 @@ in {
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
adminPassword = mkOption {
|
adminPassword = mkSecretOption {
|
||||||
type = types.str;
|
name = "keystoneAdminPassword";
|
||||||
default = "admin";
|
|
||||||
description = ''
|
description = ''
|
||||||
The keystone admin user's password.
|
The keystone admin user's password.
|
||||||
'';
|
'';
|
||||||
@ -104,13 +105,34 @@ in {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
databaseConnection = mkOption {
|
database = {
|
||||||
|
host = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = mysql://keystone:keystone@localhost/keystone;
|
default = "localhost";
|
||||||
description = ''
|
description = ''
|
||||||
The SQLAlchemy connection string to use to connect to the
|
Host of the database.
|
||||||
Keystone database.
|
|
||||||
'';
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "keystone";
|
||||||
|
description = ''
|
||||||
|
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 = {
|
systemd.services.keystone-all = {
|
||||||
description = "OpenStack Keystone Daemon";
|
description = "OpenStack Keystone Daemon";
|
||||||
packages = [ mysql ];
|
|
||||||
after = [ "network.target"];
|
after = [ "network.target"];
|
||||||
path = [ cfg.package pkgs.mysql pkgs.curl pkgs.pythonPackages.keystoneclient pkgs.gawk ];
|
path = [ cfg.package pkgs.mysql pkgs.curl pkgs.pythonPackages.keystoneclient pkgs.gawk ];
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
preStart = ''
|
preStart = ''
|
||||||
mkdir -m 755 -p /var/lib/keystone
|
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
|
# Initialise the database
|
||||||
${cfg.package}/bin/keystone-manage --config-file=${keystoneConf} db_sync
|
${cfg.package}/bin/keystone-manage --config-file=${keystoneConf} db_sync
|
||||||
# Set up the keystone's PKI infrastructure
|
# Set up the keystone's PKI infrastructure
|
||||||
@ -162,7 +191,7 @@ in {
|
|||||||
|
|
||||||
# We use the service token to create a first admin user
|
# We use the service token to create a first admin user
|
||||||
export OS_SERVICE_ENDPOINT=http://localhost:35357/v2.0
|
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
|
# If the tenant service doesn't exist, we consider
|
||||||
# keystone is not initialized
|
# keystone is not initialized
|
||||||
@ -170,7 +199,7 @@ in {
|
|||||||
then
|
then
|
||||||
keystone tenant-create --name service
|
keystone tenant-create --name service
|
||||||
keystone tenant-create --name ${cfg.bootstrap.adminTenant}
|
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 admin
|
||||||
keystone role-create --name Member
|
keystone role-create --name Member
|
||||||
keystone user-role-add --tenant ${cfg.bootstrap.adminTenant} --user ${cfg.bootstrap.adminUsername} --role admin
|
keystone user-role-add --tenant ${cfg.bootstrap.adminTenant} --user ${cfg.bootstrap.adminUsername} --role admin
|
||||||
|
@ -4,13 +4,17 @@ with import ../lib/testing.nix { inherit system; };
|
|||||||
with pkgs.lib;
|
with pkgs.lib;
|
||||||
|
|
||||||
let
|
let
|
||||||
|
keystoneMysqlPassword = "keystoneMysqlPassword";
|
||||||
|
keystoneMysqlPasswordFile = "/var/run/keystoneMysqlPassword";
|
||||||
|
keystoneAdminPassword = "keystoneAdminPassword";
|
||||||
|
|
||||||
createKeystoneDb = pkgs.writeText "create-keystone-db.sql" ''
|
createKeystoneDb = pkgs.writeText "create-keystone-db.sql" ''
|
||||||
create database keystone;
|
create database keystone;
|
||||||
GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'localhost' IDENTIFIED BY 'keystone';
|
GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'localhost' IDENTIFIED BY '${keystoneMysqlPassword}';
|
||||||
GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%' IDENTIFIED BY 'keystone';
|
GRANT ALL PRIVILEGES ON keystone.* TO 'keystone'@'%' IDENTIFIED BY '${keystoneMysqlPassword}';
|
||||||
'';
|
'';
|
||||||
# The admin keystone account
|
# The admin keystone account
|
||||||
adminOpenstackCmd = "OS_TENANT_NAME=admin OS_USERNAME=admin OS_PASSWORD=admin OS_AUTH_URL=http://localhost:5000/v3 OS_IDENTITY_API_VERSION=3 openstack";
|
adminOpenstackCmd = "OS_TENANT_NAME=admin OS_USERNAME=admin OS_PASSWORD=${keystoneAdminPassword} OS_AUTH_URL=http://localhost:5000/v3 OS_IDENTITY_API_VERSION=3 openstack";
|
||||||
# The created demo keystone account
|
# The created demo keystone account
|
||||||
demoOpenstackCmd = "OS_TENANT_NAME=demo OS_USERNAME=demo OS_PASSWORD=demo OS_AUTH_URL=http://localhost:5000/v3 OS_IDENTITY_API_VERSION=3 openstack";
|
demoOpenstackCmd = "OS_TENANT_NAME=demo OS_USERNAME=demo OS_PASSWORD=demo OS_AUTH_URL=http://localhost:5000/v3 OS_IDENTITY_API_VERSION=3 openstack";
|
||||||
|
|
||||||
@ -18,12 +22,34 @@ in makeTest {
|
|||||||
machine =
|
machine =
|
||||||
{ config, pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
{
|
{
|
||||||
|
# This is to simulate nixops deployment process.
|
||||||
|
# https://nixos.org/nixops/manual/#opt-deployment.keys
|
||||||
|
boot.postBootCommands = "echo ${keystoneMysqlPassword} > ${keystoneMysqlPasswordFile}";
|
||||||
|
|
||||||
services.mysql.enable = true;
|
services.mysql.enable = true;
|
||||||
services.mysql.initialScript = createKeystoneDb;
|
services.mysql.initialScript = createKeystoneDb;
|
||||||
|
|
||||||
virtualisation = {
|
virtualisation = {
|
||||||
openstack.keystone.enable = true;
|
|
||||||
openstack.keystone.bootstrap.enable = true;
|
openstack.keystone = {
|
||||||
|
enable = true;
|
||||||
|
# Check if we can get the secret from a file
|
||||||
|
database.password = {
|
||||||
|
value = keystoneMysqlPasswordFile;
|
||||||
|
storage = "fromFile";
|
||||||
|
};
|
||||||
|
adminToken = {
|
||||||
|
value = "adminToken";
|
||||||
|
storage = "fromNixStore";
|
||||||
|
};
|
||||||
|
|
||||||
|
bootstrap.enable = true;
|
||||||
|
# Check if we can get the secret from the store
|
||||||
|
bootstrap.adminPassword = {
|
||||||
|
value = keystoneAdminPassword;
|
||||||
|
storage = "fromNixStore";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
memorySize = 2096;
|
memorySize = 2096;
|
||||||
diskSize = 4 * 1024;
|
diskSize = 4 * 1024;
|
||||||
|
Loading…
Reference in New Issue
Block a user