Add OPA and Openshift AI

This commit is contained in:
2026-10-20 14:08:57 -04:00
parent 7df35b9dd4
commit 5b46ba7c74
27 changed files with 2634 additions and 1 deletions

View File

@@ -0,0 +1,95 @@
---
# Point the AAP controller at OPA (PolicyAsCode settings) and optionally attach
# query paths to organizations, inventories, or job templates.
- name: Read AAP admin password
kubernetes.core.k8s_info:
api_version: v1
kind: Secret
namespace: "{{ opa_server_aap_namespace }}"
name: "{{ opa_server_aap_admin_secret }}"
register: __opa_server_aap_admin
no_log: true
- name: Read the service CA bundle
kubernetes.core.k8s_info:
api_version: v1
kind: ConfigMap
namespace: "{{ opa_server_namespace }}"
name: openshift-service-ca.crt
register: __opa_server_service_ca
- name: Resolve AAP API connection
ansible.builtin.set_fact:
__opa_server_aap_api: "{{ opa_server_aap_url | regex_replace('/$', '') }}/api/controller/v2"
__opa_server_aap_password: "{{ __opa_server_aap_admin.resources[0].data.password | b64decode }}"
__opa_server_aap_settings:
OPA_HOST: "{{ __opa_server_host }}"
OPA_PORT: "{{ opa_server_port | int }}"
OPA_SSL: true
OPA_AUTH_TYPE: Token
OPA_AUTH_CA_CERT: "{{ __opa_server_service_ca.resources[0].data['service-ca.crt'] }}"
OPA_REQUEST_TIMEOUT: "{{ opa_server_aap_request_timeout | float }}"
OPA_REQUEST_RETRIES: "{{ opa_server_aap_request_retries | int }}"
no_log: true
- name: Read AAP policy-as-code settings
ansible.builtin.uri:
url: "{{ __opa_server_aap_api }}/settings/policyascode/"
user: "{{ opa_server_aap_username }}"
password: "{{ __opa_server_aap_password }}"
force_basic_auth: true
validate_certs: "{{ opa_server_aap_validate_certs }}"
register: __opa_server_aap_current
no_log: "{{ opa_server_no_log }}"
# The token reads back as $encrypted$, so it is only resent when unset or rotated
- name: Configure AAP policy-as-code settings
ansible.builtin.uri:
url: "{{ __opa_server_aap_api }}/settings/policyascode/"
method: PATCH
user: "{{ opa_server_aap_username }}"
password: "{{ __opa_server_aap_password }}"
force_basic_auth: true
validate_certs: "{{ opa_server_aap_validate_certs }}"
body_format: json
body: "{{ __opa_server_aap_settings | combine({'OPA_AUTH_TOKEN': __opa_server_token}) }}"
changed_when: true
when: >-
(__opa_server_aap_current.json | dict2items
| selectattr('key', 'in', __opa_server_aap_settings.keys() | list)
| items2dict) != __opa_server_aap_settings
or (__opa_server_aap_current.json.OPA_AUTH_TOKEN | default('')) | length == 0
or __opa_server_token_generated | bool
no_log: "{{ opa_server_no_log }}"
- name: Look up resources to attach policies to
ansible.builtin.uri:
url: "{{ __opa_server_aap_api }}/{{ item.type }}/?name={{ item.name | urlencode }}"
user: "{{ opa_server_aap_username }}"
password: "{{ __opa_server_aap_password }}"
force_basic_auth: true
validate_certs: "{{ opa_server_aap_validate_certs }}"
register: __opa_server_aap_targets
failed_when: >-
__opa_server_aap_targets.status != 200 or __opa_server_aap_targets.json.count != 1
loop: "{{ opa_server_aap_query_paths }}"
loop_control:
label: "{{ item.type }}/{{ item.name }}"
- name: Attach OPA query paths
ansible.builtin.uri:
url: "{{ __opa_server_aap_api }}/{{ item.item.type }}/{{ item.json.results[0].id }}/"
method: PATCH
user: "{{ opa_server_aap_username }}"
password: "{{ __opa_server_aap_password }}"
force_basic_auth: true
validate_certs: "{{ opa_server_aap_validate_certs }}"
body_format: json
body:
opa_query_path: "{{ item.item.query_path }}"
changed_when: true
when: item.json.results[0].opa_query_path | default('') != item.item.query_path
loop: "{{ __opa_server_aap_targets.results }}"
loop_control:
label: "{{ item.item.type }}/{{ item.item.name }} -> {{ item.item.query_path }}"

View File

@@ -0,0 +1,172 @@
---
- name: Create OPA namespace
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Namespace
metadata:
name: "{{ opa_server_namespace }}"
- name: Create OPA policy ConfigMap
kubernetes.core.k8s:
state: present
apply: true
definition:
apiVersion: v1
kind: ConfigMap
metadata:
name: "{{ opa_server_name }}-policies"
namespace: "{{ opa_server_namespace }}"
data: >-
{{ __opa_server_policies
| combine({'aap_policy_config.json': {'aap_policy_config': opa_server_policy_config} | to_json}) }}
- name: Create OPA authorization token Secret
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Secret
metadata:
name: "{{ opa_server_name }}-authz"
namespace: "{{ opa_server_namespace }}"
type: Opaque
stringData:
opa_authz.json: "{{ {'opa_authz': {'token': __opa_server_token}} | to_json }}"
no_log: "{{ opa_server_no_log }}"
- name: Create OPA Service with a service-ca serving certificate
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Service
metadata:
name: "{{ opa_server_name }}"
namespace: "{{ opa_server_namespace }}"
annotations:
service.beta.openshift.io/serving-cert-secret-name: "{{ __opa_server_tls_secret }}"
spec:
selector:
app: "{{ opa_server_name }}"
ports:
- name: https
port: "{{ opa_server_port | int }}"
targetPort: https
- name: Wait for OPA serving certificate
kubernetes.core.k8s_info:
api_version: v1
kind: Secret
namespace: "{{ opa_server_namespace }}"
name: "{{ __opa_server_tls_secret }}"
register: __opa_server_cert
until: __opa_server_cert.resources | length > 0
retries: 30
delay: 10
no_log: true
- name: Create OPA Deployment
kubernetes.core.k8s:
state: present
definition:
apiVersion: apps/v1
kind: Deployment
metadata:
name: "{{ opa_server_name }}"
namespace: "{{ opa_server_namespace }}"
labels:
app: "{{ opa_server_name }}"
spec:
replicas: "{{ opa_server_replicas | int }}"
selector:
matchLabels:
app: "{{ opa_server_name }}"
template:
metadata:
labels:
app: "{{ opa_server_name }}"
annotations:
# Roll pods when policies, config, or the token change
checksum/policies: "{{ (__opa_server_policies | to_json ~ opa_server_policy_config | to_json) | hash('sha256') }}"
checksum/authz: "{{ __opa_server_token | hash('sha256') }}"
spec:
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: opa
image: "{{ opa_server_image }}"
args: >-
{{ [
'run', '--server',
'--addr=0.0.0.0:' ~ opa_server_port,
'--tls-cert-file=/certs/tls.crt',
'--tls-private-key-file=/certs/tls.key',
'--authentication=token',
'--authorization=basic',
'--ignore=.*',
'--log-format=json',
'--log-level=' ~ opa_server_log_level,
]
+ (['--set=decision_logs.console=true'] if opa_server_decision_logs | bool else [])
+ ['/policies', '/authz'] }}
ports:
- name: https
containerPort: "{{ opa_server_port | int }}"
readinessProbe:
httpGet:
path: /health
port: https
scheme: HTTPS
initialDelaySeconds: 3
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: https
scheme: HTTPS
initialDelaySeconds: 10
periodSeconds: 20
resources: "{{ opa_server_resources }}"
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
volumeMounts:
- name: policies
mountPath: /policies
readOnly: true
- name: authz
mountPath: /authz
readOnly: true
- name: certs
mountPath: /certs
readOnly: true
volumes:
- name: policies
configMap:
name: "{{ opa_server_name }}-policies"
- name: authz
secret:
secretName: "{{ opa_server_name }}-authz"
- name: certs
secret:
secretName: "{{ __opa_server_tls_secret }}"
- name: Wait for OPA to be available
kubernetes.core.k8s_info:
api_version: apps/v1
kind: Deployment
namespace: "{{ opa_server_namespace }}"
name: "{{ opa_server_name }}"
register: __opa_server_deploy
until: >-
__opa_server_deploy.resources | length > 0 and
(__opa_server_deploy.resources[0].status.updatedReplicas | default(0)) == opa_server_replicas | int and
(__opa_server_deploy.resources[0].status.availableReplicas | default(0)) == opa_server_replicas | int
retries: "{{ __opa_server_wait_retries }}"
delay: 10

View File

@@ -0,0 +1,32 @@
---
# Deploy an Open Policy Agent server for AAP policy enforcement (policy as code)
# and point AAP's controller at it.
#
# OPA serves TLS with a service-ca certificate and requires a bearer token
# (--authentication=token, system.authz policy). AAP connects in-cluster with
# OPA_SSL=true, OPA_AUTH_TYPE=Token and the service CA bundle.
#
# AAP fails closed: once a resource has an opa_query_path, any OPA error or
# violation blocks its jobs. Nothing is attached unless opa_server_aap_query_paths
# lists it.
#
# All tasks are idempotent (kubernetes.core.k8s state: present).
- name: Resolve OPA bearer token from Vault
ansible.builtin.include_tasks: vault_token.yml
- name: Deploy OPA server
ansible.builtin.include_tasks: deploy.yml
- name: Configure AAP policy enforcement
ansible.builtin.include_tasks: aap.yml
when: opa_server_configure_aap | bool
- name: Display OPA deployment summary
ansible.builtin.debug:
msg:
- "OPA server ready!"
- " Endpoint : https://{{ __opa_server_host }}:{{ opa_server_port }} (in-cluster)"
- " Policies : {{ __opa_server_policies.keys() | list | join(', ') }}"
- " Query paths : aap/policy/decision, aap/smoke/allow_all, aap/smoke/deny_all"
- " Token : Vault {{ opa_server_vault_mount }}/{{ opa_server_vault_path }}"

View File

@@ -0,0 +1,28 @@
---
# Read the OPA bearer token from Vault; generate and store it if missing.
# Sets __opa_server_token and __opa_server_token_generated.
- name: Read OPA token from Vault
community.hashi_vault.vault_kv2_get:
path: "{{ opa_server_vault_path }}"
engine_mount_point: "{{ opa_server_vault_mount }}"
register: __opa_server_vault_read
failed_when: false
no_log: "{{ opa_server_no_log }}"
- name: Resolve OPA token (generate if missing)
ansible.builtin.set_fact:
__opa_server_token_generated: "{{ ((__opa_server_vault_read.secret | default({})).token | default('')) | length == 0 }}"
__opa_server_token: >-
{{ (__opa_server_vault_read.secret | default({})).token
| default(lookup('community.general.random_string', length=48, special=false)) }}
no_log: "{{ opa_server_no_log }}"
- name: Write OPA token to Vault
community.hashi_vault.vault_kv2_write:
path: "{{ opa_server_vault_path }}"
engine_mount_point: "{{ opa_server_vault_mount }}"
data:
token: "{{ __opa_server_token }}"
when: __opa_server_token_generated | bool
no_log: "{{ opa_server_no_log }}"