feat: Add keycloak (#510)
* feat: Add keycloak
* fix lint error
* Deploy keycloak in default
* Fix role names in deps
* Remove dynamic key in ansible variable dic
* Use custom wait logic because postgresql CRD doesn't have condition status
* Use production mode for keycloak
* Wait until zalando operator ready
* Set admin password explicitly
* Fix ingress config
* Create grafana client in keycloak and enable oauth in grafana
* Use PXC instead of zalando postgres
* Set default value for atmosphere_keycloak_enabled
* Split the mysql queries into multiple parts
* Solve convo
* Use ansible module for keycloak realm
* Concatenate realm arrays
* Tune keycloak config to work with mysql vendor and fix keycloak db user's host config
* Add notes about mysql vendor support in keycloak
* remove default client scope setting
it requires community.general collection upgrade to 4.7.0.
But there are other collections in the deps list which use lower versions.
Need to bump its version at some time surely.
* Tuen grafana oauth config
* Fix keycloak client auth mode
* Use a variable for keycloak database name
* Fix lint error
* Remove unused var from doc
* Manage pxc strict mode in ansible
* revert the changes out of the scope
* ignore changes during realm creation
* Resolve commnents
* Fix default values
* Use official keycloak image instead of bitnami
* Set proxy mode using KC env var
* securely reference oauth secrets in grafana.ini
* Remove implicit octal value in helm values
* Support id token in keycloak clients for rbac and add ingress role
* Update grafana role map attribute
* Use j2 template
https://stackoverflow.com/questions/63961938/ansible-variable-conversion-to-int-is-ignored
* Create client roles
* Fix yaml lint error
* Add keycloak health check in consumer roles
* fix: use correct annotations
* chore: add keystone-keycloak-backend to keystone
* fix: make openstack_helm_endpoints work cleaner
* chore: clean up unecessary docs
* fix: lock down grafana to allow users with roles only
* feat: integrate keystone with keycloak
* chore: added horizon auth via keycloak
* chore: add slo for grafana
* chore: bump to 0.1.5
* Fix yaml lint error
* Fix client role creation
* chore(kube-prometheus-stack): update to latest
* fix: KubeJobFailed should be SEV-3
* chore: refactor softnet alerts
* chore(monitoring): migrate to using jsonnet
* chore: refactor alerts
* chore: major monitoring refactor
* fix: solve alerts
* fix: apiserver selector
* more cleanups
* switch from SEV- to P
* fix: improve port binding alerts
* fix admin state alert for neutron
* map some more alerts
* drop uuid
* Revert "drop uuid"
This reverts commit ad0f05d0e7564759e8259c2cc53c2e2f5c73e1b8.
* fix: drop recording rules
* switch alertmanager to jsonnet
* fix: idempotence for monitoring
* chore: fix linters
* chore: lower ceph osd timeouts to p4
* chore: refactor to using new jsonnet
* chore: use vendor path
* chore: fix mixin for alertmanager
* core: fix selector
* ci: add initial keycloak kind tests
* ci: run keycloak basic scenario
* chore: refactor to using multiple domains
* chore: wip for keycloak multidomain
* adding multi domain support for keycloak as external identity provider (#556)
Co-authored-by: Mohammed Naser <mnaser@vexxhost.com>
* chore: initial impl for multiple domain
* chore: grafan + keycloak wip
* chore: fix missing roles
* chore: remove commented out role
* chore: add ci debug
* chore: use smaller nodes for test
* ci: fix keycloak_user_info
* ci: fix keycloak_user_info
* ci: debug keycloak ci
* chore: fix linters
* ci: retry a few times for keycloak users to appear
* ci: ci fixes
* ci: misc fixes
* ci: fix secret generation
* ci: fix missing secrets
---------
Co-authored-by: okozachenko1203 <okozachenko1203@users.noreply.github.com>
Co-authored-by: Mohammed Naser <mnaser@vexxhost.com>
Co-authored-by: Jeremy Lee <6729613+legit-ninja@users.noreply.github.com>
diff --git a/roles/defaults/vars/main.yml b/roles/defaults/vars/main.yml
index fb12b84..22d7fff 100644
--- a/roles/defaults/vars/main.yml
+++ b/roles/defaults/vars/main.yml
@@ -74,6 +74,7 @@
ingress_nginx_default_backend: registry.k8s.io/defaultbackend-amd64:1.5
ingress_nginx_kube_webhook_certgen: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.1.1@sha256:64d8c73dca984af206adf9d6d7e46aa550362b1d7a01f3a0a91b20cc67868660 # noqa: yaml[line-length]
keepalived: us-docker.pkg.dev/vexxhost-infra/openstack/keepalived:2.0.19
+ keycloak: quay.io/keycloak/keycloak:22.0.1-0
keystone_api: quay.io/vexxhost/keystone@sha256:4c63257d75c72137454690ad8cef600742e20b07ab31d873b88fa382ce659d17 # image-source: quay.io/vexxhost/keystone:zed
keystone_credential_cleanup: quay.io/vexxhost/heat@sha256:755225f9a63c0968f1ceeda3a2f06c66dd8d247ff00308f549e66496aa8f59d0 # image-source: quay.io/vexxhost/heat:zed
keystone_credential_rotate: quay.io/vexxhost/keystone@sha256:4c63257d75c72137454690ad8cef600742e20b07ab31d873b88fa382ce659d17 # image-source: quay.io/vexxhost/keystone:zed
diff --git a/roles/horizon/vars/main.yml b/roles/horizon/vars/main.yml
index ab8da45..fd831c5 100644
--- a/roles/horizon/vars/main.yml
+++ b/roles/horizon/vars/main.yml
@@ -27,8 +27,16 @@
secure_proxy_ssl_header: "True"
horizon_images_upload_mode: direct
openstack_enable_password_retrieve: "True"
+ auth:
+ sso:
+ enabled: true
+ initial_choice: "{{ (keystone_domains is defined) | ternary(keystone_domains[0].name, 'atmosphere') }}"
+ idp_mapping: "{{ keystone_domains | default([{'name': 'atmosphere', 'label': 'Atmosphere'}]) | vexxhost.atmosphere.keystone_domains_to_idp_mappings }}" # noqa: yaml[line-length]
raw:
+ OPENSTACK_SSL_NO_VERIFY: "{{ (cluster_issuer_type == 'self-signed') | ternary('True', 'False') | string }}"
WEBSSO_KEYSTONE_URL: https://{{ openstack_helm_endpoints['identity']['host_fqdn_override']['public']['host'] }}/v3
+ # yamllint disable-line rule:line-length
+ LOGOUT_URL: https://{{ openstack_helm_endpoints['identity']['host_fqdn_override']['public']['host'] }}/v3/auth/OS-FEDERATION/identity_providers/redirect?logout=https://{{ openstack_helm_endpoints_horizon_api_host }}/auth/logout/
local_settings_d:
_50_monasca_ui_settings: "{{ lookup('file', '50-monasca-ui-settings.py') }}"
extra_panels:
diff --git a/roles/ingress/README.md b/roles/ingress/README.md
new file mode 100644
index 0000000..5bf4f28
--- /dev/null
+++ b/roles/ingress/README.md
@@ -0,0 +1 @@
+# `ingress`
diff --git a/roles/ingress/defaults/main.yml b/roles/ingress/defaults/main.yml
new file mode 100644
index 0000000..bbb881a
--- /dev/null
+++ b/roles/ingress/defaults/main.yml
@@ -0,0 +1,16 @@
+# List of annotations to apply to the Ingress
+ingress_annotations: {}
+
+ingress_class_name: "{{ atmosphere_ingress_class_name }}"
+
+# ingress_name: string
+
+# ingress_namespace: string
+
+# ingress_host: string
+
+# ingress_service_name: string
+
+# ingress_service_port: number
+
+# ingress_secret_name: string
diff --git a/roles/ingress/meta/main.yml b/roles/ingress/meta/main.yml
new file mode 100644
index 0000000..8f47829
--- /dev/null
+++ b/roles/ingress/meta/main.yml
@@ -0,0 +1,24 @@
+# Copyright (c) 2022 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+galaxy_info:
+ author: VEXXHOST, Inc.
+ description: Ansible role for managing K8s ingress
+ license: Apache-2.0
+ min_ansible_version: 5.5.0
+ standalone: false
+ platforms:
+ - name: Ubuntu
+ versions:
+ - focal
diff --git a/roles/ingress/tasks/main.yml b/roles/ingress/tasks/main.yml
new file mode 100644
index 0000000..32d0f8b
--- /dev/null
+++ b/roles/ingress/tasks/main.yml
@@ -0,0 +1,19 @@
+# Copyright (c) 2022 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+- name: Create Ingress {{ ingress_name }}
+ run_once: true
+ kubernetes.core.k8s:
+ state: present
+ template: ingress.yml.j2
diff --git a/roles/ingress/templates/ingress.yml.j2 b/roles/ingress/templates/ingress.yml.j2
new file mode 100644
index 0000000..0e48273
--- /dev/null
+++ b/roles/ingress/templates/ingress.yml.j2
@@ -0,0 +1,26 @@
+apiVersion: v1
+kind: Ingress
+metadata:
+ name: {{ ingress_name }}
+ namespace: {{ ingress_namespace | default('default') }}
+{% if ingress_annotations %}
+ annotations:
+ {{ ingress_annotations | to_nice_yaml | indent(4) }}
+{% endif %}
+spec:
+ ingressClassName: {{ ingress_class_name }}
+ rules:
+ - host: {{ ingress_host }}
+ http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: {{ ingress_service_name }}
+ port:
+ number: {{ ingress_service_port }}
+ tls:
+ - secretName: {{ ingress_secret_name | default(ingress_service_name ~ '-certs') }}
+ hosts:
+ - {{ ingress_host }}
diff --git a/roles/keycloak/README.md b/roles/keycloak/README.md
new file mode 100644
index 0000000..8659934
--- /dev/null
+++ b/roles/keycloak/README.md
@@ -0,0 +1 @@
+# `keycloak`
diff --git a/roles/keycloak/defaults/main.yml b/roles/keycloak/defaults/main.yml
new file mode 100644
index 0000000..2ac317a
--- /dev/null
+++ b/roles/keycloak/defaults/main.yml
@@ -0,0 +1,31 @@
+# Copyright (c) 2023 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+keycloak_helm_release_name: keycloak
+keycloak_helm_chart_path: "../../charts/keycloak/"
+keycloak_helm_chart_ref: /usr/local/src/keycloak
+
+keycloak_helm_release_namespace: auth-system
+keycloak_helm_values: {}
+
+keycloak_host: "{{ undef('You must specify a Keycloak host using keycloak_host') }}"
+keycloak_ingress_annotations: {}
+keycloak_ingress_class_name: "{{ atmosphere_ingress_class_name }}"
+
+keycloak_admin_username: admin
+keycloak_admin_password: "{{ undef(hint='You must specify a Keycloak admin password using keycloak_admin_password') }}"
+
+keycloak_database_username: keycloak
+keycloak_database_password: "{{ undef('You must specify a Keycloak database password using keycloak_database_password') }}"
+keycloak_database_name: keycloak
diff --git a/roles/keycloak/meta/main.yml b/roles/keycloak/meta/main.yml
new file mode 100644
index 0000000..1a2d277
--- /dev/null
+++ b/roles/keycloak/meta/main.yml
@@ -0,0 +1,34 @@
+# Copyright (c) 2022 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+galaxy_info:
+ author: VEXXHOST, Inc.
+ description: Ansible role for Keycloak
+ license: Apache-2.0
+ min_ansible_version: 5.5.0
+ standalone: false
+ platforms:
+ - name: Ubuntu
+ versions:
+ - focal
+
+dependencies:
+ - role: defaults
+ - role: openstack_helm_endpoints
+ vars:
+ openstack_helm_endpoints_list: ["oslo_db"]
+ - role: vexxhost.kubernetes.upload_helm_chart
+ vars:
+ upload_helm_chart_src: "{{ keycloak_helm_chart_path }}"
+ upload_helm_chart_dest: "{{ keycloak_helm_chart_ref }}"
diff --git a/roles/keycloak/tasks/main.yml b/roles/keycloak/tasks/main.yml
new file mode 100644
index 0000000..0f759ef
--- /dev/null
+++ b/roles/keycloak/tasks/main.yml
@@ -0,0 +1,83 @@
+# Copyright (c) 2022 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+- name: Get the Kuberentes service for Percona XtraDB Cluster
+ run_once: true
+ kubernetes.core.k8s_info:
+ kind: Service
+ name: "{{ openstack_helm_endpoints.oslo_db.hosts.default }}"
+ namespace: openstack
+ register: _pxc_service
+
+- name: Install Kubernetes python package
+ ansible.builtin.pip:
+ name: PyMySQL
+
+- name: Create Keycloak database
+ run_once: true
+ community.mysql.mysql_db:
+ login_host: "{{ _pxc_service.resources[0].spec.clusterIP }}"
+ login_user: root
+ login_password: "{{ openstack_helm_endpoints.oslo_db.auth.admin.password }}"
+ name: "{{ keycloak_database_name }}"
+
+- name: Create a Keycloak user
+ run_once: true
+ community.mysql.mysql_user:
+ login_host: "{{ _pxc_service.resources[0].spec.clusterIP }}"
+ login_user: root
+ login_password: "{{ openstack_helm_endpoints.oslo_db.auth.admin.password }}"
+ name: "{{ keycloak_database_username }}"
+ password: "{{ keycloak_database_password }}"
+ host: "%"
+ priv: "{{ keycloak_database_name }}.*:ALL"
+
+- name: Disable pxc strict mode
+ community.mysql.mysql_query:
+ login_host: "{{ _pxc_service.resources[0].spec.clusterIP }}"
+ login_user: root
+ login_password: "{{ openstack_helm_endpoints.oslo_db.auth.admin.password }}"
+ query: "set global pxc_strict_mode='PERMISSIVE'"
+
+- name: Deploy Helm chart
+ run_once: true
+ kubernetes.core.helm:
+ name: "{{ keycloak_helm_release_name }}"
+ chart_ref: "{{ keycloak_helm_chart_ref }}"
+ release_namespace: "{{ keycloak_helm_release_namespace }}"
+ create_namespace: true
+ kubeconfig: /etc/kubernetes/admin.conf
+ wait: true
+ values: "{{ _keycloak_helm_values | combine(keycloak_helm_values, recursive=True) }}"
+
+- name: Create Keycloak Ingress
+ ansible.builtin.include_role:
+ name: ingress
+ vars:
+ ingress_name: keycloak
+ ingress_namespace: "{{ keycloak_helm_release_namespace }}"
+ ingress_class_name: "{{ keycloak_ingress_class_name }}"
+ ingress_host: "{{ keycloak_host }}"
+ ingress_service_name: "{{ keycloak_helm_release_name }}"
+ ingress_service_port: 80
+ ingress_secret_name: "{{ keycloak_host }}-tls"
+ ingress_annotations:
+ cert-manager.io/cluster-issuer: atmosphere
+
+- name: Disable pxc strict mode
+ community.mysql.mysql_query:
+ login_host: "{{ _pxc_service.resources[0].spec.clusterIP }}"
+ login_user: root
+ login_password: "{{ openstack_helm_endpoints.oslo_db.auth.admin.password }}"
+ query: "set global pxc_strict_mode='ENFORCING'"
diff --git a/roles/keycloak/vars/main.yml b/roles/keycloak/vars/main.yml
new file mode 100644
index 0000000..981c98e
--- /dev/null
+++ b/roles/keycloak/vars/main.yml
@@ -0,0 +1,68 @@
+# Copyright (c) 2022 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+_keycloak_helm_values:
+ # Note(okozachenko1203): Mysql vendor is not supported by bitnami helm chart. As a workaround,
+ # we have to define jdbc connection string explicitly along side
+ # `externalDatabase` helm values.
+ extraEnvVars:
+ - name: KC_PROXY
+ value: edge
+ - name: KC_DB
+ value: mysql
+ - name: KC_DB_URL
+ value: "jdbc:mysql://{{ openstack_helm_endpoints.oslo_db.hosts.default }}.openstack:3306/{{ keycloak_database_name }}"
+ - name: KC_DB_USERNAME
+ value: "{{ keycloak_database_username }}"
+ - name: KC_DB_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ key: db-password
+ name: keycloak-externaldb
+ command:
+ - /opt/keycloak/bin/kc.sh
+ - --verbose
+ - start
+ - --auto-build
+ - --health-enabled=true
+ - --http-enabled=true
+ - --http-port=8080
+ - --hostname-strict=false
+ - --spi-events-listener-jboss-logging-success-level=info
+ - --spi-events-listener-jboss-logging-error-level=warn
+ - --transaction-xa-enabled=false
+ auth:
+ adminPassword: "{{ keycloak_admin_password }}"
+ adminUser: "{{ keycloak_admin_username }}"
+ externalDatabase:
+ host: "{{ openstack_helm_endpoints.oslo_db.hosts.default }}.openstack"
+ port: 3306
+ database: "{{ keycloak_database_name }}"
+ user: "{{ keycloak_database_username }}"
+ password: "{{ keycloak_database_password }}"
+ image:
+ registry: "{{ atmosphere_images['keycloak'] | vexxhost.kubernetes.docker_image('domain') }}"
+ repository: "{{ atmosphere_images['keycloak'] | vexxhost.kubernetes.docker_image('path') }}"
+ tag: "{{ atmosphere_images['keycloak'] | vexxhost.kubernetes.docker_image('tag') }}"
+ postgresql:
+ enabled: false
+ production: true
+ proxy: edge
+ # Note(okozachenko1203): It takes a bit long time for db migration with mysql vendor.
+ readinessProbe:
+ initialDelaySeconds: 60
+ periodSeconds: 10
+ timeoutSeconds: 1
+ failureThreshold: 6
+ successThreshold: 1
diff --git a/roles/keystone/README.md b/roles/keystone/README.md
index 5ab571b..ef6921c 100644
--- a/roles/keystone/README.md
+++ b/roles/keystone/README.md
@@ -10,9 +10,6 @@
```yaml
keystone_helm_values:
conf:
- keystone:
- identity:
- domain_configurations_from_database: true
ks_domains:
contoso:
identity:
diff --git a/roles/keystone/defaults/main.yml b/roles/keystone/defaults/main.yml
index 2dc6dc7..40ca892 100644
--- a/roles/keystone/defaults/main.yml
+++ b/roles/keystone/defaults/main.yml
@@ -21,3 +21,43 @@
# List of annotations to apply to the Ingress
keystone_ingress_annotations: {}
+
+# The following set of variables can be used for simple setups where you can
+# only need a single domain and realm. You can use the `keystone_domains`
+# variable to configure more complex setups.
+keystone_keycloak_server_url: "https://{{ keycloak_host }}"
+keystone_keycloak_server_internal_url: http://keycloak.auth-system.svc
+keystone_keycloak_user_realm_name: master
+keystone_keycloak_admin_client_id: admin-cli
+keystone_keycloak_admin_user: admin
+keystone_keycloak_admin_password: "{{ keycloak_admin_password }}"
+keystone_keycloak_realm: atmosphere
+keystone_keycloak_realm_name: Atmosphere
+keystone_keycloak_client_id: keystone
+# keystone_keycloak_client_secret:
+keystone_keycloak_scopes: "openid email profile"
+
+# This variable can be used for more complex setups that require multiple
+# domains that are mapped to multiple realms. If you are looking to use a
+# single domain and realm, you can skip this section.
+keystone_domains:
+ - name: "{{ keystone_keycloak_realm }}" # Domain name
+ label: "{{ keystone_keycloak_realm_name }}" # Realm display name + Horizon label
+ keycloak_server_url: "{{ keystone_keycloak_server_url }}" # Public Keycloak URL
+ keycloak_server_internal_url: "{{ keystone_keycloak_server_internal_url }}" # Internal Keycloak URL
+ keycloak_user_realm_name: "{{ keystone_keycloak_user_realm_name }}" # Keycloak realm name
+ keycloak_admin_client_id: "{{ keystone_keycloak_admin_client_id }}" # Keycloak admin client ID
+ keycloak_admin_user: "{{ keystone_keycloak_admin_user }}" # Keycloak admin username
+ keycloak_admin_password: "{{ keystone_keycloak_admin_password }}" # Keycloak admin password
+ keycloak_realm: "{{ keystone_keycloak_realm }}" # Keycloak realm name
+ keycloak_client_id: "{{ keystone_keycloak_client_id }}" # Keycloak client ID
+ keycloak_client_secret: "{{ keystone_keycloak_client_secret }}" # Keycloak client secret
+ keycloak_scopes: "{{ keystone_keycloak_scopes }}" # Keycloak scopes
+
+# Keystone OpenID Connect settings (defaults to Keycloak for Atmosphere)
+keystone_oidc_ssl_validate_server: "{{ (cluster_issuer_type == 'self-signed') | ternary('Off', 'On') }}"
+keystone_oidc_crypto_passphrase: "{{ undef('You must specify a Keystone OIDC client secret using keystone_oidc_crypto_passphrase') }}"
+keystone_oidc_redirect_uri: "https://{{ openstack_helm_endpoints_keystone_api_host }}/v3/auth/OS-FEDERATION/identity_providers/redirect"
+keystone_oidc_redirect_urls_allowed:
+ - "^https://{{ openstack_helm_endpoints_keystone_api_host }}/v3/auth/OS-FEDERATION/identity_providers/({{ keystone_domains | map(attribute='name') | join('|') }})/protocols/openid/websso" # noqa: yaml[line-length]
+ - "^https://{{ openstack_helm_endpoints_horizon_api_host }}/auth/logout/$"
diff --git a/roles/keystone/meta/main.yml b/roles/keystone/meta/main.yml
index bf58a2f..3e149bf 100644
--- a/roles/keystone/meta/main.yml
+++ b/roles/keystone/meta/main.yml
@@ -25,6 +25,7 @@
dependencies:
- role: defaults
+ # - role: openstacksdk
- role: openstack_helm_endpoints
vars:
openstack_helm_endpoints_chart: keystone
diff --git a/roles/keystone/tasks/main.yml b/roles/keystone/tasks/main.yml
index 75e2e49..c9e6229 100644
--- a/roles/keystone/tasks/main.yml
+++ b/roles/keystone/tasks/main.yml
@@ -36,6 +36,54 @@
name: "{{ keystone_helm_release_name }}"
namespace: "{{ keystone_helm_release_namespace }}"
+- name: Create Keycloak realms
+ run_once: true
+ delegate_to: localhost
+ changed_when: false
+ community.general.keycloak_realm:
+ # Keycloak settings
+ auth_keycloak_url: "{{ item.keycloak_server_url }}"
+ auth_realm: "{{ item.keycloak_user_realm_name }}"
+ auth_client_id: "{{ item.keycloak_admin_client_id }}"
+ auth_username: "{{ item.keycloak_admin_user }}"
+ auth_password: "{{ item.keycloak_admin_password }}"
+ validate_certs: "{{ cluster_issuer_type != 'self-signed' }}"
+ # Realm settings
+ id: "{{ item.keycloak_realm }}"
+ realm: "{{ item.keycloak_realm }}"
+ display_name: "{{ item.label }}"
+ enabled: true
+ loop: "{{ keystone_domains }}"
+ loop_control:
+ label: "{{ item.name }}"
+
+- name: Create ConfigMap with all OpenID connect configurations
+ run_once: true
+ kubernetes.core.k8s:
+ template: configmap-openid-metadata.yml.j2
+
+- name: Create Keycloak clients
+ run_once: true
+ delegate_to: localhost
+ community.general.keycloak_client:
+ # Keycloak settings
+ auth_keycloak_url: "{{ item.keycloak_server_url }}"
+ auth_realm: "{{ item.keycloak_user_realm_name }}"
+ auth_client_id: "{{ item.keycloak_admin_client_id }}"
+ auth_username: "{{ item.keycloak_admin_user }}"
+ auth_password: "{{ item.keycloak_admin_password }}"
+ validate_certs: "{{ cluster_issuer_type != 'self-signed' }}"
+ # Realm settings
+ realm: "{{ item.keycloak_realm }}"
+ client_id: "{{ item.keycloak_client_id }}"
+ secret: "{{ item.keycloak_client_secret }}"
+ redirect_uris:
+ - "{{ keystone_oidc_redirect_uri }}"
+ - "https://{{ openstack_helm_endpoints_horizon_api_host }}/auth/logout/"
+ loop: "{{ keystone_domains }}"
+ loop_control:
+ label: "{{ item.name }}"
+
- name: Deploy Helm chart
run_once: true
kubernetes.core.helm:
@@ -54,3 +102,54 @@
openstack_helm_ingress_service_name: keystone-api
openstack_helm_ingress_service_port: 5000
openstack_helm_ingress_annotations: "{{ keystone_ingress_annotations }}"
+
+- name: Create Keystone domains
+ run_once: true
+ delegate_to: localhost
+ vexxhost.atmosphere.identity_domain:
+ name: "{{ item.name }}"
+ register: keystone_domains_result
+ loop: "{{ keystone_domains }}"
+ loop_control:
+ label: "{{ item.name }}"
+
+- name: Create Keystone identity providers
+ run_once: true
+ delegate_to: localhost
+ vexxhost.atmosphere.federation_idp:
+ name: "{{ item.domain.name }}"
+ domain_id: "{{ item.domain.id }}"
+ remote_ids:
+ - "{{ item.item | vexxhost.atmosphere.issuer_from_domain }}"
+ loop: "{{ keystone_domains_result.results }}"
+ loop_control:
+ label: "{{ item.domain.name }}"
+
+- name: Create Keystone federation mappings
+ run_once: true
+ delegate_to: localhost
+ vexxhost.atmosphere.federation_mapping:
+ name: "{{ item.name }}-openid"
+ rules:
+ - local:
+ - user:
+ type: local
+ id: "{0}"
+ domain:
+ name: "{{ item.name }}"
+ remote:
+ - type: OIDC-sub
+ loop: "{{ keystone_domains }}"
+ loop_control:
+ label: "{{ item.name }}"
+
+- name: Create Keystone federation protocols
+ run_once: true
+ delegate_to: localhost
+ vexxhost.atmosphere.keystone_federation_protocol:
+ name: openid
+ idp_id: "{{ item.name }}"
+ mapping_id: "{{ item.name }}-openid"
+ loop: "{{ keystone_domains }}"
+ loop_control:
+ label: "{{ item.name }}"
diff --git a/roles/keystone/templates/configmap-openid-metadata.yml.j2 b/roles/keystone/templates/configmap-openid-metadata.yml.j2
new file mode 100644
index 0000000..4c2e653
--- /dev/null
+++ b/roles/keystone/templates/configmap-openid-metadata.yml.j2
@@ -0,0 +1,13 @@
+apiVersion: v1
+metadata:
+ name: keystone-openid-metadata
+ namespace: "{{ keystone_helm_release_namespace }}"
+ labels:
+ application: keystone
+kind: ConfigMap
+data:
+{% for domain in keystone_domains %}
+ {{ domain.name }}-oidc-client: '{"client_id":"{{ domain.keycloak_client_id }}","client_secret":"{{ domain.keycloak_client_secret }}","response_type":"id_token"}'
+ {{ domain.name }}-oidc-conf: '{"scope":"{{ domain.keycloak_scopes }}"}'
+ {{ domain.name }}-oidc-provider: '{{ lookup('url', domain.keycloak_server_url ~ "/realms/" ~ domain.keycloak_realm ~ "/.well-known/openid-configuration", validate_certs=keystone_oidc_ssl_validate_server) }}'
+{% endfor %}
diff --git a/roles/keystone/vars/main.yml b/roles/keystone/vars/main.yml
index fc16ccd..1cc61ef 100644
--- a/roles/keystone/vars/main.yml
+++ b/roles/keystone/vars/main.yml
@@ -17,38 +17,16 @@
images:
tags: "{{ atmosphere_images | vexxhost.atmosphere.openstack_helm_image_tags('keystone') }}"
pod:
- # mounts = {
- # keystone_api = {
- # keystone_api = {
- # volumeMounts = [
- # {
- # name = kubernetes_config_map.keystone_ldap_ca.metadata[0].name
- # mountPath = "/etc/keystone/ldap"
- # },
- # {
- # name = kubernetes_config_map.keystone_openid_connect_metadata.metadata[0].name
- # mountPath = "/var/lib/apache2/oidc"
- # }
- # ],
- # volumes = [
- # {
- # name = kubernetes_config_map.keystone_ldap_ca.metadata[0].name
- # configMap = {
- # name = kubernetes_config_map.keystone_ldap_ca.metadata[0].name
- # }
- # },
- # {
- # name = kubernetes_config_map.keystone_openid_connect_metadata.metadata[0].name
- # configMap = {
- # name = kubernetes_config_map.keystone_openid_connect_metadata.metadata[0].name
- # }
- # }
- # ]
- # }
- # }
- # },
replicas:
api: 3
+ mounts:
+ keystone_api:
+ keystone_api:
+ volumeMounts: "{{ keystone_domains | vexxhost.atmosphere.keystone_domains_to_mounts }}"
+ volumes:
+ - name: keystone-openid-metadata
+ configMap:
+ name: keystone-openid-metadata
conf:
keystone:
DEFAULT:
@@ -57,15 +35,58 @@
methods: password,token,openid,application_credential
cors:
allowed_origins: "*"
+ openid:
+ remote_id_attribute: HTTP_OIDC_ISS
federation:
- assertion_prefix: OIDC-
- remote_id_attribute: OIDC-iss
# TODO(mnaser): Lookup using openstack_helm_endpoints
trusted_dashboard: "https://{{ openstack_helm_endpoints_horizon_api_host }}/auth/websso/"
- identity:
- domain_configurations_from_database: true
oslo_messaging_notifications:
driver: noop
+ wsgi_keystone: |
+ LoadModule headers_module /usr/lib/apache2/modules/mod_headers.so
+ Listen 0.0.0.0:5000
+ TransferLog /dev/stdout
+ ErrorLog /dev/stderr
+ <VirtualHost *:5000>
+ # WSGI
+ WSGIDaemonProcess keystone-public processes=4 threads=1 user=keystone group=keystone display-name=%{GROUP}
+ WSGIProcessGroup keystone-public
+ WSGIScriptAlias / /var/www/cgi-bin/keystone/keystone-wsgi-public
+ WSGIApplicationGroup %{GLOBAL}
+ WSGIPassAuthorization On
+ # NOTE(mnaser): This is to by-pass large header limits for large tokens
+ LimitRequestFieldSize 16384
+ # OIDC
+ OIDCClaimPrefix "OIDC-"
+ OIDCMetadataDir /var/lib/apache2/oidc
+ OIDCSSLValidateServer "{{ keystone_oidc_ssl_validate_server }}"
+ OIDCCryptoPassphrase {{ keystone_oidc_crypto_passphrase }}
+ OIDCRedirectURI {{ keystone_oidc_redirect_uri }}
+ OIDCRedirectURLsAllowed {{ keystone_oidc_redirect_urls_allowed | join(' ') }}
+ # NOTE(mnaser): These are Atmosphere specific settings.
+ OIDCSessionType client-cookie:store_id_token
+ OIDCXForwardedHeaders X-Forwarded-Host X-Forwarded-Proto
+ <Location /v3/auth/OS-FEDERATION/identity_providers/redirect>
+ AuthType openid-connect
+ Require valid-user
+ </Location>
+ <Location /v3/auth/OS-FEDERATION/websso/openid>
+ Require valid-user
+ AuthType openid-connect
+ </Location>
+ {% for domain in keystone_domains %}
+ <Location /v3/OS-FEDERATION/identity_providers/{{ domain.name }}/protocols/openid/auth>
+ Require valid-user
+ AuthType oauth20
+ </Location>
+ <Location /v3/auth/OS-FEDERATION/identity_providers/{{ domain.name }}/protocols/openid/websso>
+ Require valid-user
+ AuthType openid-connect
+ OIDCDiscoverURL {{ keystone_oidc_redirect_uri }}?iss={{ domain | urlencoded_issuer_from_domain }}
+ </Location>
+ {% endfor %}
+ </VirtualHost>
+ ks_domains: "{{ keystone_domains | vexxhost.atmosphere.to_ks_domains }}"
manifests:
job_credential_cleanup: false
ingress_api: false
diff --git a/roles/kube_prometheus_stack/defaults/main.yml b/roles/kube_prometheus_stack/defaults/main.yml
index 80eb37b..bce734e 100644
--- a/roles/kube_prometheus_stack/defaults/main.yml
+++ b/roles/kube_prometheus_stack/defaults/main.yml
@@ -24,3 +24,15 @@
kube_prometheus_stack_grafana_ingress_class_name: "{{ kube_prometheus_stack_ingress_class_name }}"
kube_prometheus_stack_grafana_host: "{{ undef('You must specify a Grafana host using kube_prometheus_stack_grafana_host') }}"
kube_prometheus_stack_grafana_admin_password: "{{ undef('You must specify a Grafana password using kube_prometheus_stack_grafana_admin_password') }}"
+
+grafana_keycloak_server_url: "https://{{ keycloak_host }}"
+grafana_keycloak_user_realm_name: master
+grafana_keycloak_admin_client_id: admin-cli
+grafana_keycloak_admin_user: admin
+grafana_keycloak_admin_password: "{{ keycloak_admin_password }}"
+grafana_keycloak_realm: atmosphere
+grafana_keycloak_realm_name: Atmosphere
+
+grafana_keycloak_client_id: grafana
+# grafana_keycloak_client_secret:
+grafana_keycloak_scopes: "openid email profile"
diff --git a/roles/kube_prometheus_stack/files/jsonnet/jsonnetfile.json b/roles/kube_prometheus_stack/files/jsonnet/jsonnetfile.json
index 5da830d..429d9cc 100644
--- a/roles/kube_prometheus_stack/files/jsonnet/jsonnetfile.json
+++ b/roles/kube_prometheus_stack/files/jsonnet/jsonnetfile.json
@@ -65,5 +65,5 @@
"version": "master"
}
],
- "legacyImports": true
+ "legacyImports": false
}
diff --git a/roles/kube_prometheus_stack/files/jsonnet/mixins.libsonnet b/roles/kube_prometheus_stack/files/jsonnet/mixins.libsonnet
index 086b215..cd88e7c 100644
--- a/roles/kube_prometheus_stack/files/jsonnet/mixins.libsonnet
+++ b/roles/kube_prometheus_stack/files/jsonnet/mixins.libsonnet
@@ -35,6 +35,8 @@
'CephMgrPrometheusModuleInactive:critical': 'P4',
'CephMonDown:warning': 'P4',
'CephMonDownQuorumAtRisk:critical': 'P3',
+ 'CephOSDTimeoutsClusterNetwork:warning': 'P4',
+ 'CephOSDTimeoutsPublicNetwork:warning': 'P4',
'KubeJobFailed:warning': 'P4',
};
@@ -47,24 +49,25 @@
else defaultSeverityMapping[rule.labels.severity];
local mixins = {
- alertmanager: (import 'alertmanager-mixin/mixin.libsonnet') + {
+ alertmanager: (import 'vendor/github.com/prometheus/alertmanager/doc/alertmanager-mixin/mixin.libsonnet') + {
_config+:: {
- alertmanagerSelector: 'job="kube-prometheus-stack-alertmanager"',
+ alertmanagerSelector: 'job="kube-prometheus-stack-alertmanager",namespace="monitoring"',
+ alertmanagerClusterLabels: 'namespace,service,cluster',
},
},
- ceph: (import 'ceph-mixin/mixin.libsonnet'),
- coredns: (import 'coredns-mixin/mixin.libsonnet') + {
+ ceph: (import 'vendor/github.com/ceph/ceph/monitoring/ceph-mixin/mixin.libsonnet'),
+ coredns: (import 'vendor/github.com/povilasv/coredns-mixin/mixin.libsonnet') + {
_config+:: {
corednsSelector: 'job="coredns"',
},
},
- kube: (import 'kubernetes-mixin/mixin.libsonnet') + {
+ kube: (import 'vendor/github.com/kubernetes-monitoring/kubernetes-mixin/mixin.libsonnet') + {
_config+:: {
kubeApiserverSelector: 'job="apiserver"',
},
},
- memcached: (import 'memcached-mixin/mixin.libsonnet'),
- mysqld: (import 'mysqld-mixin/mixin.libsonnet') + {
+ memcached: (import 'vendor/github.com/grafana/jsonnet-libs/memcached-mixin/mixin.libsonnet'),
+ mysqld: (import 'vendor/github.com/prometheus/mysqld_exporter/mysqld-mixin/mixin.libsonnet') + {
prometheusAlerts+:: {
groups+: [
{
@@ -105,7 +108,7 @@
],
},
},
- node: (import 'node-mixin/mixin.libsonnet'),
+ node: (import 'vendor/github.com/prometheus/node_exporter/docs/node-mixin/mixin.libsonnet'),
openstack: (import 'openstack.libsonnet'),
} + (import 'legacy.libsonnet');
diff --git a/roles/kube_prometheus_stack/files/jsonnet/vendor/ceph-mixin b/roles/kube_prometheus_stack/files/jsonnet/vendor/ceph-mixin
deleted file mode 120000
index dbbe333..0000000
--- a/roles/kube_prometheus_stack/files/jsonnet/vendor/ceph-mixin
+++ /dev/null
@@ -1 +0,0 @@
-github.com/ceph/ceph/monitoring/ceph-mixin
\ No newline at end of file
diff --git a/roles/kube_prometheus_stack/files/jsonnet/vendor/coredns-mixin b/roles/kube_prometheus_stack/files/jsonnet/vendor/coredns-mixin
deleted file mode 120000
index 56c22bd..0000000
--- a/roles/kube_prometheus_stack/files/jsonnet/vendor/coredns-mixin
+++ /dev/null
@@ -1 +0,0 @@
-github.com/povilasv/coredns-mixin
\ No newline at end of file
diff --git a/roles/kube_prometheus_stack/files/jsonnet/vendor/grafana-builder b/roles/kube_prometheus_stack/files/jsonnet/vendor/grafana-builder
deleted file mode 120000
index cfa90dd..0000000
--- a/roles/kube_prometheus_stack/files/jsonnet/vendor/grafana-builder
+++ /dev/null
@@ -1 +0,0 @@
-github.com/grafana/jsonnet-libs/grafana-builder
\ No newline at end of file
diff --git a/roles/kube_prometheus_stack/files/jsonnet/vendor/grafonnet b/roles/kube_prometheus_stack/files/jsonnet/vendor/grafonnet
deleted file mode 120000
index fd2d163..0000000
--- a/roles/kube_prometheus_stack/files/jsonnet/vendor/grafonnet
+++ /dev/null
@@ -1 +0,0 @@
-github.com/grafana/grafonnet-lib/grafonnet
\ No newline at end of file
diff --git a/roles/kube_prometheus_stack/files/jsonnet/vendor/memcached-mixin b/roles/kube_prometheus_stack/files/jsonnet/vendor/memcached-mixin
deleted file mode 120000
index 3fdefc5..0000000
--- a/roles/kube_prometheus_stack/files/jsonnet/vendor/memcached-mixin
+++ /dev/null
@@ -1 +0,0 @@
-github.com/grafana/jsonnet-libs/memcached-mixin
\ No newline at end of file
diff --git a/roles/kube_prometheus_stack/files/jsonnet/vendor/mysqld-mixin b/roles/kube_prometheus_stack/files/jsonnet/vendor/mysqld-mixin
deleted file mode 120000
index d5613a5..0000000
--- a/roles/kube_prometheus_stack/files/jsonnet/vendor/mysqld-mixin
+++ /dev/null
@@ -1 +0,0 @@
-github.com/prometheus/mysqld_exporter/mysqld-mixin
\ No newline at end of file
diff --git a/roles/kube_prometheus_stack/tasks/main.yml b/roles/kube_prometheus_stack/tasks/main.yml
index a80b36b..5bdc548 100644
--- a/roles/kube_prometheus_stack/tasks/main.yml
+++ b/roles/kube_prometheus_stack/tasks/main.yml
@@ -12,6 +12,82 @@
# License for the specific language governing permissions and limitations
# under the License.
+- name: Create Keycloak realm
+ run_once: true
+ delegate_to: localhost
+ changed_when: false
+ community.general.keycloak_realm:
+ # Keycloak settings
+ auth_keycloak_url: "{{ grafana_keycloak_server_url }}"
+ auth_realm: "{{ grafana_keycloak_user_realm_name }}"
+ auth_client_id: "{{ grafana_keycloak_admin_client_id }}"
+ auth_username: "{{ grafana_keycloak_admin_user }}"
+ auth_password: "{{ grafana_keycloak_admin_password }}"
+ validate_certs: "{{ cluster_issuer_type != 'self-signed' }}"
+ # Realm settings
+ id: "{{ grafana_keycloak_realm }}"
+ realm: "{{ grafana_keycloak_realm }}"
+ display_name: "{{ grafana_keycloak_realm_name }}"
+ enabled: true
+
+- name: Add client roles in "id_token"
+ changed_when: false
+ community.general.keycloak_clientscope:
+ # Keycloak settings
+ auth_keycloak_url: "{{ grafana_keycloak_server_url }}"
+ auth_realm: "{{ grafana_keycloak_user_realm_name }}"
+ auth_client_id: "{{ grafana_keycloak_admin_client_id }}"
+ auth_username: "{{ grafana_keycloak_admin_user }}"
+ auth_password: "{{ grafana_keycloak_admin_password }}"
+ validate_certs: "{{ cluster_issuer_type != 'self-signed' }}"
+ # Client scope settings
+ name: roles
+ realm: "{{ grafana_keycloak_realm }}"
+ protocol_mappers:
+ - name: client roles
+ protocol: openid-connect
+ protocolMapper: oidc-usermodel-client-role-mapper
+ config:
+ claim.name: "resource_access.${client_id}.roles"
+ access.token.claim: true
+ id.token.claim: true
+ multivalued: true
+
+- name: Create Keycloak client
+ run_once: true
+ delegate_to: localhost
+ community.general.keycloak_client:
+ # Keycloak settings
+ auth_keycloak_url: "{{ grafana_keycloak_server_url }}"
+ auth_realm: "{{ grafana_keycloak_user_realm_name }}"
+ auth_client_id: "{{ grafana_keycloak_admin_client_id }}"
+ auth_username: "{{ grafana_keycloak_admin_user }}"
+ auth_password: "{{ grafana_keycloak_admin_password }}"
+ validate_certs: "{{ cluster_issuer_type != 'self-signed' }}"
+ # Realm settings
+ realm: "{{ grafana_keycloak_realm }}"
+ client_id: "{{ grafana_keycloak_client_id }}"
+ secret: "{{ grafana_keycloak_client_secret }}"
+ redirect_uris:
+ - "https://{{ kube_prometheus_stack_grafana_host }}/login/generic_oauth"
+
+- name: Create Keycloak roles
+ run_once: true
+ delegate_to: localhost
+ community.general.keycloak_role:
+ # Keycloak settings
+ auth_keycloak_url: "{{ grafana_keycloak_server_url }}"
+ auth_realm: "{{ grafana_keycloak_user_realm_name }}"
+ auth_client_id: "{{ grafana_keycloak_admin_client_id }}"
+ auth_username: "{{ grafana_keycloak_admin_user }}"
+ auth_password: "{{ grafana_keycloak_admin_password }}"
+ validate_certs: "{{ cluster_issuer_type != 'self-signed' }}"
+ # Realm settings
+ realm: "{{ grafana_keycloak_realm }}"
+ client_id: "{{ grafana_keycloak_client_id }}"
+ name: "{{ item }}"
+ loop: ["admin", "editor", "viewer"]
+
- name: Retrieve "etcd" CA certificate
ansible.builtin.slurp:
src: /etc/kubernetes/pki/etcd/ca.crt
@@ -27,20 +103,6 @@
src: /etc/kubernetes/pki/etcd/healthcheck-client.key
register: _etcd_healthcheck_client_key
-- name: Create Secret with "etcd" TLS certificates
- kubernetes.core.k8s:
- state: present
- definition:
- apiVersion: v1
- kind: Secret
- metadata:
- name: kube-prometheus-stack-etcd-client-cert
- namespace: monitoring
- data:
- ca.crt: "{{ _etcd_ca_crt.content }}"
- healthcheck-client.crt: "{{ _etcd_healthcheck_client_crt.content }}"
- healthcheck-client.key: "{{ _etcd_healthcheck_client_key.content }}"
-
- name: Uninstall the legacy HelmRelease
run_once: true
block:
@@ -65,12 +127,40 @@
name: "{{ kube_prometheus_stack_helm_release_name }}"
namespace: "{{ kube_prometheus_stack_helm_release_namespace }}"
+- name: Create secrets for monitoring
+ kubernetes.core.k8s:
+ state: present
+ definition:
+ - apiVersion: v1
+ kind: Namespace
+ metadata:
+ name: "{{ kube_prometheus_stack_helm_release_namespace }}"
+
+ - apiVersion: v1
+ kind: Secret
+ metadata:
+ name: grafana-auth-generic-oauth-secret
+ namespace: "{{ kube_prometheus_stack_helm_release_namespace }}"
+ stringData:
+ client_id: "{{ grafana_keycloak_client_id }}"
+ client_secret: "{{ grafana_keycloak_client_secret }}"
+
+ - apiVersion: v1
+ kind: Secret
+ metadata:
+ name: kube-prometheus-stack-etcd-client-cert
+ namespace: monitoring
+ data:
+ ca.crt: "{{ _etcd_ca_crt.content }}"
+ healthcheck-client.crt: "{{ _etcd_healthcheck_client_crt.content }}"
+ healthcheck-client.key: "{{ _etcd_healthcheck_client_key.content }}"
+
- name: Install all CRDs
run_once: true
changed_when: false
kubernetes.core.k8s:
state: present
- definition: "{{ lookup('pipe', 'cat ' + playbook_dir + '/../charts/kube-prometheus-stack/charts/crds/crds/crd-*.yaml') | regex_replace('- =$', '- \"=\"', multiline=True) | from_yaml_all }}" # noqa: yaml[line-length]
+ definition: "{{ lookup('pipe', 'cat ' + role_path + '/../../charts/kube-prometheus-stack/charts/crds/crds/crd-*.yaml') | regex_replace('- =$', '- \"=\"', multiline=True) | from_yaml_all }}" # noqa: yaml[line-length]
apply: true
server_side_apply:
field_manager: atmosphere
diff --git a/roles/kube_prometheus_stack/vars/main.yml b/roles/kube_prometheus_stack/vars/main.yml
index b19ced8..d68a7a1 100644
--- a/roles/kube_prometheus_stack/vars/main.yml
+++ b/roles/kube_prometheus_stack/vars/main.yml
@@ -81,6 +81,41 @@
openstack-control-plane: enabled
grafana:
adminPassword: "{{ kube_prometheus_stack_grafana_admin_password }}"
+ extraSecretMounts:
+ - name: auth-generic-oauth-secret-mount
+ secretName: grafana-auth-generic-oauth-secret
+ defaultMode: "0440"
+ mountPath: /etc/secrets/auth_generic_oauth
+ readOnly: true
+ grafana.ini:
+ server:
+ root_url: https://{{ kube_prometheus_stack_grafana_host }}
+ auth:
+ oauth_skip_org_role_update_sync: false
+ disable_login_form: true
+ # NOTE(mnaser): We're not able to redirect instantly to Grafana due to the fact that it doesn't support sending
+ # the `id_token_hint`.
+ #
+ # See: https://github.com/grafana/grafana/issues/24643
+ signout_redirect_url: "{{ grafana_keycloak_server_url }}/realms/{{ grafana_keycloak_realm }}/protocol/openid-connect/logout"
+ auth.generic_oauth:
+ enabled: true
+ name: Atmosphere
+ allow_sign_up: true
+ client_id: "$__file{/etc/secrets/auth_generic_oauth/client_id}"
+ client_secret: "$__file{/etc/secrets/auth_generic_oauth/client_secret}"
+ scopes: openid email profile offline_access roles
+ email_attribute_path: email
+ login_attribute_path: username
+ name_attribute_path: full_name
+ auth_url: "{{ grafana_keycloak_server_url }}/realms/{{ grafana_keycloak_realm }}/protocol/openid-connect/auth"
+ token_url: "{{ grafana_keycloak_server_url }}/realms/{{ grafana_keycloak_realm }}/protocol/openid-connect/token"
+ api_url: "{{ grafana_keycloak_server_url }}/realms/{{ grafana_keycloak_realm }}/protocol/openid-connect/userinfo"
+ tls_skip_verify_insecure: true
+ # yamllint disable-line rule:line-length
+ role_attribute_path: "contains(resource_access.grafana.roles[*], 'admin') && 'Admin' || contains(resource_access.grafana.roles[*], 'editor') && 'Editor' || contains(resource_access.grafana.roles[*], 'viewer') && 'Viewer'"
+ role_attribute_strict: true
+ skip_org_role_sync: false
image:
repository: "{{ atmosphere_images['grafana'] | vexxhost.kubernetes.docker_image('name') }}"
tag: "{{ atmosphere_images['grafana'] | vexxhost.kubernetes.docker_image('tag') }}"
diff --git a/roles/openstack_helm_endpoints/tasks/main.yml b/roles/openstack_helm_endpoints/tasks/main.yml
index 5e23099..6e4056f 100644
--- a/roles/openstack_helm_endpoints/tasks/main.yml
+++ b/roles/openstack_helm_endpoints/tasks/main.yml
@@ -18,7 +18,7 @@
openstack_helm_endpoints_list: |-
{{ lookup('ansible.builtin.file', '../../../charts/' ~ openstack_helm_endpoints_chart ~ '/values.yaml', split_lines=False) | from_yaml | community.general.json_query('keys(endpoints)') | difference(_openstack_helm_endpoints_ignore) }}
when:
- - openstack_helm_endpoints_list is not defined or openstack_helm_endpoints_list == None
+ - openstack_helm_endpoints_chart is defined
# NOTE(mnaser): Since we manage one-RabbitMQ per service, we create the RabbitMQ
# cluster here and then append the necessary values to be used
diff --git a/roles/openstack_helm_ingress/defaults/main.yml b/roles/openstack_helm_ingress/defaults/main.yml
index 1a0aa95..f3c0133 100644
--- a/roles/openstack_helm_ingress/defaults/main.yml
+++ b/roles/openstack_helm_ingress/defaults/main.yml
@@ -10,9 +10,6 @@
# Name of the "cert-manager" ClusterIssuer to use for TLS certificates
openstack_helm_ingress_cluster_issuer: "{{ atmosphere_ingress_cluster_issuer }}"
-# Name of the Ingress class to use for exposing the service.
-openstack_helm_ingress_class_name: "{{ atmosphere_ingress_class_name }}"
-
# Secret containing the TLS certificate to use for the Ingress, overriding this
# value globally should allow to point towards a secret containing the custom
# certificate.
diff --git a/roles/openstack_helm_ingress/tasks/main.yml b/roles/openstack_helm_ingress/tasks/main.yml
index a5c9302..bf1d770 100644
--- a/roles/openstack_helm_ingress/tasks/main.yml
+++ b/roles/openstack_helm_ingress/tasks/main.yml
@@ -46,30 +46,13 @@
when: openstack_helm_ingress_secret_name is not defined
- name: Create Ingress {{ openstack_helm_ingress_name }}
- run_once: true
- kubernetes.core.k8s:
- state: present
- definition:
- apiVersion: v1
- kind: Ingress
- metadata:
- name: "{{ openstack_helm_ingress_name }}"
- namespace: openstack
- annotations: "{{ _openstack_helm_ingress_annotations | combine(openstack_helm_ingress_annotations, recursive=True) }}"
- spec:
- ingressClassName: "{{ openstack_helm_ingress_class_name }}"
- rules:
- - host: "{{ openstack_helm_ingress_host }}"
- http:
- paths:
- - path: /
- pathType: Prefix
- backend:
- service:
- name: "{{ openstack_helm_ingress_service_name }}"
- port:
- number: "{{ openstack_helm_ingress_service_port }}"
- tls:
- - secretName: "{{ openstack_helm_ingress_secret_name | default(openstack_helm_ingress_service_name ~ '-certs') }}"
- hosts:
- - "{{ openstack_helm_ingress_host }}"
+ ansible.builtin.include_role:
+ name: ingress
+ vars:
+ ingress_annotations: "{{ _openstack_helm_ingress_annotations | combine(openstack_helm_ingress_annotations, recursive=True) }}"
+ ingress_name: "{{ openstack_helm_ingress_name }}"
+ ingress_namespace: openstack
+ ingress_host: "{{ openstack_helm_ingress_host }}"
+ ingress_service_name: "{{ openstack_helm_ingress_service_name }}"
+ ingress_service_port: "{{ openstack_helm_ingress_service_port }}"
+ ingress_secret_name: "{{ openstack_helm_ingress_secret_name | default(openstack_helm_ingress_service_name ~ '-certs') }}"