From 759d8f112f4c76e3884925d79d108726d485b6ba Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Sat, 1 Feb 2025 18:03:36 -0600 Subject: [PATCH] ansible: Deploy ARA [ARA Records Ansible][0] is a results storage system for Ansible. It provides a convenient UI for tracking Ansible playbooks and tasks. The data are populated by an Ansible callback plugin. ARA is a fairly simple Python+Django application. It needs a database to store Ansible results, so we've connected it to the main PostgreSQL database and configured it to connect and authenticate using mTLS. Rather than mess with managing and distributing a static password for ARA clients, I've configured Autheliad to allow anonymous access to post data to the ARA API from within the private network or the Kubernetes cluster. Access to the web UI does require authentication. [0]: https://ara.recordsansible.org/ --- ansible/.gitignore | 1 + ansible/ara.yaml | 87 ++++++++++++++++++++++++++++++++++++++ ansible/ara/settings.toml | 38 +++++++++++++++++ ansible/ingress.yaml | 32 ++++++++++++++ ansible/kustomization.yaml | 58 +++++++++++++++++++++++++ ansible/namespace.yaml | 6 +++ ansible/postgres-cert.yaml | 12 ++++++ ansible/secrets.yaml | 19 +++++++++ authelia/configuration.yml | 13 ++++++ 9 files changed, 266 insertions(+) create mode 100644 ansible/.gitignore create mode 100644 ansible/ara.yaml create mode 100644 ansible/ara/settings.toml create mode 100644 ansible/ingress.yaml create mode 100644 ansible/kustomization.yaml create mode 100644 ansible/namespace.yaml create mode 100644 ansible/postgres-cert.yaml create mode 100644 ansible/secrets.yaml diff --git a/ansible/.gitignore b/ansible/.gitignore new file mode 100644 index 0000000..2920ec7 --- /dev/null +++ b/ansible/.gitignore @@ -0,0 +1 @@ +ara/.secrets.toml diff --git a/ansible/ara.yaml b/ansible/ara.yaml new file mode 100644 index 0000000..4f8bd4d --- /dev/null +++ b/ansible/ara.yaml @@ -0,0 +1,87 @@ +apiVersion: v1 +kind: Service +metadata: + name: ara + labels: &labels + app.kubernetes.io/name: ara + app.kubernetes.io/component: ara +spec: + selector: *labels + type: ClusterIP + ports: + - name: http + port: 8000 + targetPort: 8000 + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ara + labels: &labels + app.kubernetes.io/name: ara + app.kubernetes.io/component: ara +spec: + selector: + matchLabels: *labels + template: + metadata: + labels: *labels + spec: + enableServiceLinks: false + containers: + - name: ara-api + image: quay.io/recordsansible/ara-api + env: + - name: ARA_BASE_DIR + value: /etc/ara + - name: ARA_SETTINGS + value: /etc/ara/settings.toml + - name: SECRETS_FOR_DYNACONF + value: /etc/ara/.secrets.toml + ports: + - containerPort: 8000 + name: http + readinessProbe: &probe + httpGet: + port: 8000 + path: /api/ + httpHeaders: + - name: Host + value: ara.ansible.pyrocufflink.blue + failureThreshold: 3 + periodSeconds: 60 + successThreshold: 1 + timeoutSeconds: 5 + startupProbe: + <<: *probe + failureThreshold: 30 + initialDelaySeconds: 1 + periodSeconds: 1 + timeoutSeconds: 1 + volumeMounts: + - mountPath: /etc/ara/settings.toml + name: config + subPath: settings.toml + readOnly: true + - mountPath: /etc/ara/.secrets.toml + name: secrets + subPath: .secrets.toml + readOnly: true + - mountPath: /tmp + name: tmp + subPath: tmp + securityContext: + runAsNonRoot: true + runAsUser: 7653 + runAsGroup: 7653 + volumes: + - name: config + configMap: + name: ara + - name: secrets + secret: + secretName: ara + - name: tmp + emptyDir: + medium: Memory diff --git a/ansible/ara/settings.toml b/ansible/ara/settings.toml new file mode 100644 index 0000000..448bbd1 --- /dev/null +++ b/ansible/ara/settings.toml @@ -0,0 +1,38 @@ +[default] +ALLOWED_HOSTS = [ + 'ara.ansible.pyrocufflink.blue', +] +LOG_LEVEL = 'INFO' +TIME_ZONE = 'UTC' + +EXTERNAL_AUTH = true +READ_LOGIN_REQUIRED = false +WRITE_LOGIN_REQUIRED = false + +DATABASE_ENGINE = 'django.db.backends.postgresql' +DATABASE_HOST = 'postgresql.pyrocufflink.blue' +DATABASE_NAME = 'ara' +DATABASE_USER = 'ara' + +[default.DATABASE_OPTIONS] +sslmode = 'verify-full' +sslcert = '/run/secrets/ara/postgresql/tls.crt' +sslkey = '/run/secrets/ara/postgresql/tls.key' +sslrootcert = '/run/dch-ca/dch-root-ca.crt' + +[default.LOGGING] +version = 1 +disable_existing_loggers = false + +[default.LOGGING.formatters.normal] +format = '%(levelname)s %(name)s: %(message)s' + +[default.LOGGING.handlers.console] +class = 'logging.StreamHandler' +formatter = 'normal' +level = 'INFO' + +[default.LOGGING.loggers.ara] +handlers = ['console'] +level = 'INFO' +propagate = false diff --git a/ansible/ingress.yaml b/ansible/ingress.yaml new file mode 100644 index 0000000..6c1b7f4 --- /dev/null +++ b/ansible/ingress.yaml @@ -0,0 +1,32 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ara + labels: + app.kubernetes.io/name: ara + app.kubernetes.io/component: ara + annotations: + cert-manager.io/cluster-issuer: dch-ca + nginx.ingress.kubernetes.io/proxy-buffer-size: "8k" + nginx.ingress.kubernetes.io/auth-method: GET + nginx.ingress.kubernetes.io/auth-url: http://authelia.authelia.svc.cluster.local:9091/api/verify + nginx.ingress.kubernetes.io/auth-signin: https://auth.pyrocufflink.blue/?rm=$request_method + nginx.ingress.kubernetes.io/auth-snippet: | + proxy_set_header X-Forwarded-Method $request_method; +spec: + ingressClassName: nginx + tls: + - hosts: + - ara.ansible.pyrocufflink.blue + secretName: ara-cert + rules: + - host: ara.ansible.pyrocufflink.blue + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: ara + port: + name: http diff --git a/ansible/kustomization.yaml b/ansible/kustomization.yaml new file mode 100644 index 0000000..d2317b3 --- /dev/null +++ b/ansible/kustomization.yaml @@ -0,0 +1,58 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +labels: +- pairs: + app.kubernetes.io/instance: ansible + includeSelectors: true + includeTemplates: true +- pairs: + app.kubernetes.io/part-of: ansible + +namespace: ansible + +resources: +- ../dch-root-ca +- secrets.yaml +- namespace.yaml +- ara.yaml +- postgres-cert.yaml +- ingress.yaml + +configMapGenerator: +- name: ara + files: + - ara/settings.toml + options: + labels: + app.kubernetes.io/name: ara + +patches: +- patch: |- + apiVersion: apps/v1 + kind: Deployment + metadata: + name: ara + spec: + template: + spec: + containers: + - name: ara-api + volumeMounts: + - mountPath: /run/dch-ca/dch-root-ca.crt + name: dch-root-ca + subPath: dch-root-ca.crt + readOnly: true + - mountPath: /run/secrets/ara/postgresql + name: postgresql-cert + readOnly: true + securityContext: + fsGroup: 7653 + volumes: + - name: postgresql-cert + secret: + secretName: ara-postgres-cert + defaultMode: 0640 + - name: dch-root-ca + configMap: + name: dch-root-ca diff --git a/ansible/namespace.yaml b/ansible/namespace.yaml new file mode 100644 index 0000000..50d6273 --- /dev/null +++ b/ansible/namespace.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: ansible + labels: + app.kubernetes.io/name: ansible diff --git a/ansible/postgres-cert.yaml b/ansible/postgres-cert.yaml new file mode 100644 index 0000000..2075c6d --- /dev/null +++ b/ansible/postgres-cert.yaml @@ -0,0 +1,12 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: ara-postgres-cert +spec: + commonName: ara + privateKey: + algorithm: ECDSA + secretName: ara-postgres-cert + issuerRef: + name: postgresql-ca + kind: ClusterIssuer diff --git a/ansible/secrets.yaml b/ansible/secrets.yaml new file mode 100644 index 0000000..c75eec1 --- /dev/null +++ b/ansible/secrets.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + name: ara + namespace: ansible + labels: + app.kubernetes.io/name: ara + app.kubernetes.io/component: ara +spec: + encryptedData: + .secrets.toml: AgAiObM2t8Cmr87pMRhZN4RBqb4soEdG0OJeAmaUCl//in2LihZyOHKu5RDeLBaPneoX5G/t5eQX7KkPCW7hffjBSngVbPMZ+U8Txb1IY3uFFHGAPnJBXwHo6FgRq5uKt3LQ6UmtWLoIF4ZB4K0f/g5BHR3N6FIah/x5pKrnU9lOltVgDFSEd7Ewgrj8AorASdE7ZxBAGuUPNktQZZ9MS0d2YiOhgfZiGHDnLZoqk0osDOZzKJX47yiYg5SR4NHoGzd7gfSvBtbrU4SIQCUGZJYtOKeUGaNtCgKnLe3oCphqV3izUY1JudVF8eN5MDgUH5vG2GcqDqMYTX2oRuk+rgRjiMJTkxQ/OZlbrXGxUocTR51EoM01vgjvvCsqaE3R5MhcKE7oOHrqTXzhDY6UzBPUu6QWrkWWMegBeIVCWA2ID3m8CPljyA0oZuClqVZpx/23cs3aHcyl2vye5ey3ca5QzLkkmKK7826H3przUYdwti9gMNp0sPszsDOTzfmN9iZD33a+WvEPfbf7+ZBXBAnoH06pLXeCGc43Q2S619xdHRw4caVGeczZAWYVjlnq8BUQGmHU2Ra5W6dzYM+i3vUimJRiuzeaG+APgY4KIdZNEjPguiwg93g9NtDWgap1h5rF3Yx4nEQelEN0pNAqT8CP67gW1Kp+wjNRNHkz45Ld3mJbMsvFqjZG5cCqk1s67mBNxhani0HBJM3vEV36wnDBzWwSmemuMUdjrG0zFwQHHeZwQe7oR+9XNz1DLPxvvp6KfEbHA3YMagQa2RYxN/C9APBBC+dmk6+TJn1n + template: + metadata: + name: ara + namespace: ansible + labels: + app.kubernetes.io/name: ara + app.kubernetes.io/component: ara diff --git a/authelia/configuration.yml b/authelia/configuration.yml index 3865e11..cd90b39 100644 --- a/authelia/configuration.yml +++ b/authelia/configuration.yml @@ -5,6 +5,9 @@ access_control: networks: - 172.30.0.0/26 - 172.31.1.0/24 + - name: cluster + networks: + - 10.149.0.0/16 rules: - domain: paperless.pyrocufflink.blue policy: two_factor @@ -54,6 +57,16 @@ access_control: resources: - '^/submit/.*' policy: bypass + - domain: ara.ansible.pyrocufflink.blue + networks: + - internal + - cluster + resources: + - '^/api/.*' + methods: + - POST + - PATCH + policy: bypass authentication_backend: ldap: