1{ config, lib, pkgs, ... }:
2
3with lib; with import ./common.nix {inherit lib;};
4
5let
6 cfg = config.virtualisation.openstack.keystone;
7 keystoneConfTpl = pkgs.writeText "keystone.conf" ''
8 [DEFAULT]
9 admin_token = ${cfg.adminToken.pattern}
10 policy_file=${cfg.package}/etc/policy.json
11
12 [database]
13
14 connection = "mysql://${cfg.database.user}:${cfg.database.password.pattern}@${cfg.database.host}/${cfg.database.name}"
15
16 [paste_deploy]
17 config_file = ${cfg.package}/etc/keystone-paste.ini
18
19 ${cfg.extraConfig}
20 '';
21 keystoneConf = "/var/lib/keystone/keystone.conf";
22
23in {
24 options.virtualisation.openstack.keystone = {
25 package = mkOption {
26 type = types.package;
27 example = literalExample "pkgs.keystone";
28 description = ''
29 Keystone package to use.
30 '';
31 };
32
33 enable = mkOption {
34 default = false;
35 type = types.bool;
36 description = ''
37 Enable Keystone, the OpenStack Identity Service
38 '';
39 };
40
41 extraConfig = mkOption {
42 default = "";
43 type = types.lines;
44 description = ''
45 Additional text appended to <filename>keystone.conf</filename>,
46 the main Keystone configuration file.
47 '';
48 };
49
50 adminToken = mkSecretOption {
51 name = "adminToken";
52 description = ''
53 This is the admin token used to boostrap keystone,
54 ie. to provision first resources.
55 '';
56 };
57
58 bootstrap = {
59 enable = mkOption {
60 default = false;
61 type = types.bool;
62 description = ''
63 Bootstrap the Keystone service by creating the service
64 tenant, an admin account and a public endpoint. This options
65 provides a ready-to-use admin account. This is only done at
66 the first Keystone execution by the systemd post start.
67
68 Note this option is a helper for setting up development or
69 testing environments.
70 '';
71 };
72
73 endpointPublic = mkOption {
74 type = types.str;
75 default = "http://localhost:5000/v2.0";
76 description = ''
77 The public identity endpoint. The link <link
78 xlink:href="http://docs.openstack.org/liberty/install-guide-rdo/keystone-services.html">
79 create keystone endpoint</link> provides more informations
80 about that.
81 '';
82 };
83
84 adminUsername = mkOption {
85 type = types.str;
86 default = "admin";
87 description = ''
88 A keystone admin username.
89 '';
90 };
91
92 adminPassword = mkSecretOption {
93 name = "keystoneAdminPassword";
94 description = ''
95 The keystone admin user's password.
96 '';
97 };
98
99 adminTenant = mkOption {
100 type = types.str;
101 default = "admin";
102 description = ''
103 A keystone admin tenant name.
104 '';
105 };
106 };
107
108 database = {
109 host = mkOption {
110 type = types.str;
111 default = "localhost";
112 description = ''
113 Host of the database.
114 '';
115 };
116
117 name = mkOption {
118 type = types.str;
119 default = "keystone";
120 description = ''
121 Name of the existing database.
122 '';
123 };
124
125 user = mkOption {
126 type = types.str;
127 default = "keystone";
128 description = ''
129 The database user. The user must exist and has access to
130 the specified database.
131 '';
132 };
133 password = mkSecretOption {
134 name = "mysqlPassword";
135 description = "The database user's password";};
136 };
137 };
138
139 config = mkIf cfg.enable {
140 # Note: when changing the default, make it conditional on
141 # ‘system.stateVersion’ to maintain compatibility with existing
142 # systems!
143 virtualisation.openstack.keystone.package = mkDefault pkgs.keystone;
144
145 users.extraUsers = [{
146 name = "keystone";
147 group = "keystone";
148 uid = config.ids.uids.keystone;
149 }];
150 users.extraGroups = [{
151 name = "keystone";
152 gid = config.ids.gids.keystone;
153 }];
154
155 systemd.services.keystone-all = {
156 description = "OpenStack Keystone Daemon";
157 after = [ "network.target"];
158 path = [ cfg.package pkgs.mysql pkgs.curl pkgs.pythonPackages.keystoneclient pkgs.gawk ];
159 wantedBy = [ "multi-user.target" ];
160 preStart = ''
161 mkdir -m 755 -p /var/lib/keystone
162
163 cp ${keystoneConfTpl} ${keystoneConf};
164 chown keystone:keystone ${keystoneConf};
165 chmod 640 ${keystoneConf}
166
167 ${replaceSecret cfg.database.password keystoneConf}
168 ${replaceSecret cfg.adminToken keystoneConf}
169
170 # Initialise the database
171 ${cfg.package}/bin/keystone-manage --config-file=${keystoneConf} db_sync
172 # Set up the keystone's PKI infrastructure
173 ${cfg.package}/bin/keystone-manage --config-file=${keystoneConf} pki_setup --keystone-user keystone --keystone-group keystone
174 '';
175 postStart = optionalString cfg.bootstrap.enable ''
176 set -eu
177 # Wait until the keystone is available for use
178 count=0
179 while ! curl --fail -s http://localhost:35357/v2.0 > /dev/null
180 do
181 if [ $count -eq 30 ]
182 then
183 echo "Tried 30 times, giving up..."
184 exit 1
185 fi
186
187 echo "Keystone not yet started. Waiting for 1 second..."
188 count=$((count++))
189 sleep 1
190 done
191
192 # We use the service token to create a first admin user
193 export OS_SERVICE_ENDPOINT=http://localhost:35357/v2.0
194 export OS_SERVICE_TOKEN=${getSecret cfg.adminToken}
195
196 # If the tenant service doesn't exist, we consider
197 # keystone is not initialized
198 if ! keystone tenant-get service
199 then
200 keystone tenant-create --name service
201 keystone tenant-create --name ${cfg.bootstrap.adminTenant}
202 keystone user-create --name ${cfg.bootstrap.adminUsername} --tenant ${cfg.bootstrap.adminTenant} --pass ${getSecret cfg.bootstrap.adminPassword}
203 keystone role-create --name admin
204 keystone role-create --name Member
205 keystone user-role-add --tenant ${cfg.bootstrap.adminTenant} --user ${cfg.bootstrap.adminUsername} --role admin
206 keystone service-create --type identity --name keystone
207 ID=$(keystone service-get keystone | awk '/ id / { print $4 }')
208 keystone endpoint-create --region RegionOne --service $ID --publicurl ${cfg.bootstrap.endpointPublic} --adminurl http://localhost:35357/v2.0 --internalurl http://localhost:5000/v2.0
209 fi
210 '';
211 serviceConfig = {
212 PermissionsStartOnly = true; # preStart must be run as root
213 TimeoutStartSec = "600"; # 10min for initial db migrations
214 User = "keystone";
215 Group = "keystone";
216 ExecStart = "${cfg.package}/bin/keystone-all --config-file=${keystoneConf}";
217 };
218 };
219 };
220}