From 60bcc4510cb1e29c76aedb523c151af2f8f1e69f Mon Sep 17 00:00:00 2001 From: ozfox Date: Thu, 20 Jun 2024 14:44:22 +0200 Subject: [PATCH 1/5] backend: add quarkus docker extension and restructure properties file --- web_backend/pom.xml | 4 + .../src/main/resources/application.properties | 157 ++++++++++++------ 2 files changed, 108 insertions(+), 53 deletions(-) diff --git a/web_backend/pom.xml b/web_backend/pom.xml index eefcb91d..20afe672 100644 --- a/web_backend/pom.xml +++ b/web_backend/pom.xml @@ -78,6 +78,10 @@ io.quarkus quarkus-scheduler + + io.quarkus + quarkus-container-image-docker + io.quarkus quarkus-junit5 diff --git a/web_backend/src/main/resources/application.properties b/web_backend/src/main/resources/application.properties index 3be13d84..c83ee987 100644 --- a/web_backend/src/main/resources/application.properties +++ b/web_backend/src/main/resources/application.properties @@ -7,76 +7,110 @@ # See https://youtrack.jetbrains.com/issue/IDEA-347787 # # ENVIRONMENTS: -# dev - no docker needed, official leshan demo server +# dev - no external services needed, uses official leshan +# demo server at https://leshan.eclipseprojects.io # test - unit and integration testing # prod - production setup -# pi - config for final presentation raspberry setup -# oidc - enabled openid connect integration -# teashan - use leshan server of the platform +# +# ADDITIONAL PROFILES: +# teashan - use local leshan server instead of official demo server +# keycloak - enable Keycloak integration for dev or other non-production profiles +# publish - push container image to GitHub container registry # -quarkus.rest-client.leshan-client-api.url=https://leshan.eclipseprojects.io/api -quarkus.rest-client.leshan-event-api.url=https://leshan.eclipseprojects.io/api -quarkus.rest-client.leshan-event-api.verify-host=false -quarkus.rest-client.leshan-event-api.http2=true +# # # # # # +# Custom # +# # # # # # -%pi.quarkus.rest-client.leshan-client-api.url=http://teashan:8080/api -%pi.quarkus.rest-client.leshan-event-api.url=http://teashan:8080/api -%teashan.quarkus.rest-client.leshan-client-api.url=http://localhost:4000/leshan/api -%teashan.quarkus.rest-client.leshan-event-api.url=http://localhost:4000/leshan/api +leshan.api-url=https://leshan.eclipseprojects.io/api +%prod.leshan.api-url=http://teashan:8080/api +%teashan.leshan.api-url=http://localhost:4000/leshan/api -# General -%test.quarkus.http.test-timeout=5s +keycloak.enabled=false +%prod.keycloak.enabled=true +%keycloak.keycloak.enabled=true -%dev.quarkus.oidc.enabled=false -%test.quarkus.oidc.enabled=false +keycloak.auth-server-url=http://localhost:4000/kc/realms/teamagochi +%prod.keycloak.auth-server-url=http://keycloak:8080/kc/realms/teamagochi + +dummydata.load=true +%prod.dummydata.load=false + +postgres.jdbc.url=jdbc:postgresql://localhost:5432/backend?currentSchema=backend +%prod.postgres.jdbc.url=jdbc:postgresql://postgres:5432/backend?currentSchema=backend + +container-image.build=false +container-image.publish=false +%prod.container-image.build=true +%publish.container-image.publish=true + +# Secrets (all authors are aware that this should be secret in a realworld application) +db.password=postgres-backend-password + +# # # # # # +# General # +# # # # # # quarkus.http.cors=true quarkus.http.cors.origins=* -# Disable scheduler when running tests +%test.quarkus.http.test-timeout=5s +%test.quarkus.oidc.enabled=false %test.quarkus.scheduler.enabled=false +%test.dummydata.load=false -## OIDC Configuration -quarkus.oidc.auth-server-url=http://localhost:4000/kc/realms/teamagochi -quarkus.oidc.client-id=teamagochi-backend -quarkus.oidc.credentials.secret=5DACLJH84KTWBG22UpdnS9DSjVCIu5zB -quarkus.oidc.tls.verification=none +# # # # # # # # # +# REST Clients # +# # # # # # # # # -## Enable Policy Enforcement -quarkus.keycloak.policy-enforcer.enable=true -quarkus.keycloak.policy-enforcer.enforcement-mode=ENFORCING -quarkus.keycloak.policy-enforcer.lazy-load-paths=true +quarkus.rest-client.leshan-client-api.url=${leshan.api-url} +quarkus.rest-client.leshan-event-api.url=${leshan.api-url} +quarkus.rest-client.leshan-event-api.verify-host=false +quarkus.rest-client.leshan-event-api.http2=true -## Policy rules -## For enforcement modes, see https://www.keycloak.org/docs/latest/authorization_services/#resource_server_settings. -quarkus.keycloak.policy-enforcer.paths.q.paths=/q/* -quarkus.keycloak.policy-enforcer.paths.q.enforcement-mode=DISABLED -#%dev.quarkus.keycloak.policy-enforcer.paths.admin.paths=/api/v1/* -#%dev.quarkus.keycloak.policy-enforcer.paths.admin.enforcement-mode=ENFORCING +# # # # # # # # # +# REST Services # +# # # # # # # # # -# Services quarkus.rest.path=/api -## OpenAPI UI quarkus.swagger-ui.always-include=true -## OpenAPI Metadata -quarkus.smallrye-openapi.info-title=Teamagochi Backend API (development) +quarkus.smallrye-openapi.info-title=Teamagochi Backend API quarkus.smallrye-openapi.info-version=1.0.0 quarkus.smallrye-openapi.info-description=The REST interface for the Teamagochi frontend application. -#quarkus.smallrye-openapi.info-terms-of-service=Your terms here -#quarkus.smallrye-openapi.info-contact-email=techsupport@example.com -#quarkus.smallrye-openapi.info-contact-name=Example API Support -#quarkus.smallrye-openapi.info-contact-url=http://exampleurl.com/contact -#quarkus.smallrye-openapi.info-license-name=Apache 2.0 -#quarkus.smallrye-openapi.info-license-url=https://www.apache.org/licenses/LICENSE-2.0.html -%prod.quarkus.smallrye-openapi.info-title=Teamagochi Backend API +%prod.quarkus.smallrye-openapi.security-scheme=jwt +%prod.quarkus.smallrye-openapi.jwt-security-scheme-value=bearer +%prod.quarkus.smallrye-openapi.jwt-bearer-format=JWT -# Database -dummydata.load=true -%test.dummydata.load=false +%prod.quarkus.smallrye-openapi.servers=http://localhost:4000/backend +%prod.quarkus.swagger-ui.urls.default=/backend/q/openapi + +# # # # # # # # # # +# OIDC / Keycloak # +# # # # # # # # # # + +# General OIDC Configuration +quarkus.oidc.enabled=${keycloak.enabled} +quarkus.oidc.client-id=teamagochi-backend +quarkus.oidc.credentials.secret=5DACLJH84KTWBG22UpdnS9DSjVCIu5zB +quarkus.oidc.auth-server-url=${keycloak.auth-server-url} +quarkus.oidc.tls.verification=none + +# Keycloak extension +quarkus.keycloak.policy-enforcer.enable=true +quarkus.keycloak.policy-enforcer.enforcement-mode=ENFORCING +quarkus.keycloak.policy-enforcer.lazy-load-paths=true + +# Policy rules +# For enforcement modes, see https://www.keycloak.org/docs/latest/authorization_services/#resource_server_settings. +quarkus.keycloak.policy-enforcer.paths.q.paths=/q/* +quarkus.keycloak.policy-enforcer.paths.q.enforcement-mode=DISABLED + +# # # # # # # +# Database # +# # # # # # # quarkus.datasource.devservices.enabled=true quarkus.datasource.db-kind=h2 @@ -86,10 +120,27 @@ quarkus.hibernate-orm.dialect=org.hibernate.dialect.H2Dialect # Fixed port during development allows easier db inspection with Intellij %dev.quarkus.datasource.devservices.port=5433 -#%prod.quarkus.datasource.db-kind=postgres -#%prod.quarkus.datasource.db-version=15.6 -#%prod.quarkus.datasource.username=backend -#%prod.quarkus.datasource.password=secret-password -#%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://postgres:5432/ -#%prod.quarkus.hibernate-orm.database.generation=update -#%prod.quarkus.hibernate-orm.dialect=org.hibernate.dialect.PostgreSQLDialect +# Use postgres database on production +%prod.quarkus.datasource.devservices.enabled=false +%prod.quarkus.datasource.db-kind=postgresql +%prod.quarkus.datasource.db-version=15.6 +%prod.quarkus.datasource.jdbc.url=${postgres.jdbc.url} +%prod.quarkus.datasource.username=backend +%prod.quarkus.datasource.password=${db.password} +%prod.quarkus.hibernate-orm.database.generation=update +%prod.quarkus.hibernate-orm.dialect=org.hibernate.dialect.PostgreSQLDialect + +# # # # # # # # # # # +# Container image # +# # # # # # # # # # # + +quarkus.container-image.builder=docker +quarkus.container-image.build=${container-image.build} +quarkus.container-image.push=${container-image.publish} +quarkus.container-image.registry=ghcr.io +quarkus.container-image.group=smartuni +quarkus.container-image.name=teamagochi/web-backend +quarkus.container-image.tag=snapshot +quarkus.container-image.labels."org.opencontainers.image.source"=https://github.com/smartuni/teamagochi +quarkus.container-image.labels."org.opencontainers.image.description"="Teamagochi Web-Backend" +quarkus.container-image.labels."org.opencontainers.image.licenses"=MIT From 84a49750572f8d1aee5ba5c294b266d376b4dd8e Mon Sep 17 00:00:00 2001 From: ozfox Date: Thu, 20 Jun 2024 15:31:16 +0200 Subject: [PATCH 2/5] backend: update web_backend/README.md --- web_backend/README.md | 55 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/web_backend/README.md b/web_backend/README.md index 95d4d18e..a97da24d 100644 --- a/web_backend/README.md +++ b/web_backend/README.md @@ -27,14 +27,47 @@ about the [command line interface](https://quarkus.io/guides/cli-tooling) in gen ## Extensions -The following table contains all used [Quarkus Extensions](https://quarkus.io/extensions/) - -| Name | Links | Why? | -|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------| -| REST | [Docs](https://quarkus.io/guides/rest), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-rest) | Create and provide REST services | -| REST Jackson | [Docs](https://quarkus.io/guides/rest#json-serialisation), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-rest-jackson/) | Jackson serialization (JSON) support for REST extension | -| REST Client | [Docs](https://quarkus.io/guides/rest-client), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-rest-client/) | Call REST services, support receiving Server-sent events (SSE) | -| REST Client Jackson | [Info](https://quarkus.io/extensions/io.quarkus/quarkus-rest-client-jackson/) | Jackson serialization (JSON) support for the REST Client | -| Hibernate ORM with Panache | [Docs](https://quarkus.io/guides/hibernate-orm-panache), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-hibernate-orm-panache/) | Domain model persistence for relational databases using the the repository pattern | -| JDBC Driver - PostgreSQL | [Docs](https://quarkus.io/guides/datasource), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-jdbc-postgresql/) | Connect to the PostreSQL database via JDBC | -| JDBC Driver - H2 | [Docs](https://quarkus.io/guides/datasource), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-jdbc-h2/) | Connect to the H2 database via JDBC | +The following table contains all used [Quarkus Extensions](https://quarkus.io/extensions/). + +| Name | Links | Why? | +|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| REST | [Docs](https://quarkus.io/guides/rest), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-rest) | Create and provide REST services | +| REST Jackson | [Docs](https://quarkus.io/guides/rest#json-serialisation), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-rest-jackson/) | Jackson serialization (JSON) support for REST extension | +| REST Client | [Docs](https://quarkus.io/guides/rest-client), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-rest-client/) | Call REST services, support receiving Server-sent events (SSE) | +| REST Client Jackson | [Info](https://quarkus.io/extensions/io.quarkus/quarkus-rest-client-jackson/) | Jackson serialization (JSON) support for the REST Client | +| Hibernate ORM with Panache | [Docs](https://quarkus.io/guides/hibernate-orm-panache), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-hibernate-orm-panache/) | Domain model persistence for relational databases using the the repository pattern | +| JDBC Driver - PostgreSQL | [Docs](https://quarkus.io/guides/datasource), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-jdbc-postgresql/) | Connect to the PostreSQL database via JDBC | +| JDBC Driver - H2 | [Docs](https://quarkus.io/guides/datasource), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-jdbc-h2/) | Connect to the H2 database via JDBC | +| Container Image Docker | [Docs](https://quarkus.io/guides/container-image), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-container-image-docker/) | Build container images of your application using Docker | +| OpenID Connect | [Docs](https://quarkus.io/guides/security-oidc-bearer-token-authentication-tutorial), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-oidc/) | Verify Bearer access tokens and authenticate users with Authorization Code Flow | +| Keycloak Authorization | [Docs](https://quarkus.io/guides/security-keycloak-authorization), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-keycloak-authorization/) | Policy enforcer using Keycloak-managed permissions to control access to protected resources | +| SmallRye JWT | [Docs](https://quarkus.io/guides/security-jwt), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-smallrye-jwt/) | Secure your applications with JSON Web Token | +| SmallRye OpenAPI | [Docs](https://quarkus.io/guides/openapi-swaggerui), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-smallrye-openapi/) | Document your REST APIs with OpenAPI - comes with Swagger UI | +| Scheduler - tasks | [Docs](https://quarkus.io/guides/scheduler), [Info](https://quarkus.io/extensions/io.quarkus/quarkus-scheduler/) | Schedule jobs and tasks | + +## Container image + +The image is build with the `quarkus-container-image-docker` extension +using the configuration in `src/main/resources/application.properties`. +See [the official guide][quarkus-container-image]. + +To build the image locally, run [`install`][maven-lifecycle] with the `prod` +profile. Tests can be skipped to minimize the build time. + +```shell +./mvnw install -DskipTests -Dquarkus.profile=prod +``` + +The image can be pushed to the [GitHub container registry][gh-docs-registry], +but a login is required. Publishing is enabled by adding the `publish` profile. + +```shell +echo $PERSONAL_ACCESS_TOKEN | docker login ghcr.io -u --password-stdin + +# Build and publish +./mvnw install -Dquarkus.profile=prod,publish +``` + +[gh-docs-registry]: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry +[quarkus-container-image]: https://quarkus.io/guides/container-image +[maven-lifecycle]: https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html From 07eef48b2dc3517d133cab9f4259373f294e9ce7 Mon Sep 17 00:00:00 2001 From: ozfox Date: Thu, 20 Jun 2024 15:34:39 +0200 Subject: [PATCH 3/5] backend: support token auth when using Swagger UI --- .../backend/device/service/rest/v1/DeviceRestSelfService.java | 2 ++ .../backend/device/service/rest/v1/DeviceRestService.java | 2 ++ .../backend/pet/service/rest/v1/PetRestSelfService.java | 2 ++ .../teamagochi/backend/pet/service/rest/v1/PetRestService.java | 2 ++ .../backend/pet/service/rest/v1/PetTypeRestService.java | 2 ++ 5 files changed, 10 insertions(+) diff --git a/web_backend/src/main/java/haw/teamagochi/backend/device/service/rest/v1/DeviceRestSelfService.java b/web_backend/src/main/java/haw/teamagochi/backend/device/service/rest/v1/DeviceRestSelfService.java index 274c2af6..ab598d09 100644 --- a/web_backend/src/main/java/haw/teamagochi/backend/device/service/rest/v1/DeviceRestSelfService.java +++ b/web_backend/src/main/java/haw/teamagochi/backend/device/service/rest/v1/DeviceRestSelfService.java @@ -17,6 +17,7 @@ import java.util.Objects; import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; import org.eclipse.microprofile.openapi.annotations.tags.Tag; /** @@ -26,6 +27,7 @@ */ @Path("/v1/devices/self") @Tag(name = "devices/self", description = "Everything about a users devices.") +@SecurityRequirement(name = "SecurityScheme") public class DeviceRestSelfService { @Inject diff --git a/web_backend/src/main/java/haw/teamagochi/backend/device/service/rest/v1/DeviceRestService.java b/web_backend/src/main/java/haw/teamagochi/backend/device/service/rest/v1/DeviceRestService.java index 71517d5a..4a03abe6 100644 --- a/web_backend/src/main/java/haw/teamagochi/backend/device/service/rest/v1/DeviceRestService.java +++ b/web_backend/src/main/java/haw/teamagochi/backend/device/service/rest/v1/DeviceRestService.java @@ -14,6 +14,7 @@ import java.util.List; import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; import org.eclipse.microprofile.openapi.annotations.tags.Tag; /** @@ -21,6 +22,7 @@ */ @Path("/v1/devices") @Tag(name = "devices", description = "Everything about devices.") +@SecurityRequirement(name = "SecurityScheme") public class DeviceRestService { @Inject diff --git a/web_backend/src/main/java/haw/teamagochi/backend/pet/service/rest/v1/PetRestSelfService.java b/web_backend/src/main/java/haw/teamagochi/backend/pet/service/rest/v1/PetRestSelfService.java index 8b083410..436b7d44 100644 --- a/web_backend/src/main/java/haw/teamagochi/backend/pet/service/rest/v1/PetRestSelfService.java +++ b/web_backend/src/main/java/haw/teamagochi/backend/pet/service/rest/v1/PetRestSelfService.java @@ -20,6 +20,7 @@ import java.util.Objects; import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; import org.eclipse.microprofile.openapi.annotations.tags.Tag; /** @@ -27,6 +28,7 @@ */ @Path("/v1/pets/self") @Tag(name = "pets/self", description = "Everything about a users pets.") +@SecurityRequirement(name = "SecurityScheme") public class PetRestSelfService { @Inject diff --git a/web_backend/src/main/java/haw/teamagochi/backend/pet/service/rest/v1/PetRestService.java b/web_backend/src/main/java/haw/teamagochi/backend/pet/service/rest/v1/PetRestService.java index 9e1b5f1a..07ff9012 100644 --- a/web_backend/src/main/java/haw/teamagochi/backend/pet/service/rest/v1/PetRestService.java +++ b/web_backend/src/main/java/haw/teamagochi/backend/pet/service/rest/v1/PetRestService.java @@ -14,6 +14,7 @@ import java.util.List; import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; import org.eclipse.microprofile.openapi.annotations.tags.Tag; /** @@ -21,6 +22,7 @@ */ @Path("/v1/pets") @Tag(name = "pets", description = "Everything about pets.") +@SecurityRequirement(name = "SecurityScheme") public class PetRestService { @Inject diff --git a/web_backend/src/main/java/haw/teamagochi/backend/pet/service/rest/v1/PetTypeRestService.java b/web_backend/src/main/java/haw/teamagochi/backend/pet/service/rest/v1/PetTypeRestService.java index abbea1ac..e1d57ee1 100644 --- a/web_backend/src/main/java/haw/teamagochi/backend/pet/service/rest/v1/PetTypeRestService.java +++ b/web_backend/src/main/java/haw/teamagochi/backend/pet/service/rest/v1/PetTypeRestService.java @@ -10,6 +10,7 @@ import java.util.List; import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; import org.eclipse.microprofile.openapi.annotations.tags.Tag; /** @@ -17,6 +18,7 @@ */ @Path("/v1/pets/types") @Tag(name = "pets/types", description = "Everything about pets.") +@SecurityRequirement(name = "SecurityScheme") public class PetTypeRestService { @Inject From c939c3d0faa24fadacf862db2b33f4c12e256492 Mon Sep 17 00:00:00 2001 From: ozfox Date: Thu, 20 Jun 2024 15:47:25 +0200 Subject: [PATCH 4/5] platform: general improvements better compose file organization, backend integration, devdash update, modularized proxy config --- platform/.env | 3 +- platform/compose.common.yml | 122 ++++++++++++++ platform/compose.core.yml | 67 ++++++++ platform/compose.no-backend.yml | 20 +++ platform/{raspi.yml => compose.raspi.yml} | 5 + platform/compose.yml | 149 +++--------------- .../keycloak/import/teamagochi-realm.json | 10 +- platform/devdash/src/config.ts | 10 +- platform/postgres/initdb.d/init-backend-db.sh | 15 ++ .../postgres/initdb.d/init-keycloak-db.sh | 2 +- .../proxy/templates/backend.include.template | 8 + .../proxy/templates/default.conf.template | 2 + 12 files changed, 276 insertions(+), 137 deletions(-) create mode 100644 platform/compose.common.yml create mode 100644 platform/compose.core.yml create mode 100644 platform/compose.no-backend.yml rename platform/{raspi.yml => compose.raspi.yml} (76%) create mode 100644 platform/postgres/initdb.d/init-backend-db.sh create mode 100644 platform/proxy/templates/backend.include.template diff --git a/platform/.env b/platform/.env index 6ce34d5b..0988f02c 100644 --- a/platform/.env +++ b/platform/.env @@ -1,4 +1,5 @@ PROXY_PUBLIC_PORT=4000 # Secrets (not that secret) POSTGRES_PASSWORD=postgres-password -POSTGRES_KC_PASSWORD=postgres-kc-password \ No newline at end of file +POSTGRES_KC_PASSWORD=postgres-kc-password +POSTGRES_BACKEND_PASSWORD=postgres-backend-password diff --git a/platform/compose.common.yml b/platform/compose.common.yml new file mode 100644 index 00000000..571ca88d --- /dev/null +++ b/platform/compose.common.yml @@ -0,0 +1,122 @@ +# +# Provides common service definitions which can be used +# in other compose file with the `extend` keyword. +# + +x-common-healthcheck: &common-healthcheck + interval: 30s + timeout: 5s + retries: 3 + start_period: 60s + start_interval: 3s + +services: + proxy: + image: nginx:1.25-alpine + ports: + - "127.0.0.1:${PROXY_PUBLIC_PORT}:80" + - "[::]:${PROXY_PUBLIC_PORT}:80" + volumes: + - "./proxy/proxy_params:/etc/nginx/proxy_params:ro" + - "./proxy/nginx.conf:/etc/nginx/nginx.conf:ro" + - "./proxy/templates:/etc/nginx/templates:ro" + environment: + PROXY_PUBLIC_PORT: ${PROXY_PUBLIC_PORT} + # NOTE: picky about trailing slash + DEVDASH_WEB_URL: http://devdash:8080/ + TEASHAN_WEB_URL: http://teashan:8080/ + TEASHAN_BS_WEB_URL: http://teashan:8081/ + MAILPIT_WEB_URL: http://mailpit:8025/mailbox/ + KEYCLOAK_WEB_URL: http://keycloak:8080/kc + + backend: + image: ghcr.io/smartuni/teamagochi/web-backend:snapshot + healthcheck: + test: [ "CMD-SHELL", "timeout 10s bash -c ':> /dev/tcp/127.0.0.1/8080' || exit 1"] + <<: *common-healthcheck + + devdash: + build: + context: ./devdash + environment: + VITE_AUTHORITY: http://localhost:${PROXY_PUBLIC_PORT}/kc/realms/teamagochi + VITE_CLIENT_ID: teamagochi-webapp + + teashan: + build: + context: ./teashan + ports: + # Don't use a proxy, just map them to the host + - "127.0.0.1:5683:5683/udp" # [coap://] CoAP over UDP (with experimental OSCORE) + - "127.0.0.1:5683:5683/tcp" # [coap+tcp://] CoAP over TCP (experimental) + - "127.0.0.1:5684:5684/udp" # [coaps://] CoAP over DTLS + - "127.0.0.1:5684:5684/tcp" # [coaps+tcp://] CoAP over TLS (experimental) + - "127.0.0.1:5685:5685/udp" # [coap://] CoAP over UDP + # Bootstrap + - "127.0.0.1:5783:5783/udp" # [coap://] CoAP over UDP + - "127.0.0.1:5784:5784/udp" # [coaps://] CoAP over DTLS + environment: + NGINX_ENABLE: "false" + # Overriding LESHAN_CMD and LESHAN_BS_CMD allows changing ports and more, see ./teashan/Dockerfile + + keycloak: + build: + context: ./keycloak + command: ["start", "--optimized", "--import-realm"] + environment: + # NOTE: Uses a preconfigured Dockerfile, see ./keycloak/Dockerfile + KC_DB_PASSWORD: "${POSTGRES_KC_PASSWORD}" + KC_PROXY_HEADERS: xforwarded + KC_HTTP_ENABLED: "true" + KC_HOSTNAME_URL: http://localhost:${PROXY_PUBLIC_PORT}/kc + KC_HOSTNAME_ADMIN_URL: http://localhost:${PROXY_PUBLIC_PORT}/kc + KC_HOSTNAME_STRICT: "false" + KC_HOSTNAME_STRICT_BACKCHANNEL: "false" + KC_HOSTNAME_DEBUG: "true" + #KC_LOG_LEVEL: debug + healthcheck: + test: [ "CMD-SHELL", "timeout 10s bash -c ':> /dev/tcp/127.0.0.1/8080' || exit 1"] + <<: *common-healthcheck + volumes: + - "./data/keycloak/import:/opt/keycloak/data/import" + depends_on: + postgres: + condition: service_healthy + + postgres: + # Not the latest version, but recommended by Keycloak. + # See https://www.keycloak.org/server/db#_supported_databases + image: postgres:15.6-bookworm + ports: + - "127.0.0.1:5432:5432" + environment: + TZ: "Europe/Berlin" + POSTGRES_DB: postgres + POSTGRES_USER: postgres + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_HOST_AUTH_METHOD: scram-sha-256 + POSTGRES_INITDB_ARGS: --auth=scram-sha-256 + # Vars for database init scripts + POSTGRES_KC_USER: keycloak + POSTGRES_KC_DB: keycloak + POSTGRES_KC_PASSWORD: ${POSTGRES_KC_PASSWORD} + POSTGRES_BACKEND_USER: backend + POSTGRES_BACKEND_DB: backend + POSTGRES_BACKEND_PASSWORD: ${POSTGRES_BACKEND_PASSWORD} + healthcheck: + test: [ "CMD", "pg_isready", "-q", "-d", "postgres", "-U", "postgres" ] + <<: *common-healthcheck + volumes: + - postgres:/var/lib/postgresql/data + - "./postgres/initdb.d/init-keycloak-db.sh:/docker-entrypoint-initdb.d/init-keycloak-db.sh" + - "./data/db/import/keycloak-db.dump:/docker-entrypoint-initdb.d/keycloak-db.dump" + + mailpit: + image: axllent/mailpit + environment: + TZ: "Europe/Berlin" + MP_WEBROOT: /mailbox/ + MP_MAX_MESSAGES: 100 + MP_SMTP_AUTH_ACCEPT_ANY: 1 + MP_SMTP_AUTH_ALLOW_INSECURE: 1 + MP_QUIET: "true" diff --git a/platform/compose.core.yml b/platform/compose.core.yml new file mode 100644 index 00000000..8a527115 --- /dev/null +++ b/platform/compose.core.yml @@ -0,0 +1,67 @@ +# +# Provides all core services, volumes and networks. Can be used as a base +# for more specialized compose files with the `includes` keyword. +# + +services: + + # Development Dashboard + devdash: + extends: + file: ./compose.common.yml + service: devdash + networks: + - private + + # LwM2M Server and Bootstrap Server + teashan: + extends: + file: ./compose.common.yml + service: teashan + networks: + - private + - public + + # Keycloak Identity Provider and Access Management + keycloak: + extends: + file: ./compose.common.yml + service: keycloak + depends_on: + postgres: + condition: service_healthy + networks: + - private + - db + + # Shared database management system + postgres: + extends: + file: ./compose.common.yml + service: postgres + volumes: + - "./postgres/initdb.d/init-backend-db.sh:/docker-entrypoint-initdb.d/init-backend-db.sh" + networks: + - db + + # Email & SMTP testing tool + mailpit: + extends: + file: ./compose.common.yml + service: mailpit + networks: + - private + +volumes: + postgres: + +networks: + public: + internal: false + driver: bridge + private: + internal: true + driver: bridge + db: + internal: false + driver: bridge diff --git a/platform/compose.no-backend.yml b/platform/compose.no-backend.yml new file mode 100644 index 00000000..4967c2cf --- /dev/null +++ b/platform/compose.no-backend.yml @@ -0,0 +1,20 @@ +# +# Same as `compose.yml` but without the backend service. +# + +name: t8i-no-backend + +include: + - ./compose.core.yml + +services: + proxy: + extends: + file: ./compose.common.yml + service: proxy + depends_on: + keycloak: + condition: service_healthy + networks: + - public + - private diff --git a/platform/raspi.yml b/platform/compose.raspi.yml similarity index 76% rename from platform/raspi.yml rename to platform/compose.raspi.yml index f8334b89..4dcfafef 100644 --- a/platform/raspi.yml +++ b/platform/compose.raspi.yml @@ -1,3 +1,8 @@ +# +# Additions which are only relevant on the Raspberry Pi. Use with `-f` option, +# e.g.: `docker compose -f compose.yml -f compose.raspi.yml up` +# + services: teashan: ports: diff --git a/platform/compose.yml b/platform/compose.yml index 33a00851..5ccf0f30 100644 --- a/platform/compose.yml +++ b/platform/compose.yml @@ -1,146 +1,37 @@ +# +# Manages all our services and configures the proxy accordingly. +# + name: t8i -x-common-healthcheck: &common-healthcheck - interval: 30s - timeout: 5s - retries: 3 - start_period: 60s - start_interval: 3s +include: + - ./compose.core.yml services: - # Reverse proxy for all web interfaces proxy: - image: nginx:1.25-alpine - ports: - - "127.0.0.1:${PROXY_PUBLIC_PORT}:80" - - "[::]:${PROXY_PUBLIC_PORT}:80" - volumes: - - "./proxy/proxy_params:/etc/nginx/proxy_params:ro" - - "./proxy/nginx.conf:/etc/nginx/nginx.conf:ro" - - "./proxy/templates:/etc/nginx/templates:ro" + extends: + file: ./compose.common.yml + service: proxy environment: - PROXY_PUBLIC_PORT: ${PROXY_PUBLIC_PORT} - # NOTE: picky about trailing slash - DEVDASH_WEB_URL: http://devdash:8080/ - TEASHAN_WEB_URL: http://teashan:8080/ - TEASHAN_BS_WEB_URL: http://teashan:8081/ - MAILPIT_WEB_URL: http://mailpit:8025/mailbox/ - KEYCLOAK_WEB_URL: http://keycloak:8080/kc + BACKEND_WEB_URL: http://backend:8080/ depends_on: keycloak: condition: service_healthy + backend: + condition: service_healthy networks: - public - - proxy - - # Development Dashboard - devdash: - build: - context: ./devdash - environment: - VITE_AUTHORITY: http://localhost:${PROXY_PUBLIC_PORT}/kc/realms/teamagochi - VITE_CLIENT_ID: teamagochi-webapp - networks: - - proxy - - # LwM2M Server and Bootstrap Server - teashan: - build: - context: ./teashan - ports: - # Don't use a proxy, just map them to the host - - "127.0.0.1:5683:5683/udp" # [coap://] CoAP over UDP (with experimental OSCORE) - - "127.0.0.1:5683:5683/tcp" # [coap+tcp://] CoAP over TCP (experimental) - - "127.0.0.1:5684:5684/udp" # [coaps://] CoAP over DTLS - - "127.0.0.1:5684:5684/tcp" # [coaps+tcp://] CoAP over TLS (experimental) - - "127.0.0.1:5685:5685/udp" # [coap://] CoAP over UDP - # Bootstrap - - "127.0.0.1:5783:5783/udp" # [coap://] CoAP over UDP - - "127.0.0.1:5784:5784/udp" # [coaps://] CoAP over DTLS - environment: - NGINX_ENABLE: "false" - # Overriding LESHAN_CMD and LESHAN_BS_CMD allows changing ports and more, see ./teashan/Dockerfile - networks: - - proxy - - public + - private - # Keycloak Identity Provider and Access Management - keycloak: - build: - context: ./keycloak - command: ["start", "--optimized", "--import-realm"] - environment: - # NOTE: Uses a preconfigured Dockerfile, see ./keycloak/Dockerfile - KC_DB_PASSWORD: "${POSTGRES_KC_PASSWORD}" - KC_PROXY_HEADERS: xforwarded - KC_HTTP_ENABLED: "true" - KC_HOSTNAME_URL: http://localhost:${PROXY_PUBLIC_PORT}/kc - KC_HOSTNAME_ADMIN_URL: http://localhost:${PROXY_PUBLIC_PORT}/kc - KC_HOSTNAME_STRICT: "false" - KC_HOSTNAME_STRICT_BACKCHANNEL: "false" - KC_HOSTNAME_DEBUG: "true" - #KC_LOG_LEVEL: debug - healthcheck: - test: [ "CMD-SHELL", "timeout 10s bash -c ':> /dev/tcp/127.0.0.1/8080' || exit 1"] - <<: *common-healthcheck - volumes: - - "./data/keycloak/import:/opt/keycloak/data/import" + backend: + extends: + file: ./compose.common.yml + service: backend depends_on: postgres: condition: service_healthy + keycloak: + condition: service_healthy networks: - - proxy - - db - - # Shared database management system - postgres: - # Not the latest version, but recommended by Keycloak. - # See https://www.keycloak.org/server/db#_supported_databases - image: postgres:15.6-bookworm - environment: - TZ: "Europe/Berlin" - POSTGRES_DB: postgres - POSTGRES_USER: postgres - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - POSTGRES_HOST_AUTH_METHOD: scram-sha-256 - POSTGRES_INITDB_ARGS: --auth=scram-sha-256 - # Vars for database init scripts - POSTGRES_KC_USER: keycloak - POSTGRES_KC_DB: keycloak - POSTGRES_KC_PASSWORD: ${POSTGRES_KC_PASSWORD} - healthcheck: - test: [ "CMD", "pg_isready", "-q", "-d", "postgres", "-U", "postgres" ] - <<: *common-healthcheck - volumes: - - postgres:/var/lib/postgresql/data - - "./postgres/initdb.d/init-keycloak-db.sh:/docker-entrypoint-initdb.d/init-keycloak-db.sh" - - "./data/db/import/keycloak-db.dump:/docker-entrypoint-initdb.d/keycloak-db.dump" - networks: + - private - db - - # Email & SMTP testing tool - mailpit: - image: axllent/mailpit - environment: - TZ: "Europe/Berlin" - MP_WEBROOT: /mailbox/ - MP_MAX_MESSAGES: 100 - MP_SMTP_AUTH_ACCEPT_ANY: 1 - MP_SMTP_AUTH_ALLOW_INSECURE: 1 - MP_QUIET: "true" - networks: - - proxy - -volumes: - postgres: - -networks: - public: - internal: false - driver: bridge - proxy: - internal: true - driver: bridge - db: - internal: true - driver: bridge diff --git a/platform/data/keycloak/import/teamagochi-realm.json b/platform/data/keycloak/import/teamagochi-realm.json index 7809de99..69ba48cc 100644 --- a/platform/data/keycloak/import/teamagochi-realm.json +++ b/platform/data/keycloak/import/teamagochi-realm.json @@ -735,8 +735,8 @@ "enabled" : true, "alwaysDisplayInConsole" : false, "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ "http://localhost:3030/*", "http://localhost:5173/*" ], - "webOrigins" : [ "http://localhost:3030", "+" ], + "redirectUris" : [ "http://localhost:3030/*", "http://localhost:5173/*", "http://localhost:4000/*" ], + "webOrigins" : [ "http://localhost:3030", "+", "http://localhost:4000" ], "notBefore" : 0, "bearerOnly" : false, "consentRequired" : false, @@ -748,7 +748,7 @@ "frontchannelLogout" : true, "protocol" : "openid-connect", "attributes" : { - "post.logout.redirect.uris" : "/*##http://localhost:3030##http://localhost:5173/*", + "post.logout.redirect.uris" : "/*##http://localhost:3030##http://localhost:5173/*##http://localhost:4000/*", "oauth2.device.authorization.grant.enabled" : "false", "backchannel.logout.revoke.offline.tokens" : "false", "use.refresh.tokens" : "true", @@ -1304,7 +1304,7 @@ "subType" : "anonymous", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "oidc-address-mapper", "saml-user-property-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper", "saml-user-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-role-list-mapper", "oidc-full-name-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-full-name-mapper", "oidc-usermodel-attribute-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "saml-user-property-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-role-list-mapper" ] } }, { "id" : "59a69e26-7bca-418b-bdb0-c538d42c7f99", @@ -1313,7 +1313,7 @@ "subType" : "authenticated", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "saml-user-property-mapper", "saml-user-attribute-mapper", "oidc-address-mapper", "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "saml-role-list-mapper", "oidc-full-name-mapper", "saml-user-property-mapper", "oidc-address-mapper", "saml-user-attribute-mapper" ] } }, { "id" : "75f731da-45cd-43ad-a244-67d8f59fd1c2", diff --git a/platform/devdash/src/config.ts b/platform/devdash/src/config.ts index 5b70b69d..11e7d6d1 100644 --- a/platform/devdash/src/config.ts +++ b/platform/devdash/src/config.ts @@ -20,6 +20,14 @@ export const mainMenu: Page[] = [ ]; export const services: Service[] = [ + { + name: 'Web-Backend', + description: 'The Teamagochi backend service.', + links: [ + {title: 'Swagger UI', url: '/backend/q/swagger-ui/'}, + {title: 'openapi.json', url: '/backend/q/openapi.json'} + ], + }, { name: 'Tea:shan', description: 'Eclipse Leshan Server demo including a Bootstrap Server.', @@ -53,4 +61,4 @@ export const themeOptions: ThemeOptions = { main: '#f26c73', }, }, -}; \ No newline at end of file +}; diff --git a/platform/postgres/initdb.d/init-backend-db.sh b/platform/postgres/initdb.d/init-backend-db.sh new file mode 100644 index 00000000..1f036cbd --- /dev/null +++ b/platform/postgres/initdb.d/init-backend-db.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -e + +# Create keycloak user and database +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL + CREATE USER ${POSTGRES_BACKEND_USER} WITH PASSWORD '${POSTGRES_BACKEND_PASSWORD}'; + CREATE DATABASE ${POSTGRES_BACKEND_DB}; + GRANT ALL PRIVILEGES ON DATABASE ${POSTGRES_BACKEND_DB} TO ${POSTGRES_BACKEND_USER}; +EOSQL + +# Be sure the schema exists +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL + \connect ${POSTGRES_BACKEND_DB}; + CREATE SCHEMA IF NOT EXISTS ${POSTGRES_BACKEND_DB} AUTHORIZATION ${POSTGRES_BACKEND_USER}; +EOSQL diff --git a/platform/postgres/initdb.d/init-keycloak-db.sh b/platform/postgres/initdb.d/init-keycloak-db.sh index b4b6bcb9..82340ccb 100644 --- a/platform/postgres/initdb.d/init-keycloak-db.sh +++ b/platform/postgres/initdb.d/init-keycloak-db.sh @@ -16,6 +16,6 @@ fi # Be sure the schema exists psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL - \connect keycloak; + \connect ${POSTGRES_KC_DB}; CREATE SCHEMA IF NOT EXISTS keycloak AUTHORIZATION ${POSTGRES_KC_USER}; EOSQL diff --git a/platform/proxy/templates/backend.include.template b/platform/proxy/templates/backend.include.template new file mode 100644 index 00000000..9655c9e2 --- /dev/null +++ b/platform/proxy/templates/backend.include.template @@ -0,0 +1,8 @@ +location = /backend { + rewrite ^ $scheme://$http_host/backend/ permanent; +} + +location /backend/ { + proxy_pass ${BACKEND_WEB_URL}; + include proxy_params; +} diff --git a/platform/proxy/templates/default.conf.template b/platform/proxy/templates/default.conf.template index feca3dbf..3c6913d2 100644 --- a/platform/proxy/templates/default.conf.template +++ b/platform/proxy/templates/default.conf.template @@ -4,6 +4,8 @@ server { server_name _; + include /etc/nginx/conf.d/*.include; + # # Development Dashboard # From 47f85d83c74ed232d0264b566816bf8220c11115 Mon Sep 17 00:00:00 2001 From: ozfox Date: Thu, 20 Jun 2024 15:52:59 +0200 Subject: [PATCH 5/5] platform: hacky preconfiguration for the leshan demo client --- platform/teashan/client/client-wrapper.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/platform/teashan/client/client-wrapper.sh b/platform/teashan/client/client-wrapper.sh index b5b3110e..c6771541 100755 --- a/platform/teashan/client/client-wrapper.sh +++ b/platform/teashan/client/client-wrapper.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # # Wrapper script for leshan-client-demo.jar @@ -18,10 +18,10 @@ fi if [ ! -d ./models ]; then mkdir models - pushd models + pushd models || exit 1 wget https://raw.githubusercontent.com/smartuni/teamagochi/main/platform/data/objectmodels/32769.xml wget https://raw.githubusercontent.com/smartuni/teamagochi/main/platform/data/objectmodels/32770.xml - popd + popd || exit 1 fi USE_BOOTSTRAP=true @@ -31,7 +31,8 @@ ENDPOINT_NAME=teamagochi-java-client-0 if [ "$USE_BOOTSTRAP" = true ]; then # With bootstrap server SERVER_URL=coap://localhost:5683 - java -jar ./leshan-client-demo.jar \ + { sleep 5; echo -e "create 32769\n create 32770\n delete 6\n delete 3303\n delete 3442\n"; } \ + | java -jar ./leshan-client-demo.jar \ --models-folder="$MODELS_FOLDER" \ --server-url="$SERVER_URL" \ --endpoint-name="$ENDPOINT_NAME" \ @@ -39,7 +40,8 @@ if [ "$USE_BOOTSTRAP" = true ]; then else # Without bootstrap server SERVER_URL=coap://localhost:5783 - java -jar ./leshan-client-demo.jar \ + { sleep 5; echo -e "create 32769\n create 32770\n delete 6\n delete 3303\n delete 3442\n"; } \ + | java -jar ./leshan-client-demo.jar \ --models-folder="$MODELS_FOLDER" \ --server-url="$SERVER_URL" \ --endpoint-name="$ENDPOINT_NAME" \