From bda4178c0ba7f0a5083d16d2983b94c66b3e2039 Mon Sep 17 00:00:00 2001 From: Madhu Kanoor Date: Tue, 7 Jan 2025 14:19:08 -0500 Subject: [PATCH] feat: support PG Notify for event streams using credentials Create a Postgres credential that can store certificates and keys or userid/password at server startup and attach it to Activations that use EventStream. This allows us to test mTLS for Postgres, a sample pg_hba file is attached to this PR. To test this you need to create certificates and keys for * Postgres Server called (server.crt and server.key) * EDA Server called (client.crt and client.key) * You also need to create the CA certificate These files have to be present in tools/docker/postgres_ssl_config/certs The docker-compose file tools/docker/docker-compose-mac-pg-mtls.yml can be used to test mTLS The docker-compose file tools/docker/docker-compose-mac.yml can be used to test userid/password auth chore: rebase --- src/aap_eda/api/serializers/activation.py | 35 ++- src/aap_eda/core/enums.py | 1 + .../commands/create_initial_data.py | 143 +++++++++++ src/aap_eda/settings/default.py | 21 +- .../api/test_activation_with_event_stream.py | 7 +- tools/docker/docker-compose-mac-pg-mtls.yml | 241 ++++++++++++++++++ .../postgres_ssl_config/hba/pg_hba_mtls.conf | 107 ++++++++ .../postgresql-cfg/custom.conf | 3 +- 8 files changed, 532 insertions(+), 26 deletions(-) create mode 100644 tools/docker/docker-compose-mac-pg-mtls.yml create mode 100755 tools/docker/postgres_ssl_config/hba/pg_hba_mtls.conf diff --git a/src/aap_eda/api/serializers/activation.py b/src/aap_eda/api/serializers/activation.py index d727a2ff8..c996218b3 100644 --- a/src/aap_eda/api/serializers/activation.py +++ b/src/aap_eda/api/serializers/activation.py @@ -65,6 +65,16 @@ "rulebook_hash", ] +PG_NOTIFY_DSN = ( + "host={{postgres_db_host}} port={{postgres_db_port}} " + "dbname={{postgres_db_name}} user={{postgres_db_user}} " + "password={{postgres_db_password}} sslmode={{postgres_sslmode}} " + "sslcert={{eda.filename.postgres_sslcert|default(None)}} " + "sslkey={{eda.filename.postgres_sslkey|default(None)}} " + "sslpassword={{postgres_sslpassword|default(None)}} " + "sslrootcert={{eda.filename.postgres_sslrootcert|default(None)}}" +) + @dataclass class VaultData: @@ -72,17 +82,8 @@ class VaultData: password_used: bool = False -def _update_event_stream_source( - validated_data: dict, vault_data: VaultData -) -> str: +def _update_event_stream_source(validated_data: dict) -> str: try: - vault_data.password_used = True - encrypted_dsn = encrypt_string( - password=vault_data.password, - plaintext=settings.PG_NOTIFY_DSN, - vault_id=EDA_SERVER_VAULT_LABEL, - ) - source_mappings = yaml.safe_load(validated_data["source_mappings"]) sources_info = {} for source_map in source_mappings: @@ -91,7 +92,7 @@ def _update_event_stream_source( sources_info[obj.name] = { "ansible.eda.pg_listener": { - "dsn": encrypted_dsn, + "dsn": PG_NOTIFY_DSN, "channels": [obj.channel_name], }, } @@ -329,7 +330,7 @@ def to_representation(self, activation): ) eda_credentials = [ EdaCredentialSerializer(credential).data - for credential in activation.eda_credentials.all() + for credential in activation.eda_credentials.filter(managed=False) ] extra_var = ( replace_vault_data(activation.extra_var) @@ -471,8 +472,14 @@ def create(self, validated_data): if validated_data.get("source_mappings", []): validated_data["rulebook_rulesets"] = _update_event_stream_source( - validated_data, vault_data + validated_data ) + eda_credentials = validated_data.get("eda_credentials", []) + postgres_cred = models.EdaCredential.objects.filter( + name=settings.DEFAULT_SYSTEM_PG_NOTIFY_CREDENTIAL_NAME + ).first() + eda_credentials.append(postgres_cred.id) + validated_data["eda_credentials"] = eda_credentials vault = _get_vault_credential_type() @@ -642,7 +649,7 @@ def to_representation(self, activation): ) eda_credentials = [ EdaCredentialSerializer(credential).data - for credential in activation.eda_credentials.all() + for credential in activation.eda_credentials.filter(managed=False) ] extra_var = ( replace_vault_data(activation.extra_var) diff --git a/src/aap_eda/core/enums.py b/src/aap_eda/core/enums.py index dbbfd5e33..ef4197f63 100644 --- a/src/aap_eda/core/enums.py +++ b/src/aap_eda/core/enums.py @@ -98,6 +98,7 @@ class DefaultCredentialType(DjangoStrEnum): SOURCE_CONTROL = "Source Control" AAP = "Red Hat Ansible Automation Platform" GPG = "GPG Public Key" + POSTGRES = "Postgres" # TODO: rename to "RulebookProcessStatus" or "ParentProcessStatus" diff --git a/src/aap_eda/core/management/commands/create_initial_data.py b/src/aap_eda/core/management/commands/create_initial_data.py index 710306826..68df27114 100644 --- a/src/aap_eda/core/management/commands/create_initial_data.py +++ b/src/aap_eda/core/management/commands/create_initial_data.py @@ -17,6 +17,7 @@ from ansible_base.rbac import permission_registry from ansible_base.rbac.models import DABPermission, RoleDefinition +from django.conf import settings from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ImproperlyConfigured from django.core.management import BaseCommand @@ -841,6 +842,97 @@ "required": ["auth_type", "username", "password", "http_header_key"], } +POSTGRES_CREDENTIAL_INPUTS = { + "fields": [ + { + "id": "postgres_db_host", + "label": "Postgres DB Host", + "help_text": "Postgres DB Server", + }, + { + "id": "postgres_db_port", + "label": "Postgres DB Port", + "help_text": "Postgres DB Port", + "default": "5432", + }, + { + "id": "postgres_db_name", + "label": "Postgres DB Name", + "help_text": "Postgres Database name", + }, + { + "id": "postgres_db_user", + "label": "Postgres DB User", + "help_text": "Postgres Database user", + }, + { + "id": "postgres_db_password", + "label": "Postgres DB Password", + "help_text": "Postgres Database password", + "secret": True, + }, + { + "id": "postgres_sslmode", + "label": "Postgres SSL Mode", + "help_text": "Postgres SSL Mode", + "choices": [ + "disable", + "allow", + "prefer", + "require", + "verify-ca", + "verify-full", + ], + "default": "prefer", + }, + { + "id": "postgres_sslcert", + "label": "Postgres SSL Certificate", + "help_text": "Postgres SSL Certificate", + "multiline": True, + "default": "", + }, + { + "id": "postgres_sslkey", + "label": "Postgres SSL Key", + "help_text": "Postgres SSL Key", + "multiline": True, + "secret": True, + "default": "", + }, + { + "id": "postgres_sslpassword", + "label": "Postgres SSL Password", + "help_text": "Postgres SSL Password for key", + "secret": True, + "default": "", + }, + { + "id": "postgres_sslrootcert", + "label": "Postgres SSL Root Certificate", + "help_text": "Postgres SSL Root Certificate", + "multiline": True, + "default": "", + }, + ] +} + +POSTGRES_CREDENTIAL_INJECTORS = { + "extra_vars": { + "postgres_db_host": "{{ postgres_db_host }}", + "postgres_db_port": "{{ postgres_db_port }}", + "postgres_db_name": "{{ postgres_db_name }}", + "postgres_db_user": "{{ postgres_db_user }}", + "postgres_db_password": "{{ postgres_db_password }}", + "postgres_sslpassword": "{{ postgres_sslpassword | default(None) }}", + "postgres_sslmode": "{{ postgres_sslmode }}", + }, + "file": { + "template.postgres_sslcert": "{{ postgres_sslcert }}", + "template.postgres_sslrootcert": "{{ postgres_sslrootcert }}", + "template.postgres_sslkey": "{{ postgres_sslkey }}", + }, +} CREDENTIAL_TYPES = [ { "name": enums.DefaultCredentialType.SOURCE_CONTROL, @@ -1014,6 +1106,14 @@ "the Basic authentication." ), }, + { + "name": enums.DefaultCredentialType.POSTGRES, + "kind": "cloud", + "namespace": "eda", + "inputs": POSTGRES_CREDENTIAL_INPUTS, + "injectors": POSTGRES_CREDENTIAL_INJECTORS, + "managed": True, + }, ] @@ -1046,6 +1146,7 @@ class Command(BaseCommand): @transaction.atomic def handle(self, *args, **options): self._preload_credential_types() + self._update_postgres_credentials() self._copy_registry_credentials() self._copy_scm_credentials() self._create_org_roles() @@ -1167,6 +1268,48 @@ def _copy_scm_credentials(self): "Control eda-credentials" ) + def _update_postgres_credentials(self): + cred_type = models.CredentialType.objects.filter( + name=enums.DefaultCredentialType.POSTGRES + ).first() + inputs = { + "postgres_db_host": settings.ACTIVATION_DB_HOST, + "postgres_db_port": settings.DATABASES["default"]["PORT"], + "postgres_db_name": settings.DATABASES["default"]["NAME"], + "postgres_db_user": settings.DATABASES["default"]["USER"], + "postgres_db_password": settings.DATABASES["default"]["PASSWORD"], + "postgres_sslmode": settings.PGSSLMODE, + "postgres_sslcert": "", + "postgres_sslkey": "", + "postgres_sslrootcert": "", + } + + if settings.PGSSLCERT: + inputs["postgres_sslcert"] = self._read_file(settings.PGSSLCERT) + + if settings.PGSSLKEY: + inputs["postgres_sslkey"] = self._read_file(settings.PGSSLKEY) + + if settings.PGSSLROOTCERT: + inputs["postgres_sslrootcert"] = self._read_file( + settings.PGSSLROOTCERT + ) + + models.EdaCredential.objects.update_or_create( + name=settings.DEFAULT_SYSTEM_PG_NOTIFY_CREDENTIAL_NAME, + defaults={ + "description": "Default PG Notify Credentials", + "managed": True, + "credential_type": cred_type, + "inputs": inputs_to_store(inputs), + "organization": get_default_organization(), + }, + ) + + def _read_file(self, name): + with open(name) as f: + return f.read() + def _create_org_roles(self): org_ct = ContentType.objects.get(model="organization") created = updated = 0 diff --git a/src/aap_eda/settings/default.py b/src/aap_eda/settings/default.py index f1980dcec..0a48fb5af 100644 --- a/src/aap_eda/settings/default.py +++ b/src/aap_eda/settings/default.py @@ -781,15 +781,6 @@ def get_rulebook_process_log_level() -> RulebookProcessLogLevel: "ACTIVATION_DB_HOST", "host.containers.internal" ) -_DEFAULT_PG_NOTIFY_DSN = ( - f"host={ACTIVATION_DB_HOST} " - f"port={DATABASES['default']['PORT']} " - f"dbname={DATABASES['default']['NAME']} " - f"user={DATABASES['default']['USER']} " - f"password={DATABASES['default']['PASSWORD']}" -) - -PG_NOTIFY_DSN = settings.get("PG_NOTIFY_DSN", _DEFAULT_PG_NOTIFY_DSN) PG_NOTIFY_TEMPLATE_RULEBOOK = settings.get("PG_NOTIFY_TEMPLATE_RULEBOOK", None) SAFE_PLUGINS_FOR_PORT_FORWARD = settings.get( @@ -806,7 +797,11 @@ def get_rulebook_process_log_level() -> RulebookProcessLogLevel: f"port={DATABASES['default']['PORT']} " f"dbname={DATABASES['default']['NAME']} " f"user={DATABASES['default']['USER']} " - f"password={DATABASES['default']['PASSWORD']}" + f"password={DATABASES['default']['PASSWORD']} " + f"sslmode={settings.get('PGSSLMODE', default='prefer')} " + f"sslcert={settings.get('PGSSLCERT', default='None')} " + f"sslkey={settings.get('PGSSLKEY', default='None')} " + f"sslrootcert={settings.get('PGSSLROOTCERT', default='None')}" ) PG_NOTIFY_DSN_SERVER = settings.get( "PG_NOTIFY_DSN_SERVER", _DEFAULT_PG_NOTIFY_DSN_SERVER @@ -823,3 +818,9 @@ def get_rulebook_process_log_level() -> RulebookProcessLogLevel: MAX_PG_NOTIFY_MESSAGE_SIZE = int( settings.get("MAX_PG_NOTIFY_MESSAGE_SIZE", 6144) ) + +DEFAULT_SYSTEM_PG_NOTIFY_CREDENTIAL_NAME = "_DEFAULT_EDA_PG_NOTIFY_CREDS" +PGSSLMODE = settings.get("PGSSLMODE", default="prefer") +PGSSLCERT = settings.get("PGSSLCERT", default="") +PGSSLKEY = settings.get("PGSSLKEY", default="") +PGSSLROOTCERT = settings.get("PGSSLROOTCERT", default="") diff --git a/tests/integration/api/test_activation_with_event_stream.py b/tests/integration/api/test_activation_with_event_stream.py index f76bb9b68..fb6c6ee9c 100644 --- a/tests/integration/api/test_activation_with_event_stream.py +++ b/tests/integration/api/test_activation_with_event_stream.py @@ -282,8 +282,11 @@ def create_activation(fks: dict): @pytest.mark.django_db def test_create_activation_with_event_stream( - admin_client: APIClient, preseed_credential_types + admin_client: APIClient, + preseed_credential_types, + create_initial_data_command, ): + create_initial_data_command.handle() fks = create_activation_related_data(["demo"]) test_activation = TEST_ACTIVATION.copy() test_activation["decision_environment_id"] = fks["decision_environment_id"] @@ -674,12 +677,14 @@ def test_create_activation_with_duplicate_event_stream_name( def test_bad_src_activation_with_event_stream( admin_client: APIClient, preseed_credential_types, + create_initial_data_command, source_tuples, rulesets, status_code, message, error_key, ): + create_initial_data_command.handle() names = [event_stream_name for _, event_stream_name in source_tuples] fks = create_activation_related_data(names, True, rulesets) test_activation = TEST_ACTIVATION.copy() diff --git a/tools/docker/docker-compose-mac-pg-mtls.yml b/tools/docker/docker-compose-mac-pg-mtls.yml new file mode 100644 index 000000000..47c746198 --- /dev/null +++ b/tools/docker/docker-compose-mac-pg-mtls.yml @@ -0,0 +1,241 @@ +x-environment: + &common-env + - EDA_DB_HOST=postgres + - EDA_MQ_HOST=${EDA_MQ_HOST:-redis} + - EDA_MQ_PORT=${EDA_MQ_PORT:-6379} + - EDA_MQ_REDIS_HA_CLUSTER_HOSTS=${EDA_MQ_REDIS_HA_CLUSTER_HOSTS:-} + - DJANGO_SETTINGS_MODULE=aap_eda.settings.default + - EDA_DB_PASSWORD=secret + - EDA_SECRET_KEY=secret + - EDA_ALLOWED_HOSTS=['*'] + - EDA_DEPLOYMENT_TYPE=podman + - EDA_WEBSOCKET_BASE_URL=${EDA_WEBSOCKET_BASE_URL:-ws://host.containers.internal:8001} + - EDA_WEBSOCKET_SSL_VERIFY=no + - EDA_PODMAN_SOCKET_URL="unix:///run/podman/podman.sock" + - EDA_CONTROLLER_URL=${EDA_CONTROLLER_URL:-https://awx-example.com} + - EDA_CONTROLLER_SSL_VERIFY=${EDA_CONTROLLER_SSL_VERIFY:-yes} + - EDA_PROTOCOL=http + - EDA_HOST=${EDA_HOST:-eda-api:8000} + - EDA_SERVER=http://${EDA_HOST:-eda-api:8000} + - EDA_ANSIBLE_RULEBOOK_LOG_LEVEL=${EDA_ANSIBLE_RULEBOOK_LOG_LEVEL:-'-v'} + - EDA_CONTAINER_NAME_PREFIX=${EDA_CONTAINER_NAME_PREFIX:-eda} + - EDA_PODMAN_ENV_VARS=${EDA_PODMAN_ENV_VARS:-'@none None'} + - EDA_MAX_RUNNING_ACTIVATIONS=${EDA_MAX_RUNNING_ACTIVATIONS:-5} + - EDA_ACTIVATION_RESTART_SECONDS_ON_COMPLETE=${EDA_ACTIVATION_RESTART_SECONDS_ON_COMPLETE:-60} + - EDA_ACTIVATION_RESTART_SECONDS_ON_FAILURE=${EDA_ACTIVATION_RESTART_SECONDS_ON_FAILURE:-60} + - EDA_ANSIBLE_BASE_JWT_VALIDATE_CERT=${EDA_ANSIBLE_BASE_JWT_VALIDATE_CERT:-False} + - EDA_ANSIBLE_BASE_JWT_KEY=${EDA_ANSIBLE_BASE_JWT_KEY:-'https://localhost'} + - EDA_DEBUG=${EDA_DEBUG:-True} + - EDA_ALLOW_LOCAL_RESOURCE_MANAGEMENT=${EDA_ALLOW_LOCAL_RESOURCE_MANAGEMENT:-True} + - EDA_ALLOW_LOCAL_ASSIGNING_JWT_ROLES=${EDA_ALLOW_LOCAL_ASSIGNING_JWT_ROLES:-True} + - EDA_ALLOW_SHARED_RESOURCE_CUSTOM_ROLES=${EDA_ALLOW_SHARED_RESOURCE_CUSTOM_ROLES:-True} + - EDA_ANSIBLE_RULEBOOK_FLUSH_AFTER=${EDA_ANSIBLE_RULEBOOK_FLUSH_AFTER:-100} + - EDA_DEFAULT_QUEUE_TIMEOUT=${EDA_DEFAULT_QUEUE_TIMEOUT:-300} + - EDA_DEFAULT_RULEBOOK_QUEUE_TIMEOUT=${EDA_DEFAULT_RULEBOOK_QUEUE_TIMEOUT:-120} + - EDA_RESOURCE_SERVER__URL=${EDA_RESOURCE_SERVER__URL:-'@none None'} + - EDA_RESOURCE_SERVER__SECRET_KEY=${EDA_RESOURCE_SERVER__SECRET_KEY:-'@none None'} + - EDA_RESOURCE_SERVER__VALIDATE_HTTPS=${EDA_RESOURCE_SERVER__VALIDATE_HTTPS:-False} + - EDA_RESOURCE_JWT_USER_ID=${EDA_RESOURCE_JWT_USER_ID:-'@none None'} + - EDA_ANSIBLE_BASE_MANAGED_ROLE_REGISTRY=${EDA_ANSIBLE_BASE_MANAGED_ROLE_REGISTRY:-@json {}} + - EDA_SERVER_UUID=edgecafe-beef-feed-fade-decadeedgecafe + - EDA_EVENT_STREAM_BASE_URL=${EDA_EVENT_STREAM_BASE_URL:-https://localhost:8443/edgecafe-beef-feed-fade-decadeedgecafe/} + - EDA_EVENT_STREAM_MTLS_BASE_URL=${EDA_EVENT_STREAM_MTLS_BASE_URL:-https://localhost:8443/mtls/edgecafe-beef-feed-fade-decadeedgecafe/} + - EDA_WEBHOOK_HOST=${EDA_WEBHOOK_HOST:-eda-webhook-api:8000} + - EDA_WEBHOOK_SERVER=http://${EDA_WEBHOOK_HOST:-eda-webhook-api:8000} + - EDA_STATIC_URL=${EDA_STATIC_URL:-api/eda/v1/static/} + - SSL_CERTIFICATE=${SSL_CERTIFICATE:-/certs/wildcard.crt} + - SSL_CERTIFICATE_KEY=${SSL_CERTIFICATE_KEY:-/certs/wildcard.key} + - SSL_CLIENT_CERTIFICATE=${SSL_CLIENT_CERTIFICATE:-/certs/client.crt} + - EDA_PGSSLMODE=${EDA_PGSSLMODE:-"verify-full"} + - EDA_PGSSLCERT=${EDA_PGSSLCERT:-"/pg_certs/client.crt"} + - EDA_PGSSLKEY=${EDA_PGSSLKEY:-"/pg_certs/client.key"} + - EDA_PGSSLROOTCERT=${EDA_PGSSLROOTCERT:-"/pg_certs/ca.crt"} + + +services: + postgres: + image: ${EDA_POSTGRES_IMAGE:-quay.io/sclorg/postgresql-15-c9s}:${EDA_POSTGRES_VERSION:-latest} + environment: + POSTGRESQL_USER: eda + POSTGRESQL_PASSWORD: secret + POSTGRESQL_ADMIN_PASSWORD: secret + POSTGRESQL_DATABASE: eda + ports: + - '${EDA_PG_PORT:-5432}:5432' + volumes: + - 'postgres_data:/var/lib/pgsql/data' + - "${PWD}/tools/docker/postgres_ssl_config/postgresql-cfg:/opt/app-root/src/postgresql-cfg:z" + - "${PWD}/tools/docker/postgres_ssl_config/certs:/opt/app-root/src/certs:z" + - "${PWD}/tools/docker/postgres_ssl_config/hba:/opt/app-root/src/hba:z" + healthcheck: + test: [ 'CMD', 'pg_isready', '-U', 'postgres' ] + interval: 5s + timeout: 5s + retries: 3 + start_period: 5s + + redis: + image: ${EDA_REDIS_IMAGE:-docker.io/library/redis}:${EDA_REDIS_VERSION:-6.2.14} + ports: + - '${EDA_REDIS_PORT:-6379}:6379' + healthcheck: + test: [ 'CMD', 'redis-cli', 'ping' ] + interval: 5s + timeout: 5s + retries: 3 + start_period: 5s + + eda-api: + image: ${EDA_IMAGE:-quay.io/ansible/eda-server}:${EDA_IMAGE_VERSION:-main} + environment: *common-env + security_opt: + - label=disable + command: + - /bin/bash + - -c + - >- + aap-eda-manage migrate + && ANSIBLE_REVERSE_RESOURCE_SYNC=false aap-eda-manage create_initial_data + && ANSIBLE_REVERSE_RESOURCE_SYNC=false scripts/create_superuser.sh + && aap-eda-manage runserver 0.0.0.0:8000 + ports: + - '${EDA_API_PORT:-8000}:8000' + depends_on: + redis: + condition: service_healthy + postgres: + condition: service_healthy + healthcheck: + test: [ 'CMD', 'curl', '-q', 'http://localhost:8000/_healthz' ] + interval: 30s + timeout: 5s + retries: 10 + volumes: + - ${EDA_HOST_PODMAN_SOCKET_URL:-/run/user/501/podman/podman.sock}:/run/podman/podman.sock:z + - "${PWD}/tools/docker/postgres_ssl_config/certs:/pg_certs:z" + + eda-ws: + image: ${EDA_IMAGE:-quay.io/ansible/eda-server}:${EDA_IMAGE_VERSION:-main} + environment: *common-env + command: + - /bin/bash + - -c + - >- + aap-eda-manage runserver 0.0.0.0:8000 + ports: + - '${EDA_WS_PORT:-8001}:8000' + depends_on: + eda-api: + condition: service_healthy + volumes: + - "${PWD}/tools/docker/postgres_ssl_config/certs:/pg_certs:z" + + eda-default-worker: + user: "${EDA_POD_USER_ID:-0}" + image: ${EDA_IMAGE:-quay.io/ansible/eda-server}:${EDA_IMAGE_VERSION:-main} + deploy: + replicas: ${EDA_DEFAULT_WORKERS:-1} + environment: *common-env + security_opt: + - label=disable + command: + - /bin/bash + - -c + - >- + aap-eda-manage rqworker + --worker-class aap_eda.core.tasking.DefaultWorker + depends_on: + eda-api: + condition: service_healthy + restart: always + volumes: + - ${EDA_HOST_PODMAN_SOCKET_URL:-/run/user/501/podman/podman.sock}:/run/podman/podman.sock:z + - "${PWD}/tools/docker/postgres_ssl_config/certs:/pg_certs:z" + + eda-activation-worker: + user: "${EDA_POD_USER_ID:-0}" + image: ${EDA_IMAGE:-quay.io/ansible/eda-server}:${EDA_IMAGE_VERSION:-main} + deploy: + replicas: ${EDA_ACTIVATION_WORKERS:-2} + environment: *common-env + security_opt: + - label=disable + command: + - /bin/bash + - -c + - >- + aap-eda-manage rqworker + --worker-class aap_eda.core.tasking.ActivationWorker + depends_on: + eda-api: + condition: service_healthy + restart: always + volumes: + - ${EDA_HOST_PODMAN_SOCKET_URL:-/run/user/501/podman/podman.sock}:/run/podman/podman.sock:z + - "${PWD}/tools/docker/postgres_ssl_config/certs:/pg_certs:z" + + eda-scheduler: + image: ${EDA_IMAGE:-quay.io/ansible/eda-server}:${EDA_IMAGE_VERSION:-main} + environment: *common-env + command: + - /bin/bash + - -c + - >- + aap-eda-manage scheduler + depends_on: + eda-api: + condition: service_healthy + restart: always + volumes: + - "${PWD}/tools/docker/postgres_ssl_config/certs:/pg_certs:z" + + eda-webhook-api: + image: ${EDA_IMAGE:-quay.io/ansible/eda-server}:${EDA_IMAGE_VERSION:-main} + environment: *common-env + security_opt: + - label=disable + command: + - /bin/bash + - -c + - >- + aap-eda-manage runserver 0.0.0.0:8000 + ports: + - '8555:8000' + depends_on: + eda-api: + condition: service_healthy + healthcheck: + test: [ 'CMD', 'curl', '-q', 'http://0.0.0.0:8000/_healthz' ] + interval: 30s + timeout: 5s + retries: 10 + start_period: 15s + volumes: + - "${PWD}/tools/docker/postgres_ssl_config/certs:/pg_certs:z" + + squid: + image: ${EDA_SQUID_IMAGE:-quay.io/openshifttest/squid-proxy}:${EDA_SQUID_VERSION:-1.2.0} + profiles: + - proxy + hostname: squid + volumes: + - ./squid/squid.conf:/etc/squid/squid.conf + - ./squid/htpass:/etc/squid/htpass + ports: + - '${EDA_PROXY_PORT:-3128}:3128' + + eda-ui: + image: ${EDA_UI_IMAGE:-quay.io/ansible/eda-ui:main} + environment: *common-env + ports: + - '${EDA_UI_PORT:-8443}:443' + volumes: + - './my_certs:/tmp/my_certs:z' + - './nginx/certs:/certs:z' + - './nginx/default.conf.template:/etc/nginx/templates/default.conf.template:z' + depends_on: + eda-webhook-api: + condition: service_healthy + +volumes: + postgres_data: {} diff --git a/tools/docker/postgres_ssl_config/hba/pg_hba_mtls.conf b/tools/docker/postgres_ssl_config/hba/pg_hba_mtls.conf new file mode 100755 index 000000000..5a7d006f2 --- /dev/null +++ b/tools/docker/postgres_ssl_config/hba/pg_hba_mtls.conf @@ -0,0 +1,107 @@ +# PostgreSQL Client Authentication Configuration File +# =================================================== +# +# Refer to the "Client Authentication" section in the PostgreSQL +# documentation for a complete description of this file. A short +# synopsis follows. +# +# This file controls: which hosts are allowed to connect, how clients +# are authenticated, which PostgreSQL user names they can use, which +# databases they can access. Records take one of these forms: +# +# local DATABASE USER METHOD [OPTIONS] +# host DATABASE USER ADDRESS METHOD [OPTIONS] +# hostssl DATABASE USER ADDRESS METHOD [OPTIONS] +# hostnossl DATABASE USER ADDRESS METHOD [OPTIONS] +# hostgssenc DATABASE USER ADDRESS METHOD [OPTIONS] +# hostnogssenc DATABASE USER ADDRESS METHOD [OPTIONS] +# +# (The uppercase items must be replaced by actual values.) +# +# The first field is the connection type: +# - "local" is a Unix-domain socket +# - "host" is a TCP/IP socket (encrypted or not) +# - "hostssl" is a TCP/IP socket that is SSL-encrypted +# - "hostnossl" is a TCP/IP socket that is not SSL-encrypted +# - "hostgssenc" is a TCP/IP socket that is GSSAPI-encrypted +# - "hostnogssenc" is a TCP/IP socket that is not GSSAPI-encrypted +# +# DATABASE can be "all", "sameuser", "samerole", "replication", a +# database name, or a comma-separated list thereof. The "all" +# keyword does not match "replication". Access to replication +# must be enabled in a separate record (see example below). +# +# USER can be "all", a user name, a group name prefixed with "+", or a +# comma-separated list thereof. In both the DATABASE and USER fields +# you can also write a file name prefixed with "@" to include names +# from a separate file. +# +# ADDRESS specifies the set of hosts the record matches. It can be a +# host name, or it is made up of an IP address and a CIDR mask that is +# an integer (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that +# specifies the number of significant bits in the mask. A host name +# that starts with a dot (.) matches a suffix of the actual host name. +# Alternatively, you can write an IP address and netmask in separate +# columns to specify the set of hosts. Instead of a CIDR-address, you +# can write "samehost" to match any of the server's own IP addresses, +# or "samenet" to match any address in any subnet that the server is +# directly connected to. +# +# METHOD can be "trust", "reject", "md5", "password", "scram-sha-256", +# "gss", "sspi", "ident", "peer", "pam", "ldap", "radius" or "cert". +# Note that "password" sends passwords in clear text; "md5" or +# "scram-sha-256" are preferred since they send encrypted passwords. +# +# OPTIONS are a set of options for the authentication in the format +# NAME=VALUE. The available options depend on the different +# authentication methods -- refer to the "Client Authentication" +# section in the documentation for a list of which options are +# available for which authentication methods. +# +# Database and user names containing spaces, commas, quotes and other +# special characters must be quoted. Quoting one of the keywords +# "all", "sameuser", "samerole" or "replication" makes the name lose +# its special character, and just match a database or username with +# that name. +# +# This file is read on server startup and when the server receives a +# SIGHUP signal. If you edit the file on a running system, you have to +# SIGHUP the server for the changes to take effect, run "pg_ctl reload", +# or execute "SELECT pg_reload_conf()". +# +# Put your actual configuration here +# ---------------------------------- +# +# If you want to allow non-local connections, you need to add more +# "host" records. In that case you will also need to make PostgreSQL +# listen on a non-local interface via the listen_addresses +# configuration parameter, or via the -i or -h command line switches. + +# CAUTION: Configuring the system for local "trust" authentication +# allows any local user to connect as any PostgreSQL user, including +# the database superuser. If you do not trust all your local users, +# use another authentication method. + + +# TYPE DATABASE USER ADDRESS METHOD + +# "local" is for Unix domain socket connections only +local all all trust +# Allow replication connections from localhost, by a user with the +# replication privilege. +local replication all trust +host replication all 127.0.0.1/32 trust +host replication all ::1/128 trust + +# +# Custom OpenShift configuration starting at this point. +# + +# Allow connections from all hosts. +#host all all all md5 + +# Allow replication connections from all hosts. +#host replication all all md5 + +hostssl all all ::/0 cert clientcert=verify-full +hostssl all all 0.0.0.0/0 cert clientcert=verify-full diff --git a/tools/docker/postgres_ssl_config/postgresql-cfg/custom.conf b/tools/docker/postgres_ssl_config/postgresql-cfg/custom.conf index 698ca6440..258b36edd 100644 --- a/tools/docker/postgres_ssl_config/postgresql-cfg/custom.conf +++ b/tools/docker/postgres_ssl_config/postgresql-cfg/custom.conf @@ -3,4 +3,5 @@ ssl_cert_file = '/opt/app-root/src/certs/server.crt' ssl_key_file = '/opt/app-root/src/certs/server.key' ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' ssl_ca_file = '/opt/app-root/src/certs/ca.crt' -ssl_prefer_server_ciphers = on \ No newline at end of file +ssl_prefer_server_ciphers = on +hba_file = '/opt/app-root/src/hba/pg_hba_mtls.conf'