Skip to content

Commit 2ab114b

Browse files
committed
feat(ansible): create playbook and roles for keystone bootstrapping
The current keystone bootstrap is a bash script which makes it hard to ensure that everything has been setup. By making it Ansible we can ensure that things are consistent. Unfortunately the openstack's role module does not support cross-domain or inherited like the CLI does so this hacks around it with ansible.builtin.command that then needs some hoops to avoid lint errors.
1 parent 7b77435 commit 2ab114b

File tree

9 files changed

+259
-93
lines changed

9 files changed

+259
-93
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
# Copyright (c) 2025 Rackspace Technology, Inc.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
- name: Keystone Bootstrap
17+
hosts: localhost
18+
19+
roles:
20+
- role: keystone_bootstrap

ansible/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ ansible-runner==2.4.0
33
openstacksdk==4.3.0
44
pynautobot==2.6.1
55
jmespath==1.0.1
6+
# remove me after the inherited roles workaround can be dropped
7+
python-openstackclient
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
keystone_bootstrap_dex_url: "{{ 'https://dex.' + lookup('ansible.builtin.env', 'DNS_ZONE', default='localnet') }}"
3+
4+
keystone_bootstrap_groups:
5+
- name: ucadmin
6+
desc: 'Users Federated with Admin'
7+
roles:
8+
- member
9+
- admin
10+
- name: ucuser
11+
desc: 'Regular Federated Users'
12+
roles:
13+
- member
14+
- name: ucneteng
15+
desc: 'Federated Network Engineers'
16+
roles:
17+
- member
18+
- name: ucdctech
19+
desc: 'Federated DC Technicians'
20+
roles:
21+
- member
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
# Copyright (c) 2025 Rackspace Technology, Inc.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
- name: Create 'infra' domain
17+
openstack.cloud.identity_domain:
18+
name: infra
19+
description: 'System Infra'
20+
state: present
21+
22+
- name: Create 'baremetal' project in 'infra' domain
23+
openstack.cloud.project:
24+
name: baremetal
25+
domain: infra
26+
description: 'Ironic Resources'
27+
state: present
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
3+
- name: Admin needs admin role for default domain
4+
openstack.cloud.role_assignment:
5+
user: "{{ lookup('ansible.builtin.env', 'OS_USERNAME', default=Undefined) }}"
6+
domain: "{{ lookup('ansible.builtin.env', 'OS_DEFAULT_DOMAIN', default=Undefined) }}"
7+
role: admin
8+
state: present
9+
10+
- name: Define baremetal
11+
ansible.builtin.include_tasks: baremetal.yml
12+
13+
- name: Define SSO
14+
ansible.builtin.include_tasks: sso.yml
15+
16+
- name: Define misc keystone
17+
ansible.builtin.include_tasks: misc.yml
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
---
2+
# Copyright (c) 2025 Rackspace Technology, Inc.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
- name: Create 'argoworkflow' user
17+
openstack.cloud.identity_user:
18+
name: argoworkflow
19+
password: demo
20+
domain: infra
21+
state: present
22+
23+
- name: Set 'argoworkflow' role
24+
openstack.cloud.role_assignment:
25+
domain: infra
26+
user: argoworkflow
27+
project: baremetal
28+
role: admin
29+
state: present
30+
31+
- name: Create 'monitoring' user
32+
openstack.cloud.identity_user:
33+
name: monitoring
34+
password: monitoring_demo
35+
domain: infra
36+
state: present
37+
38+
- name: Set 'monitoring' role
39+
openstack.cloud.role_assignment:
40+
domain: infra
41+
user: monitoring
42+
project: baremetal
43+
role: admin
44+
state: present
45+
46+
- name: Create 'flavorsync' user
47+
openstack.cloud.identity_user:
48+
name: flavorsync
49+
password: abcd1234
50+
domain: service
51+
state: present
52+
53+
- name: Create 'flavorsync' role
54+
openstack.cloud.identity_role:
55+
name: flavorsync
56+
state: present
57+
58+
- name: Set 'flavorsync' role
59+
openstack.cloud.role_assignment:
60+
user: flavorsync
61+
project: baremetal
62+
role: flavorsync
63+
state: present
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
---
2+
# Copyright (c) 2025 Rackspace Technology, Inc.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
- name: Create 'sso' domain
17+
openstack.cloud.identity_domain:
18+
name: sso
19+
description: 'SSO to dex'
20+
state: present
21+
register: _domain_sso
22+
23+
- name: Create 'sso' identity provider
24+
openstack.cloud.federation_idp:
25+
name: sso
26+
domain_id: "{{ _domain_sso.domain.id }}"
27+
description: 'Identity Provider to dex'
28+
remote_ids:
29+
- "{{ keystone_bootstrap_dex_url }}"
30+
31+
- name: Create sso mapping
32+
openstack.cloud.federation_mapping:
33+
name: sso_mapping
34+
rules:
35+
- local:
36+
- user:
37+
id: '{0}'
38+
name: '{1}'
39+
email: '{2}'
40+
groups: '{3}'
41+
domain:
42+
id: "{{ _domain_sso.domain.id }}"
43+
remote:
44+
- type: HTTP_OIDC_SUB
45+
- type: REMOTE_USER
46+
- type: HTTP_OIDC_EMAIL
47+
- type: HTTP_OIDC_GROUPS
48+
49+
- name: Create openid protocol
50+
openstack.cloud.keystone_federation_protocol:
51+
name: openid
52+
idp: sso
53+
mapping: sso_mapping
54+
55+
- name: Create federated group mappings
56+
loop: keystone_bootstrap_groups
57+
block:
58+
- name: Create group
59+
openstack.cloud.identity_group:
60+
name: "{{ item.name }}"
61+
domain_id: "{{ _domain_sso.domain.id }}"
62+
description: "{{ item.desc }}"
63+
state: present
64+
register: _group
65+
66+
# role assignment module is lacking inherited and cross domain assignments
67+
- name: Assign member access
68+
ansible.builtin.command: openstack role add --group "{{ _group.group.id }}" --domain default --inherited member
69+
when: dont_set_roles is not defined
70+
changed_when: false
71+
72+
- name: Grant admin for groups
73+
loop:
74+
- ucadmin
75+
block:
76+
- name: Find group
77+
openstack.cloud.identity_group_info:
78+
name: "{{ item }}"
79+
domain: "{{ _domain_sso.domain.id }}"
80+
81+
# role assignment module is lacking inherited and cross domain assignments
82+
- name: Assign member access
83+
ansible.builtin.command: openstack role add --group "{{ _group.group.id }}" --domain default --inherited admin
84+
when: dont_set_roles is not defined
85+
changed_when: false
86+
87+
# role assignment module is lacking inherited and cross domain assignments
88+
- name: Assign member access
89+
ansible.builtin.command: openstack role add --group "{{ _group.group.id }}" --domain infra --inherited admin
90+
when: dont_set_roles is not defined
91+
changed_when: false

components/images-openstack.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ images:
1414

1515
# keystone
1616
keystone_api: "ghcr.io/rackerlabs/understack/keystone:2024.2-ubuntu_jammy"
17-
keystone_bootstrap: "docker.io/openstackhelm/heat:2024.2-ubuntu_jammy"
17+
keystone_bootstrap: "ghcr.io/rackerlabs/understack/ansible:pr-806"
1818
keystone_credential_rotate: "ghcr.io/rackerlabs/understack/keystone:2024.2-ubuntu_jammy"
1919
keystone_credential_setup: "ghcr.io/rackerlabs/understack/keystone:2024.2-ubuntu_jammy"
2020
keystone_db_sync: "ghcr.io/rackerlabs/understack/keystone:2024.2-ubuntu_jammy"

components/keystone/values.yaml

Lines changed: 17 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -2,102 +2,15 @@
22
---
33
release_group: null
44

5+
images:
6+
tags:
7+
bootstrap: "ghcr.io/rackerlabs/understack/ansible:pr-806"
8+
59
bootstrap:
610
enabled: true
711
ks_user: admin
812
script: |
9-
# admin needs the admin role for the default domain
10-
openstack role add \
11-
--user="${OS_USERNAME}" \
12-
--domain="${OS_DEFAULT_DOMAIN}" \
13-
"admin"
14-
# create 'infra' domain
15-
openstack domain create --or-show infra
16-
# create 'baremetal' project for our ironic nodes to live in
17-
openstack project create --or-show --domain infra baremetal
18-
# create 'argoworkflow' user for automation
19-
openstack user create --or-show --domain infra --password demo argoworkflow
20-
# give 'argoworkflow' 'admin' over the 'baremetal' project
21-
openstack role add --user-domain infra --project-domain infra --user argoworkflow --project baremetal admin
22-
23-
# create 'flavorsync' user to allow synchronization of the flavors to nova
24-
openstack user create --or-show --domain service --password abcd1234 flavorsync
25-
openstack role create --or-show flavorsync
26-
openstack role add --user flavorsync --user-domain service --domain default --inherited flavorsync
27-
28-
# create 'monitoring' user for monitoring usage
29-
openstack user create --or-show --domain infra --password monitoring_demo monitoring
30-
# give 'monitoring' the 'admin' over the 'baremetal' project
31-
openstack role add --user-domain infra --project-domain infra --user monitoring --project baremetal admin
32-
33-
# this is too early because ironic won't exist
34-
openstack role add --project service --user ironic --user-domain service service
35-
36-
# OIDC integration
37-
RULES_FILE=$(mktemp)
38-
cat <<EOF > ${RULES_FILE}
39-
[
40-
{
41-
"local": [
42-
{
43-
"user": {
44-
"id": "{0}",
45-
"name": "{1}",
46-
"email": "{2}"
47-
},
48-
"groups": "{3}",
49-
"domain": {
50-
"name": "sso"
51-
}
52-
}
53-
],
54-
"remote": [
55-
{
56-
"type": "HTTP_OIDC_SUB"
57-
},
58-
{
59-
"type": "REMOTE_USER"
60-
},
61-
{
62-
"type": "HTTP_OIDC_EMAIL"
63-
},
64-
{
65-
"type": "HTTP_OIDC_GROUPS"
66-
}
67-
]
68-
}
69-
]
70-
EOF
71-
# look up or create our domain for SSO integration, called 'sso'
72-
sso_domain_id=$(openstack domain create --or-show --description "Domain for 'sso' identity provider" -f value -c id sso)
73-
74-
# define our identity provider 'sso' for the domain 'sso'
75-
openstack identity provider show sso 2>/dev/null || openstack identity provider create --domain "${sso_domain_id}" --description "Identity Provider to our dex" --remote-id https://dex.dev.undercloud.rackspace.net sso
76-
77-
# create or update the mapping that we'll use for the 'sso' identity
78-
if openstack mapping show sso_mapping 2>/dev/null; then
79-
openstack mapping set --rules ${RULES_FILE} sso_mapping;
80-
else
81-
openstack mapping create --rules ${RULES_FILE} sso_mapping;
82-
fi
83-
84-
# clean up
85-
rm -f "${RULES_FILE}"
86-
87-
# create the federation protocol 'openid' to use the identity provider 'sso' with the 'sso_mapping'
88-
openstack federation protocol show openid --identity-provider sso || openstack federation protocol create openid --mapping sso_mapping --identity-provider sso
89-
90-
for group in ucadmin ucuser ucneteng ucdctech; do
91-
# create groups which map to our groups claim from dex which can be mapped to permissions
92-
openstack group create --domain "${sso_domain_id}" ${group} --or-show
93-
# give each of the groups the member role on the domain and have them inherit it to each project
94-
# in the domain
95-
openstack role add --group-domain "${sso_domain_id}" --group ${group} --inherited --domain default member
96-
done
97-
# ucadmin can manage the standard project domain
98-
openstack role add --group-domain "${sso_domain_id}" --group ucadmin --inherited --domain default manager
99-
# ucadmin can manage the infra domain
100-
openstack role add --group-domain "${sso_domain_id}" --group ucadmin --inherited --domain infra manager
13+
ansible-runner run /runner --playboot keystone_bootstrap.yaml
10114
10215
network:
10316
# configure OpenStack Helm to use Undercloud's ingress
@@ -238,6 +151,18 @@ pod:
238151
- name: oidc-secret
239152
secret:
240153
secretName: sso-passphrase
154+
keystone_bootstrap:
155+
keystone_bootstrap:
156+
volumeMounts:
157+
- name: ansible-inventory
158+
mountPath: /runner/inventory/
159+
volumes:
160+
- name: ansible-inventory
161+
configMap:
162+
name: ansible-inventory
163+
items:
164+
- key: hosts.yaml
165+
path: hosts.yaml
241166
replicas:
242167
api: 2
243168
lifecycle:

0 commit comments

Comments
 (0)