diff --git a/dsf-podman-dev-setup/bpe/kube.yaml b/dsf-podman-dev-setup/bpe/kube.yaml new file mode 100644 index 000000000..3cf412639 --- /dev/null +++ b/dsf-podman-dev-setup/bpe/kube.yaml @@ -0,0 +1,300 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: dsf-fhir-db-passwords +type: Opaque +stringData: + db_liquibase.password: "PW99vXYyTbBTGzemQbCnVb4Bce7xuzcU77W5BUCVjYTjbwleVfpTXkQWtGFHYrT8" + db_user_engine.password: "dcPa7a9wTCaTxFk7BdjmCuQp8k29e2eL" + db_user.password: "8s4cGYqY41mrWqTmwhZ3beVQcz6wc3Yr" +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dsf-bpe-proxy + labels: + app: dsf-bpe-proxy +spec: + replicas: 1 + selector: + matchLabels: + app: dsf-bpe-proxy + template: + metadata: + labels: + app: dsf-bpe-proxy + spec: + restartPolicy: Always + containers: + - name: proxy + image: datasharingframework/bpe_proxy + env: + - name: TZ + value: "Europe/Berlin" + - name: HTTPS_SERVER_NAME_PORT + value: "localhost:443" + - name: APP_SERVER_IP + value: "dsf-bpe-app" + - name: SSL_CERTIFICATE_FILE + value: "/run/secrets/bpe.crt" + - name: SSL_CERTIFICATE_KEY_FILE + value: "/run/secrets/bpe.key.plain" + - name: SSL_CERTIFICATE_CHAIN_FILE + value: "/run/secrets/issuing_ca.crt" + - name: SSL_CA_CERTIFICATE_FILE + value: "/run/secrets/ca_chain.crt" + - name: SSL_CA_DN_REQUEST_FILE + value: "/run/secrets/issuing_ca.crt" + - name: SSL_EXPECTED_CLIENT_I_DN_CN_VALUES + value: "'DSF Dev Issuing CA'" + ports: + - containerPort: 80 + hostPort: 8080 + hostIP: 127.0.0.1 + - containerPort: 443 + hostPort: 8443 + hostIP: 127.0.0.1 + volumeMounts: + - name: bpe-crt + mountPath: /run/secrets/bpe.crt + subPath: bpe.crt + readOnly: true + - name: bpe-key-plain + mountPath: /run/secrets/bpe.key.plain + subPath: bpe.key.plain + readOnly: true + - name: issuing-ca-crt + mountPath: /run/secrets/issuing_ca.crt + subPath: issuing_ca.crt + readOnly: true + - name: ca-chain-crt + mountPath: /run/secrets/ca_chain.crt + subPath: ca_chain.crt + readOnly: true + volumes: + - name: bpe-crt + hostPath: + path: ./secrets/bpe.crt + type: File + - name: bpe-key-plain + hostPath: + path: ./secrets/bpe.key.plain + type: File + - name: issuing-ca-crt + hostPath: + path: ./secrets/issuing_ca.crt + type: File + - name: ca-chain-crt + hostPath: + path: ./secrets/ca_chain.crt + type: File +--- +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: dsf-bpe-app-config +data: + application.yml: | + dev: + dsf: + log: + data: + file: + enabled: true + bpe: + db: + url: jdbc:postgresql://dsf-bpe-db/bpe + liquibase: + password: + file: /run/secrets/db_liquibase.password + user: + password: + file: /run/secrets/db_user.password + engine: + password: + file: /run/secrets/db_user_engine.password + fhir: + client: + trust: + server: + certificate: + cas: /run/secrets/root_ca.crt + server: + base: + url: https://dsf-bpe-proxy:8443/bpe + server: + ui: + theme: dev + base: + url: https://dsf-bpe-proxy:8443/bpe + roleconfig: | + - webbrowser_test_user: + thumbprint: ${WEBBROWSER_TEST_USER_THUMBPRINT} + dsf-role: + - ADMIN + server: + auth: + trust: + client: + certificate: + cas: /run/secrets/ca_chain.crt +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dsf-bpe-app + labels: + app: dsf-bpe-app +spec: + replicas: 1 + selector: + matchLabels: + app: dsf-bpe-app + template: + metadata: + labels: + app: dsf-bpe-app + spec: + restartPolicy: Always + containers: + - name: app + image: datasharingframework/bpe + ports: + - containerPort: 5002 + hostPort: 5002 + hostIP: 127.0.0.1 + env: + - name: TZ + value: "Europe/Berlin" + - name: EXTRA_JVM_ARGS + value: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5002" + - name: SPRING_CONFIG_ADDITIONAL_LOCATION + value: "file:/config/application.yml" + # conflict with dev.dsf.bpe.fhir.client.certificate.private.* + # (Scalar vs. nested key under the same parent) and therefore remain as ENV + - name: DEV_DSF_BPE_FHIR_CLIENT_CERTIFICATE + value: "/run/secrets/bpe.crt" + - name: DEV_DSF_BPE_FHIR_CLIENT_CERTIFICATE_PRIVATE_KEY + value: "/run/secrets/bpe.key" + - name: DEV_DSF_BPE_FHIR_CLIENT_CERTIFICATE_PRIVATE_KEY_PASSWORD + value: "password" + volumeMounts: + - name: spring-application-config + mountPath: /config + readOnly: true + - name: bpe-process + mountPath: /opt/bpe/process + readOnly: true + - name: bpe-log + mountPath: /opt/bpe/log + - name: db-passwords + mountPath: /run/secrets/db_liquibase.password + subPath: db_liquibase.password + readOnly: true + - name: db-passwords + mountPath: /run/secrets/db_user.password + subPath: db_user.password + readOnly: true + - name: db-passwords + mountPath: /run/secrets/db_user_engine.password + subPath: db_user_engine.password + readOnly: true + - name: root-ca-crt + mountPath: /run/secrets/root_ca.crt + subPath: root_ca.crt + readOnly: true + - name: bpe-crt + mountPath: /run/secrets/bpe.crt + subPath: bpe.crt + readOnly: true + - name: bpe-key + mountPath: /run/secrets/bpe.key + subPath: bpe.key + readOnly: true + - name: ca-chain-crt + mountPath: /run/secrets/ca_chain.crt + subPath: ca_chain.crt + readOnly: true + volumes: + - name: spring-application-config + configMap: + name: dsf-bpe-app-config + - name: bpe-process + hostPath: + path: ./process + type: Directory + - name: bpe-log + hostPath: + path: ./log + type: Directory + - name: db-passwords + secret: + secretName: dsf-fhir-db-passwords + - name: root-ca-crt + hostPath: + path: ./secrets/root_ca.crt + type: File + - name: bpe-crt + hostPath: + path: ./secrets/bpe.crt + type: File + - name: bpe-key + hostPath: + path: ./secrets/bpe.key + type: File + - name: ca-chain-crt + hostPath: + path: ./secrets/ca_chain.crt + type: File +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dsf-bpe-db + labels: + app: dsf-bpe-db +spec: + replicas: 1 + selector: + matchLabels: + app: dsf-bpe-db + template: + metadata: + labels: + app: dsf-bpe-db + spec: + restartPolicy: Always + containers: + - name: db + image: docker.io/library/postgres:18 + env: + - name: TZ + value: "Europe/Berlin" + - name: POSTGRES_PASSWORD_FILE + value: "/run/secrets/db_liquibase.password" + - name: POSTGRES_USER + value: "liquibase_user" + - name: POSTGRES_DB + value: "bpe" + livenessProbe: + exec: + command: + - pg_isready + - -U + - liquibase_user + - -d + - bpe + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + volumeMounts: + - name: db-passwords + mountPath: /run/secrets/db_liquibase.password + subPath: db_liquibase.password + readOnly: true + volumes: + - name: db-passwords + secret: + secretName: dsf-fhir-db-passwords diff --git a/dsf-podman-dev-setup/bpe/log/README.md b/dsf-podman-dev-setup/bpe/log/README.md new file mode 100644 index 000000000..2fd366f72 --- /dev/null +++ b/dsf-podman-dev-setup/bpe/log/README.md @@ -0,0 +1 @@ +Empty folder for log files \ No newline at end of file diff --git a/dsf-podman-dev-setup/bpe/process/README.md b/dsf-podman-dev-setup/bpe/process/README.md new file mode 100644 index 000000000..47c7890cc --- /dev/null +++ b/dsf-podman-dev-setup/bpe/process/README.md @@ -0,0 +1 @@ +Empty folder for process jars \ No newline at end of file diff --git a/dsf-podman-dev-setup/bpe/secrets/db_liquibase.password b/dsf-podman-dev-setup/bpe/secrets/db_liquibase.password new file mode 100644 index 000000000..f4256802f --- /dev/null +++ b/dsf-podman-dev-setup/bpe/secrets/db_liquibase.password @@ -0,0 +1 @@ +PW99vXYyTbBTGzemQbCnVb4Bce7xuzcU77W5BUCVjYTjbwleVfpTXkQWtGFHYrT8 \ No newline at end of file diff --git a/dsf-podman-dev-setup/bpe/secrets/db_user.password b/dsf-podman-dev-setup/bpe/secrets/db_user.password new file mode 100644 index 000000000..5bb40f708 --- /dev/null +++ b/dsf-podman-dev-setup/bpe/secrets/db_user.password @@ -0,0 +1 @@ +8s4cGYqY41mrWqTmwhZ3beVQcz6wc3Yr \ No newline at end of file diff --git a/dsf-podman-dev-setup/bpe/secrets/db_user_engine.password b/dsf-podman-dev-setup/bpe/secrets/db_user_engine.password new file mode 100644 index 000000000..0e271e87f --- /dev/null +++ b/dsf-podman-dev-setup/bpe/secrets/db_user_engine.password @@ -0,0 +1 @@ +dcPa7a9wTCaTxFk7BdjmCuQp8k29e2eL \ No newline at end of file diff --git a/dsf-podman-dev-setup/fhir/conf/bundle.xml b/dsf-podman-dev-setup/fhir/conf/bundle.xml new file mode 100644 index 000000000..1d767d624 --- /dev/null +++ b/dsf-podman-dev-setup/fhir/conf/bundle.xml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dsf-podman-dev-setup/fhir/docker-compose.yml b/dsf-podman-dev-setup/fhir/docker-compose.yml new file mode 100644 index 000000000..973c27c27 --- /dev/null +++ b/dsf-podman-dev-setup/fhir/docker-compose.yml @@ -0,0 +1,168 @@ +# +# Copyright 2018-2025 Heilbronn University of Applied Sciences +# +# 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. +# + +services: + proxy: + build: ../../dsf-docker/fhir_proxy + image: datasharingframework/fhir_proxy + restart: "no" + ports: + - 127.0.0.1:80:80 + - 127.0.0.1:443:443 + secrets: + - fhir.crt + - fhir.key.plain + - issuing_ca.crt + - ca_chain.crt + environment: + TZ: Europe/Berlin + HTTPS_SERVER_NAME_PORT: localhost:443 + APP_SERVER_IP: 172.28.1.11 + SSL_CERTIFICATE_FILE: /run/secrets/fhir.crt + SSL_CERTIFICATE_KEY_FILE: /run/secrets/fhir.key.plain + SSL_CERTIFICATE_CHAIN_FILE: /run/secrets/issuing_ca.crt + SSL_CA_CERTIFICATE_FILE: /run/secrets/ca_chain.crt + SSL_CA_DN_REQUEST_FILE: /run/secrets/issuing_ca.crt + SSL_EXPECTED_CLIENT_I_DN_CN_VALUES: "'DSF Dev Issuing CA'" + networks: + frontend: + ipv4_address: 172.28.1.10 + bpe: + ipv4_address: 172.28.1.2 + aliases: + - fhir + depends_on: + - app + + app: + build: ../../dsf-fhir/dsf-fhir-server-jetty/docker + image: datasharingframework/fhir + restart: "no" + ports: + - 127.0.0.1:5001:5001 + secrets: + - db_liquibase.password + - db_user.password + - db_user_permanent_delete.password + - root_ca.crt + - bpe.crt + - bpe.key + - ca_chain.crt + volumes: + - type: bind + source: ./conf/bundle.xml + target: /opt/fhir/conf/bundle.xml + read_only: true + - type: bind + source: ./log + target: /opt/fhir/log + environment: + TZ: Europe/Berlin + EXTRA_JVM_ARGS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5001 + DEV_DSF_FHIR_DB_URL: jdbc:postgresql://db/fhir + DEV_DSF_FHIR_DB_LIQUIBASE_PASSWORD_FILE: /run/secrets/db_liquibase.password + DEV_DSF_FHIR_DB_USER_PASSWORD_FILE: /run/secrets/db_user.password + DEV_DSF_FHIR_DB_USER_PERMANENT_DELETE_PASSWORD_FILE: /run/secrets/db_user_permanent_delete.password + DEV_DSF_FHIR_CLIENT_TRUST_SERVER_CERTIFICATE_CAS: /run/secrets/root_ca.crt + DEV_DSF_FHIR_CLIENT_CERTIFICATE: /run/secrets/bpe.crt + DEV_DSF_FHIR_CLIENT_CERTIFICATE_PRIVATE_KEY: /run/secrets/bpe.key + DEV_DSF_FHIR_CLIENT_CERTIFICATE_PRIVATE_KEY_PASSWORD: 'password' + DEV_DSF_FHIR_SERVER_UI_THEME: dev + DEV_DSF_FHIR_SERVER_BASE_URL: https://fhir/fhir + DEV_DSF_FHIR_SERVER_ORGANIZATION_IDENTIFIER_VALUE: Test_Organization + DEV_DSF_FHIR_SERVER_ROLECONFIG: | + - webbrowser_test_user: + thumbprint: ${WEBBROWSER_TEST_USER_THUMBPRINT} + dsf-role: + - CREATE: [Task] + - READ + - UPDATE: [QuestionnaireResponse] + - SEARCH + - HISTORY + practitioner-role: + - http://dsf.dev/fhir/CodeSystem/practitioner-role|DSF_ADMIN + DEV_DSF_SERVER_AUTH_TRUST_CLIENT_CERTIFICATE_CAS: /run/secrets/ca_chain.crt + networks: + frontend: + ipv4_address: 172.28.1.11 + backend: + ipv4_address: 172.28.1.18 + depends_on: + - db + + db: + image: postgres:18 + restart: "no" +# ports: +# - 127.0.0.1:5432:5432 + healthcheck: + test: ["CMD-SHELL", "pg_isready -U liquibase_user -d fhir"] + interval: 10s + timeout: 5s + retries: 5 + environment: + TZ: Europe/Berlin + POSTGRES_PASSWORD_FILE: /run/secrets/db_liquibase.password + POSTGRES_USER: liquibase_user + POSTGRES_DB: fhir + networks: + backend: + ipv4_address: 172.28.1.19 + secrets: + - db_liquibase.password + +secrets: + fhir.crt: + file: ./secrets/fhir.crt + fhir.key.plain: + file: ./secrets/fhir.key.plain + bpe.crt: + file: ./secrets/bpe.crt + bpe.key: + file: ./secrets/bpe.key + issuing_ca.crt: + file: ./secrets/issuing_ca.crt + root_ca.crt: + file: ./secrets/root_ca.crt + ca_chain.crt: + file: ./secrets/ca_chain.crt + + db_liquibase.password: + file: ./secrets/db_liquibase.password + db_user.password: + file: ./secrets/db_user.password + db_user_permanent_delete.password: + file: ./secrets/db_user_permanent_delete.password + +networks: + frontend: + driver: bridge + ipam: + driver: default + config: + - subnet: 172.28.1.8/29 + backend: + driver: bridge + ipam: + driver: default + config: + - subnet: 172.28.1.16/29 + bpe: + driver: bridge + ipam: + driver: default + config: + - subnet: 172.28.1.0/29 \ No newline at end of file diff --git a/dsf-podman-dev-setup/fhir/kube.yaml b/dsf-podman-dev-setup/fhir/kube.yaml new file mode 100644 index 000000000..93853b286 --- /dev/null +++ b/dsf-podman-dev-setup/fhir/kube.yaml @@ -0,0 +1,302 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: dsf-fhir-db-passwords +type: Opaque +stringData: + db_liquibase.password: "v4smzLRUKvQPHStp5WHMRhhdcV4GWkuPGvcq8unKa8CEhTmUTDyMWd62tHdHgwhf" + db_user.password: "xTZkzduUjYw3Bk7XQ4hYi2cRbunDAdNT" + db_user_permanent_delete.password: "aPBRCNJZkfbf46Fh9YE4MDG5X3vDBMca" +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dsf-fhir-proxy + labels: + app: dsf-fhir-proxy +spec: + replicas: 1 + selector: + matchLabels: + app: dsf-fhir-proxy + template: + metadata: + labels: + app: dsf-fhir-proxy + spec: + restartPolicy: Always + containers: + - name: proxy + image: datasharingframework/fhir_proxy + env: + - name: TZ + value: "Europe/Berlin" + - name: HTTPS_SERVER_NAME_PORT + value: "localhost:443" + - name: APP_SERVER_IP + value: "dsf-fhir-app" + - name: SSL_CERTIFICATE_FILE + value: "/run/secrets/fhir.crt" + - name: SSL_CERTIFICATE_KEY_FILE + value: "/run/secrets/fhir.key.plain" + - name: SSL_CERTIFICATE_CHAIN_FILE + value: "/run/secrets/issuing_ca.crt" + - name: SSL_CA_CERTIFICATE_FILE + value: "/run/secrets/ca_chain.crt" + - name: SSL_CA_DN_REQUEST_FILE + value: "/run/secrets/issuing_ca.crt" + - name: SSL_EXPECTED_CLIENT_I_DN_CN_VALUES + value: "'DSF Dev Issuing CA'" + ports: + - containerPort: 80 + hostPort: 80 + hostIP: 127.0.0.1 + - containerPort: 443 + hostPort: 443 + hostIP: 127.0.0.1 + volumeMounts: + - name: fhir-crt + mountPath: /run/secrets/fhir.crt + subPath: fhir.crt + readOnly: true + - name: fhir-key-plain + mountPath: /run/secrets/fhir.key.plain + subPath: fhir.key.plain + readOnly: true + - name: issuing-ca-crt + mountPath: /run/secrets/issuing_ca.crt + subPath: issuing_ca.crt + readOnly: true + - name: ca-chain-crt + mountPath: /run/secrets/ca_chain.crt + subPath: ca_chain.crt + readOnly: true + volumes: + - name: fhir-crt + hostPath: + path: ./secrets/fhir.crt + type: File + - name: fhir-key-plain + hostPath: + path: ./secrets/fhir.key.plain + type: File + - name: issuing-ca-crt + hostPath: + path: ./secrets/issuing_ca.crt + type: File + - name: ca-chain-crt + hostPath: + path: ./secrets/ca_chain.crt + type: File +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: dsf-fhir-app-config +data: + application.yml: | + dev: + dsf: + fhir: + db: + url: jdbc:postgresql://dsf-fhir-db/fhir + liquibase: + password: + file: /run/secrets/db_liquibase.password + user: + password: + file: /run/secrets/db_user.password + permanent: + delete: + password: + file: /run/secrets/db_user_permanent_delete.password + client: + trust: + server: + certificate: + cas: /run/secrets/root_ca.crt + server: + ui: + theme: dev + base: + url: https://dsf-fhir-proxy/fhir + organization: + identifier: + value: Test_Organization + roleconfig: | + - webbrowser_test_user: + thumbprint: ${WEBBROWSER_TEST_USER_THUMBPRINT} + dsf-role: + - CREATE: [Task] + - READ + - UPDATE: [QuestionnaireResponse] + - SEARCH + - HISTORY + practitioner-role: + - http://dsf.dev/fhir/CodeSystem/practitioner-role|DSF_ADMIN + server: + auth: + trust: + client: + certificate: + cas: /run/secrets/ca_chain.crt +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dsf-fhir-app + labels: + app: dsf-fhir-app +spec: + replicas: 1 + selector: + matchLabels: + app: dsf-fhir-app + template: + metadata: + labels: + app: dsf-fhir-app + spec: + restartPolicy: Always + containers: + - name: app + image: datasharingframework/fhir + ports: + - containerPort: 5001 + hostPort: 5001 + hostIP: 127.0.0.1 + env: + - name: TZ + value: "Europe/Berlin" + - name: EXTRA_JVM_ARGS + value: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5001" + - name: SPRING_CONFIG_ADDITIONAL_LOCATION + value: "file:/config/application.yml" + # Diese drei stehen in Konflikt mit dev.dsf.fhir.client.certificate.private.* + # (Scalar vs. verschachtelter Key unter demselben Parent) und bleiben daher als ENV: + - name: DEV_DSF_FHIR_CLIENT_CERTIFICATE + value: "/run/secrets/bpe.crt" + - name: DEV_DSF_FHIR_CLIENT_CERTIFICATE_PRIVATE_KEY + value: "/run/secrets/bpe.key" + - name: DEV_DSF_FHIR_CLIENT_CERTIFICATE_PRIVATE_KEY_PASSWORD + value: "password" + volumeMounts: + - name: spring-application-config + mountPath: /config + readOnly: true + - name: bundle-xml + mountPath: /opt/fhir/conf/bundle.xml + subPath: bundle.xml + readOnly: true + - name: fhir-log + mountPath: /opt/fhir/log + - name: db-passwords + mountPath: /run/secrets/db_liquibase.password + subPath: db_liquibase.password + readOnly: true + - name: db-passwords + mountPath: /run/secrets/db_user.password + subPath: db_user.password + readOnly: true + - name: db-passwords + mountPath: /run/secrets/db_user_permanent_delete.password + subPath: db_user_permanent_delete.password + readOnly: true + - name: root-ca-crt + mountPath: /run/secrets/root_ca.crt + subPath: root_ca.crt + readOnly: true + - name: bpe-crt + mountPath: /run/secrets/bpe.crt + subPath: bpe.crt + readOnly: true + - name: bpe-key + mountPath: /run/secrets/bpe.key + subPath: bpe.key + readOnly: true + - name: ca-chain-crt + mountPath: /run/secrets/ca_chain.crt + subPath: ca_chain.crt + readOnly: true + volumes: + - name: spring-application-config + configMap: + name: dsf-fhir-app-config + - name: bundle-xml + hostPath: + path: ./conf/bundle.xml + type: File + - name: fhir-log + hostPath: + path: ./log + type: Directory + - name: root-ca-crt + hostPath: + path: ./secrets/root_ca.crt + type: File + - name: bpe-crt + hostPath: + path: ./secrets/bpe.crt + type: File + - name: bpe-key + hostPath: + path: ./secrets/bpe.key + type: File + - name: ca-chain-crt + hostPath: + path: ./secrets/ca_chain.crt + type: File + - name: db-passwords + secret: + secretName: dsf-fhir-db-passwords +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dsf-fhir-db + labels: + app: dsf-fhir-db +spec: + replicas: 1 + selector: + matchLabels: + app: dsf-fhir-db + template: + metadata: + labels: + app: dsf-fhir-db + spec: + restartPolicy: Always + containers: + - name: db + image: docker.io/library/postgres:18 + env: + - name: TZ + value: "Europe/Berlin" + - name: POSTGRES_PASSWORD_FILE + value: "/run/secrets/db_liquibase.password" + - name: POSTGRES_USER + value: "liquibase_user" + - name: POSTGRES_DB + value: "fhir" + livenessProbe: + exec: + command: + - pg_isready + - -U + - liquibase_user + - -d + - fhir + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + volumeMounts: + - name: db-passwords + mountPath: /run/secrets/db_liquibase.password + subPath: db_liquibase.password + readOnly: true + volumes: + - name: db-passwords + secret: + secretName: dsf-fhir-db-passwords diff --git a/dsf-podman-dev-setup/fhir/log/README.md b/dsf-podman-dev-setup/fhir/log/README.md new file mode 100644 index 000000000..2fd366f72 --- /dev/null +++ b/dsf-podman-dev-setup/fhir/log/README.md @@ -0,0 +1 @@ +Empty folder for log files \ No newline at end of file diff --git a/dsf-podman-dev-setup/fhir/secrets/db_liquibase.password b/dsf-podman-dev-setup/fhir/secrets/db_liquibase.password new file mode 100644 index 000000000..b8dfeff3f --- /dev/null +++ b/dsf-podman-dev-setup/fhir/secrets/db_liquibase.password @@ -0,0 +1 @@ +v4smzLRUKvQPHStp5WHMRhhdcV4GWkuPGvcq8unKa8CEhTmUTDyMWd62tHdHgwhf \ No newline at end of file diff --git a/dsf-podman-dev-setup/fhir/secrets/db_user.password b/dsf-podman-dev-setup/fhir/secrets/db_user.password new file mode 100644 index 000000000..5ed8aaf48 --- /dev/null +++ b/dsf-podman-dev-setup/fhir/secrets/db_user.password @@ -0,0 +1 @@ +xTZkzduUjYw3Bk7XQ4hYi2cRbunDAdNT \ No newline at end of file diff --git a/dsf-podman-dev-setup/fhir/secrets/db_user_permanent_delete.password b/dsf-podman-dev-setup/fhir/secrets/db_user_permanent_delete.password new file mode 100644 index 000000000..034d7f42d --- /dev/null +++ b/dsf-podman-dev-setup/fhir/secrets/db_user_permanent_delete.password @@ -0,0 +1 @@ +aPBRCNJZkfbf46Fh9YE4MDG5X3vDBMca \ No newline at end of file diff --git a/dsf-podman-dev-setup/podman-build.bat b/dsf-podman-dev-setup/podman-build.bat new file mode 100644 index 000000000..2cce25ea3 --- /dev/null +++ b/dsf-podman-dev-setup/podman-build.bat @@ -0,0 +1,29 @@ +@REM +@REM Copyright 2018-2025 Heilbronn University of Applied Sciences +@REM +@REM Licensed under the Apache License, Version 2.0 (the "License"); +@REM you may not use this file except in compliance with the License. +@REM You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, software +@REM distributed under the License is distributed on an "AS IS" BASIS, +@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@REM See the License for the specific language governing permissions and +@REM limitations under the License. +@REM + +@echo off + +echo datasharingframework/bpe ... +podman build --pull -t datasharingframework/bpe ..\dsf-bpe\dsf-bpe-server-jetty\docker + +echo datasharingframework/fhir ... +podman build --pull -t datasharingframework/fhir ..\dsf-fhir\dsf-fhir-server-jetty\docker + +echo datasharingframework/bpe_proxy ... +podman build --pull -t datasharingframework/bpe_proxy ..\dsf-docker\bpe_proxy + +echo datasharingframework/fhir_proxy ... +podman build --pull -t datasharingframework/fhir_proxy ..\dsf-docker\fhir_proxy diff --git a/dsf-podman-dev-setup/podman-build.sh b/dsf-podman-dev-setup/podman-build.sh new file mode 100644 index 000000000..bee97f3b1 --- /dev/null +++ b/dsf-podman-dev-setup/podman-build.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# +# Copyright 2018-2025 Heilbronn University of Applied Sciences +# +# 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. +# + + +echo datasharingframework/bpe ... +podman build --pull -t datasharingframework/bpe ../dsf-bpe/dsf-bpe-server-jetty/docker + +echo datasharingframework/fhir ... +podman build --pull -t datasharingframework/fhir ../dsf-fhir/dsf-fhir-server-jetty/docker + +echo datasharingframework/bpe_proxy ... +podman build --pull -t datasharingframework/bpe_proxy ../dsf-docker/bpe_proxy + +echo datasharingframework/fhir_proxy ... +podman build --pull -t datasharingframework/fhir_proxy ../dsf-docker/fhir_proxy diff --git a/dsf-podman-dev-setup/prod/README.md b/dsf-podman-dev-setup/prod/README.md new file mode 100644 index 000000000..72bb7a70f --- /dev/null +++ b/dsf-podman-dev-setup/prod/README.md @@ -0,0 +1,276 @@ +# DSF Kube + +> [!NOTE] +> This is currently being refactored and not all changes are well tested together. This will be changed, when we have a development setup. + +A rootless Podman setup for the Data Sharing Framework (DSF), designed as an intermediate step towards Kubernetes. It uses native Quadlet integration into systemd and Kubernetes-compatible YAML manifests. + +## Improvements over the original Docker Compose setup + +- Explicit registry prefix (e.g. `docker.io`) to avoid ambiguity +- More descriptive image tags (e.g. `postgres:18.3-alpine3.23`) +- Rootless Podman with user namespace isolation +- Engine-managed volumes instead of bind-mounts +- Fixed sysctl settings for the proxy container +- Official enterprise Linux support (SLES, RHEL, Ubuntu) +- Native init system integration via Quadlet instead of a central daemon + +## Additional requirements compared to the original setup + +- Podman >= 5.0 (Ubuntu 24+, SLES 16, RHEL 9+) +- `passt` (any version) +- Rootless service account with configured SubUIDs and SubGIDs + +## Preparation + +### Install dependencies + +```bash +# Ubuntu +apt install podman passt + +# SLES +zypper install podman passt + +# Alma Linux / RHEL +dnf install podman passt +``` + +### Allow unprivileged ports (required for the FHIR proxy on port 443) + +```bash +echo "net.ipv4.ip_unprivileged_port_start=80" > /etc/sysctl.d/99-user_priv_ports.conf +sysctl --system +``` + +### Create a service account + +To use a separate partition for application data, mount that partition on `/home` before creating the user. + +```bash +useradd -r -m -s /bin/bash podman + +# Add to systemd-journal group for log access +usermod -a -G systemd-journal podman + +# Configure SubUIDs and SubGIDs (adjust ranges for additional accounts) +usermod --add-subgids 100000-165536 --add-subuids 100000-165536 podman + +# Enable persistent user session (services survive logout) +loginctl enable-linger podman + +# Configure XDG_RUNTIME_DIR for rootless podman and systemd --user +cat >> /home/podman/.bashrc << 'EOF' +export XDG_RUNTIME_DIR=/run/user/$(id -u) +EOF + +# Switch into the service account context +sudo --login -u podman +``` + +--- + +## FHIR-Deployment + +### Secrets und Zertifikate + +Edit the certificate YAML files and insert the PEM contents: + +```bash +# Server certificate (Certificate A): SSL cert, key and chain +vi ./dsf-fhir/dsf-ssl-cert.yaml + +# Client certificate (Certificate B): used by the FHIR app to authenticate +vi ./dsf-fhir/dsf-client-cert.yaml +``` + +Generate and apply database passwords: + +```bash +# For using own passwords encode them as base64 and set them as env +export DB_LIQUIBASE_PASSWORD=$(openssl rand -base64 30 | tr -d '\n') +export DB_USER_PASSWORD=$(openssl rand -base64 16 | tr -d '\n') +export DB_USER_PERMANENT_DELETE_PASSWORD=$(openssl rand -base64 16 | tr -d '\n') + +envsubst < dsf-fhir/dsf-fhir-passwords.yaml.tpl > dsf-fhir-passwords.yaml +podman kube play dsf-fhir-passwords.yaml +rm dsf-fhir-passwords.yaml +``` + +### Install Quadlet units and create directories + +```bash +# Install Quadlet units +podman quadlet install ./dsf-fhir + +# Install systemd target +install -m 640 ./dsf-fhir.target ~/.config/systemd/user/dsf-fhir.target +``` + +### Configuration + +Edit the Kubernetes YAML and set the required environment variables: + +| Variable | Description | +| --------------------------------------------------- | --------------------------------------------------------------------- | +| `DEV_DSF_FHIR_SERVER_BASE_URL` | External FQDN of the FHIR server, e.g. `https://dsf.example.com/fhir` | +| `DEV_DSF_FHIR_SERVER_ORGANIZATION_IDENTIFIER_VALUE` | Organization identifier, e.g. `dsf.example.com` | +| `DEV_DSF_FHIR_SERVER_ROLECONFIG` | Role configuration for browser and API access | +| `HTTPS_SERVER_NAME_PORT` | FQDN and port of the FHIR server, e.g. `dsf.example.com:443` | + +See the [FHIR server configuration reference](https://dsf.dev/operations/latest/fhir/configuration) for all available parameters. + +### Start and stop + +```bash +# Start +systemctl --user daemon-reload +systemctl --user enable --now dsf-fhir.target + +# Restart (e.g. after configuration changes or certificate renewal) +systemctl --user restart dsf-fhir.target + +# Stop +systemctl --user disable --now dsf-fhir.target +``` + +### Verify startup + +Check the logs for successful startup: + +```bash +journalctl --user -u dsf-app.service -f +``` + +Expected on successful startup: +- FHIR server is reachable and responding on port 443 +- Proxy presents the correct server certificate (Certificate A) + +Test TLS from a remote host: + +```bash +openssl s_client -connect dsf.example.com:443 +# Expected: server certificate shown, connection ends with: +# tlsv13 alert certificate required +``` + +--- + +## BPE Server Deployment + +### Secrets and certificates + +Edit the certificate YAML file: + +```bash +# Client certificate (Certificate B): same certificate as used by the FHIR server +vi ./dsf-bpe/dsf-client-cert.yaml +``` + +Generate and apply database passwords: + +```bash +# For using own passwords encode them as base64 and set them as env +export DB_LIQUIBASE_PASSWORD=$(openssl rand -base64 30 | tr -d '\n') +export DB_USER_PASSWORD=$(openssl rand -base64 16 | tr -d '\n') +export DB_USER_CAMUNDA=$(openssl rand -base64 16 | tr -d '\n') + +envsubst < dsf-bpe/dsf-bpe-passwords.yaml.tpl > dsf-bpe-passwords.yaml +podman kube play dsf-bpe-passwords.yaml +rm dsf-bpe-passwords.yaml +``` + +### Install Quadlet units and create directories + +```bash +# Install Quadlet units +podman quadlet install ./dsf-bpe + +# Install systemd target +install -m 640 ./dsf-bpe.target ~/.config/systemd/user/dsf-bpe.target + +# Create process plugin directory +mkdir -p ~/.config/dsf-bpe/process +podman unshare chown root:2202 ~/.config/dsf-bpe/process +podman unshare chmod 650 ~/.config/dsf-bpe/process +``` + +### Configuration + +Edit the Kubernetes YAML and set the required environment variables: + +| Variable | Description | +| ---------------------------------- | --------------------------------------------------------------------------------- | +| `DEV_DSF_BPE_FHIR_SERVER_BASE_URL` | Base URL of the corresponding FHIR server, e.g. `https://dsf.example.com/fhir` | +| `DEV_DSF_BPE_PROCESS_EXCLUDED` | Pipe-separated list of process IDs to exclude, e.g. `dsfdev_updateAllowList\|1.0` | + +See the [BPE server configuration reference](https://dsf.dev/operations/latest/bpe/configuration) for all available parameters. + +### Start and stop + +```bash +# Start +systemctl --user daemon-reload +systemctl --user enable --now dsf-bpe.target + +# Restart (e.g. after configuration changes or plugin updates) +systemctl --user restart dsf-bpe.target + +# Stop +systemctl --user disable --now dsf-bpe.target +``` + +### Verify startup + +```bash +journalctl --user -u dsf-bpe-app.service -f +``` + +Expected on successful startup: +- BPE downloaded Task resources from the DSF FHIR server +- BPE downloaded a Subscription resource from the DSF FHIR server +- BPE established a WebSocket connection to the DSF FHIR server + +If TLS issues occur, test the connection manually: + +```bash +podman run -it --rm alpine/openssl s_client dsf.example.com:443 +# Expected: server certificate shown, ends with tlsv13 alert certificate required +``` + +--- + +## Certificate renewal + +Both FHIR and BPE use certificate YAML files (`dsf-ssl-cert.yaml`, `dsf-client-cert.yaml`) that can be updated in place. After updating the PEM contents, restart the affected service: + +```bash +# FHIR proxy (server certificate) +systemctl --user restart dsf-proxy.service + +# FHIR app or BPE app (client certificate) +systemctl --user restart dsf-app.service +systemctl --user restart dsf-bpe-app.service +``` + +--- + +## Kubernetes Migration Notes + +The Kubernetes YAML files under `dsf-fhir` and `dsf-bpe` can be used as a starting point for a Kubernetes deployment with minor additions: + +- Add `namespace` to each resource +- Replace ConfigMap-based private keys with proper `kind: Secret` resources +- Replace `hostPort` with a proper `Service` of type `LoadBalancer` or `NodePort` +- Consider a sidecar or init container approach for process plugins +- Instead of deploying plugins as jar files via bind-mount, publish them as OCI images and mount them into the container. +- use one secret per password -> least privilege + +### Notes on certificate handling + +In this setup, certificate keys are provided as ConfigMaps. This has the following advantages in the Podman/Quadlet context: + +- Editable as plain text (PEM format) +- Reusable across multiple pods via the `--configmap` option in `podman kube play` + +In a production Kubernetes deployment, private keys should be stored as `kind: Secret` instead of ConfigMap to benefit from Kubernetes secret management, RBAC, and optional encryption at rest. diff --git a/dsf-podman-dev-setup/prod/dsf-bpe-passwords.yaml.tpl b/dsf-podman-dev-setup/prod/dsf-bpe-passwords.yaml.tpl new file mode 100644 index 000000000..4e1b12258 --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-bpe-passwords.yaml.tpl @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: dsf-bpe-passwords +stringData: + db_liquibase.password: "${DB_LIQUIBASE_PASSWORD}" + db_user.password: "${DB_USER_PASSWORD}" + db_user_camunda.password: "${DB_USER_CAMUNDA}" \ No newline at end of file diff --git a/dsf-podman-dev-setup/prod/dsf-bpe.target b/dsf-podman-dev-setup/prod/dsf-bpe.target new file mode 100644 index 000000000..8e27441bb --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-bpe.target @@ -0,0 +1,7 @@ +[Unit] +Description=DSF FHIR Server +Wants=dsf-bpe-app.service +After=dsf-bpe-app.service + +[Install] +WantedBy=default.target diff --git a/dsf-podman-dev-setup/prod/dsf-bpe/dsf-backend.network b/dsf-podman-dev-setup/prod/dsf-bpe/dsf-backend.network new file mode 100644 index 000000000..dc672fa89 --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-bpe/dsf-backend.network @@ -0,0 +1,2 @@ +[Network] +NetworkName=dsf-backend \ No newline at end of file diff --git a/dsf-podman-dev-setup/prod/dsf-bpe/dsf-bpe-app.kube b/dsf-podman-dev-setup/prod/dsf-bpe/dsf-bpe-app.kube new file mode 100644 index 000000000..9297ce780 --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-bpe/dsf-bpe-app.kube @@ -0,0 +1,16 @@ +[Unit] +Description=DSF BPE Application +PartOf=dsf-bpe.target +After=dsf-bpe-db.service +Wants=dsf-db.service + +[Kube] +Yaml=%h/.config/containers/systemd/dsf-bpe-app.yaml +ConfigMap=%h/.config/containers/systemd/dsf-client-cert.yaml +Network=dsf-bpe-backend.network + +[Service] +Restart=on-failure + +[Install] +WantedBy=dsf-bpe.target \ No newline at end of file diff --git a/dsf-podman-dev-setup/prod/dsf-bpe/dsf-bpe-app.yaml b/dsf-podman-dev-setup/prod/dsf-bpe/dsf-bpe-app.yaml new file mode 100644 index 000000000..6e17b0d36 --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-bpe/dsf-bpe-app.yaml @@ -0,0 +1,109 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: dsf-bpe-cache + annotations: + volume.podman.io/uid: "0" + volume.podman.io/gid: "2202" + volume.podman.io/mount-options: "uid=0,gid=2202,mode=0770" +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: dsf-bpe-config +data: + application.yml: | + dev: + dsf: + bpe: + db: + url: jdbc:postgresql://dsf-bpe-db/bpe + liquibase: + password: + file: /run/secrets/db/db_liquibase.password + user: + password: + file: /run/secrets/db/db_user.password + engine: + password: + file: /run/secrets/db/db_user_camunda.password + fhir: + client: + certificate: /run/secrets/cert/client_certificate.pem + private: + key: /run/secrets/cert/client_certificate_private_key.pem + key: + password: + file: /run/secrets/cert/client_certificate_private_key.pem.password + server: + base: + # TODO specify the base url of this DSF FHIR server + url: https://dsf.todo.organization.com/fhir + roleConfig: | + "" + # process: + # excluded: | + # dsfdev_updateAllowList|1.0 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dsf-bpe-app + labels: + app: dsf-bpe-app +spec: + replicas: 1 + selector: + matchLabels: + app: dsf-bpe-app + template: + metadata: + labels: + app: dsf-bpe-app + spec: + restartPolicy: OnFailure + containers: + - name: dsf-bpe-app + image: ghcr.io/datasharingframework/bpe:2.1.0 + env: + - name: TZ + value: "Europe/Berlin" + volumeMounts: + - name: spring-application-config + mountPath: /config + readOnly: true + - name: db-passwords + mountPath: /run/secrets/db + readOnly: true + - name: client-cert + mountPath: /run/secrets/cert + readOnly: true + - name: bpe-process + mountPath: /opt/bpe/process + readOnly: true + - name: bpe-cache + mountPath: /opt/bpe/cache + volumes: + - name: spring-application-config + configMap: + name: dsf-bpe-config + - name: bpe-process + hostPath: + path: /home/podman/.config/dsf-bpe/process + type: Directory + - name: db-passwords + secret: + secretName: dsf-bpe-passwords + - name: client-cert + configMap: + name: dsf-client-cert + - name: bpe-cache + persistentVolumeClaim: + claimName: dsf-bpe-cache diff --git a/dsf-podman-dev-setup/prod/dsf-bpe/dsf-bpe-db.kube b/dsf-podman-dev-setup/prod/dsf-bpe/dsf-bpe-db.kube new file mode 100644 index 000000000..1ff6aefb3 --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-bpe/dsf-bpe-db.kube @@ -0,0 +1,13 @@ +[Unit] +Description=DSF BPE Database +PartOf=dsf-bpe.target + +[Kube] +Yaml=%h/.config/containers/systemd/dsf-bpe-db.yaml +Network=dsf-backend.network + +[Service] +Restart=on-failure + +[Install] +WantedBy=dsf-bpe.target diff --git a/dsf-podman-dev-setup/prod/dsf-bpe/dsf-bpe-db.yaml b/dsf-podman-dev-setup/prod/dsf-bpe/dsf-bpe-db.yaml new file mode 100644 index 000000000..ea7fc48dd --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-bpe/dsf-bpe-db.yaml @@ -0,0 +1,72 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: dsf-bpe-db-data + annotations: + volume.podman.io/driver: local + volume.podman.io/uid: "70" + volume.podman.io/gid: "70" + volume.podman.io/mount-options: "uid=70,gid=70,mode=0770" +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dsf-bpe-db + labels: + app: dsf-bpe-db +spec: + replicas: 1 + selector: + matchLabels: + app: dsf-bpe-db + template: + metadata: + labels: + app: dsf-bpe-db + spec: + restartPolicy: OnFailure + containers: + - name: dsf-bpe-db + image: docker.io/library/postgres:18.4-alpine3.23 + env: + - name: TZ + value: Europe/Berlin + - name: POSTGRES_USER + value: liquibase_user + - name: POSTGRES_DB + value: bpe + - name: POSTGRES_PASSWORD_FILE + value: /run/secrets/db_liquibase.password + livenessProbe: + exec: + command: + - pg_isready + - -U + - liquibase_user + - -d + - bpe + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + volumeMounts: + - name: db-data + mountPath: /var/lib/postgresql + - name: db-passwords + mountPath: /run/secrets/db_liquibase.password + subPath: db_liquibase.password + readOnly: true + volumes: + - name: db-data + persistentVolumeClaim: + claimName: dsf-bpe-db-data + - name: db-passwords + secret: + secretName: dsf-bpe-passwords diff --git a/dsf-podman-dev-setup/prod/dsf-bpe/dsf-client-cert.yaml b/dsf-podman-dev-setup/prod/dsf-bpe/dsf-client-cert.yaml new file mode 100644 index 000000000..edbaad91e --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-bpe/dsf-client-cert.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: dsf-client-cert +data: + client_certificate.pem: | + -----BEGIN CERTIFICATE----- + MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw + ... + -----END CERTIFICATE----- + client_certificate_private_key.pem: | + -----BEGIN CERTIFICATE----- + ... + -----END CERTIFICATE----- + client_certificate_private_key.pem.password: "dein-passwort" diff --git a/dsf-podman-dev-setup/prod/dsf-fhir-passwords.yaml.tpl b/dsf-podman-dev-setup/prod/dsf-fhir-passwords.yaml.tpl new file mode 100644 index 000000000..40afe8167 --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-fhir-passwords.yaml.tpl @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: dsf-fhir-passwords +stringData: + db_liquibase.password: "${DB_LIQUIBASE_PASSWORD}" + db_user.password: "${DB_USER_PASSWORD}" + db_user_permanent_delete.password: "${DB_USER_PERMANENT_DELETE_PASSWORD}" \ No newline at end of file diff --git a/dsf-podman-dev-setup/prod/dsf-fhir.target b/dsf-podman-dev-setup/prod/dsf-fhir.target new file mode 100644 index 000000000..2903ded98 --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-fhir.target @@ -0,0 +1,7 @@ +[Unit] +Description=DSF FHIR Server +Wants=dsf-proxy.service +After=dsf-proxy.service + +[Install] +WantedBy=default.target \ No newline at end of file diff --git a/dsf-podman-dev-setup/prod/dsf-fhir/dsf-app.kube b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-app.kube new file mode 100644 index 000000000..0af71d7b9 --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-app.kube @@ -0,0 +1,16 @@ +[Unit] +Description=DSF FHIR App +After=dsf-db.service +Wants=dsf-db.service +PartOf=dsf-fhir.target + +[Kube] +Yaml=%h/.config/containers/systemd/dsf-app.yaml +ConfigMap=%h/.config/containers/systemd/dsf-client-cert.yaml +Network=dsf-backend.network + +[Service] +Restart=on-failure + +[Install] +WantedBy=dsf-fhir.target \ No newline at end of file diff --git a/dsf-podman-dev-setup/prod/dsf-fhir/dsf-app.yaml b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-app.yaml new file mode 100644 index 000000000..202d68100 --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-app.yaml @@ -0,0 +1,83 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: dsf-fhir-config +data: + application.yml: | + dev: + dsf: + fhir: + db: + url: jdbc:postgresql://dsf-db/fhir + liquibase: + password: + file: /run/secrets/db/db_liquibase.password + user: + password: + file: /run/secrets/db/db_user.password + permanent: + delete: + password: + file: /run/secrets/db/db_user_permanent_delete.password + client: + certificate: /run/secrets/cert/client_certificate.pem + private: + key: /run/secrets/cert/client_certificate_private_key.pem + password: + file: /run/secrets/cert/client_certificate_private_key.pem.password + server: + organization: + identifier: + value: "todo.organization.com" + # TODO specify the SHA-512 thumbprint of the Client-Certificate as lower case HEX (Regex: ^[a-f0-9]{128}$) + # certtool --fingerprint --hash=sha512 --infile=client_certificate.pem + # or simply get it from allowlist management tool + thumbprint: "" + base: + # TODO specify the base url of this DSF FHIR server + url: https://dsf.todo.organization.com/fhir +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dsf-app + labels: + app: dsf-app +spec: + replicas: 1 + selector: + matchLabels: + app: dsf-app + template: + metadata: + labels: + app: dsf-app + spec: + restartPolicy: OnFailure + containers: + - name: dsf-app + image: ghcr.io/datasharingframework/fhir:2.1.0 + env: + - name: TZ + value: Europe/Berlin + volumeMounts: + - name: db-passwords + mountPath: /run/secrets/db + readOnly: true + - name: client-cert + mountPath: /run/secrets/cert + readOnly: true + - name: spring-application-config + mountPath: /config + readOnly: true + volumes: + - name: db-passwords + secret: + secretName: dsf-fhir-passwords + - name: client-cert + configMap: + name: dsf-client-cert + - name: spring-application-config + configMap: + name: dsf-fhir-config diff --git a/dsf-podman-dev-setup/prod/dsf-fhir/dsf-backend.network b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-backend.network new file mode 100644 index 000000000..dc672fa89 --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-backend.network @@ -0,0 +1,2 @@ +[Network] +NetworkName=dsf-backend \ No newline at end of file diff --git a/dsf-podman-dev-setup/prod/dsf-fhir/dsf-client-cert.yaml b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-client-cert.yaml new file mode 100644 index 000000000..edbaad91e --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-client-cert.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: dsf-client-cert +data: + client_certificate.pem: | + -----BEGIN CERTIFICATE----- + MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw + ... + -----END CERTIFICATE----- + client_certificate_private_key.pem: | + -----BEGIN CERTIFICATE----- + ... + -----END CERTIFICATE----- + client_certificate_private_key.pem.password: "dein-passwort" diff --git a/dsf-podman-dev-setup/prod/dsf-fhir/dsf-db.kube b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-db.kube new file mode 100644 index 000000000..460405bfa --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-db.kube @@ -0,0 +1,13 @@ +[Unit] +Description=DSF FHIR DB +PartOf=dsf-fhir.target + +[Kube] +Yaml=%h/.config/containers/systemd/dsf-db.yaml +Network=dsf-backend.network + +[Service] +Restart=on-failure + +[Install] +WantedBy=dsf-fhir.target diff --git a/dsf-podman-dev-setup/prod/dsf-fhir/dsf-db.yaml b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-db.yaml new file mode 100644 index 000000000..78efc4125 --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-db.yaml @@ -0,0 +1,71 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: dsf-db-data + annotations: + volume.podman.io/driver: local + volume.podman.io/gid: "70" + volume.podman.io/uid: "70" + volume.podman.io/mount-options: "uid=70,gid=70,mode=0770" +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dsf-db + labels: + app: dsf-db +spec: + replicas: 1 + selector: + matchLabels: + app: dsf-db + template: + metadata: + labels: + app: dsf-db + spec: + containers: + - image: docker.io/library/postgres:18.4-alpine3.23 + name: dsf-db + livenessProbe: + exec: + command: + - pg_isready + - -U + - liquibase_user + - -d + - fhir + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 5 + env: + - name: POSTGRES_DB + value: fhir + - name: POSTGRES_PASSWORD_FILE + value: /run/secrets/db_liquibase.password + - name: POSTGRES_USER + value: liquibase_user + - name: TZ + value: Europe/Berlin + volumeMounts: + - mountPath: /var/lib/postgresql + name: postgres-data-pvc + - name: db-passwords + mountPath: /run/secrets/db_liquibase.password + subPath: db_liquibase.password + readOnly: true + volumes: + - name: postgres-data-pvc + persistentVolumeClaim: + claimName: dsf-db-data + - name: db-passwords + secret: + secretName: dsf-fhir-passwords diff --git a/dsf-podman-dev-setup/prod/dsf-fhir/dsf-frontend.network b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-frontend.network new file mode 100644 index 000000000..8bf0a4829 --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-frontend.network @@ -0,0 +1,2 @@ +[Network] +NetworkName=dsf-frontend \ No newline at end of file diff --git a/dsf-podman-dev-setup/prod/dsf-fhir/dsf-proxy.kube b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-proxy.kube new file mode 100644 index 000000000..d0227e3d8 --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-proxy.kube @@ -0,0 +1,16 @@ +[Unit] +Description=DSF FHIR Proxy +PartOf=dsf-fhir.target +After=dsf-app.service + +[Kube] +Yaml=%h/.config/containers/systemd/dsf-proxy.yaml +ConfigMap=%h/.config/containers/systemd/dsf-ssl-cert.yaml +Network=dsf-frontend.network +Network=dsf-backend.network + +[Service] +Restart=on-failure + +[Install] +WantedBy=dsf-fhir.target \ No newline at end of file diff --git a/dsf-podman-dev-setup/prod/dsf-fhir/dsf-proxy.yaml b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-proxy.yaml new file mode 100644 index 000000000..d90905df3 --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-proxy.yaml @@ -0,0 +1,49 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dsf-proxy + labels: + app: dsf-proxy +spec: + replicas: 1 + selector: + matchLabels: + app: dsf-proxy + template: + metadata: + labels: + app: dsf-proxy + spec: + restartPolicy: OnFailure + securityContext: + sysctls: + - name: net.ipv4.ip_unprivileged_port_start + value: "80" + containers: + - name: dsf-proxy + image: ghcr.io/datasharingframework/fhir_proxy:2.1.0 + env: + - name: TZ + value: Europe/Berlin + - name: APP_SERVER_IP + value: dsf-app + - name: HTTPS_SERVER_NAME_PORT + value: "dsf.todo.organization.com:443" + - name: SSL_CERTIFICATE_FILE + value: /run/secrets/ssl_certificate_file.pem + - name: SSL_CERTIFICATE_KEY_FILE + value: /run/secrets/ssl_certificate_key_file.pem + - name: SSL_CERTIFICATE_CHAIN_FILE + value: /run/secrets/ssl_certificate_chain_file.pem + ports: + - containerPort: 443 + hostPort: 443 + volumeMounts: + - name: ssl-cert + mountPath: /run/secrets + readOnly: true + volumes: + - name: ssl-cert + configMap: + name: dsf-ssl-cert diff --git a/dsf-podman-dev-setup/prod/dsf-fhir/dsf-ssl-cert.yaml b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-ssl-cert.yaml new file mode 100644 index 000000000..bf542d20e --- /dev/null +++ b/dsf-podman-dev-setup/prod/dsf-fhir/dsf-ssl-cert.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: dsf-ssl-cert +data: + ssl_certificate_file.pem: | + -----BEGIN CERTIFICATE----- + MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw + ... + -----END CERTIFICATE----- + ssl_certificate_chain_file.pem: | + -----BEGIN CERTIFICATE----- + ... + -----END CERTIFICATE----- + ssl_certificate_key_file.pem: | + -----BEGIN CERTIFICATE----- + ... + -----END CERTIFICATE-----