diff --git a/.env.example b/.env.example index 21a60e60d1f..95285979747 100644 --- a/.env.example +++ b/.env.example @@ -44,6 +44,9 @@ PORT=3000 FE_HOST=localhost FE_PORT=4200 +# Default TLD for docker dev stack (e.g. when set to "local", services will be openproject.local, nextcloud.local, etc.) +OPENPROJECT_DOCKER_DEV_TLD=local + # Use this variables to configure hostnames for frontend and backend, e.g. to enable HTTPS in docker development setup OPENPROJECT_DEV_HOST=localhost OPENPROJECT_DEV_URL=http://${OPENPROJECT_DEV_HOST}:${FE_PORT} diff --git a/docker-compose.yml b/docker-compose.yml index b26bf37eb57..12ef9b8fb3a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -87,7 +87,7 @@ services: networks: - network environment: - __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS: "openproject-assets.local" + __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS: "openproject-assets.${OPENPROJECT_DOCKER_DEV_TLD}" ports: - "${FE_PORT:-4200}:4200" diff --git a/docker/dev/gitlab/.env b/docker/dev/gitlab/.env new file mode 100644 index 00000000000..c962deb2abe --- /dev/null +++ b/docker/dev/gitlab/.env @@ -0,0 +1 @@ +OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/gitlab/docker-compose.yml b/docker/dev/gitlab/docker-compose.yml index 7389f4ed4eb..42ad0f3eb6a 100644 --- a/docker/dev/gitlab/docker-compose.yml +++ b/docker/dev/gitlab/docker-compose.yml @@ -19,10 +19,10 @@ services: networks: - external extra_hosts: - - "openproject.local:host-gateway" + - "openproject.${OPENPROJECT_DOCKER_DEV_TLD}:host-gateway" labels: - "traefik.enable=true" - - "traefik.http.routers.gitlab.rule=Host(`gitlab.local`)" + - "traefik.http.routers.gitlab.rule=Host(`gitlab.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.gitlab.entrypoints=websecure" - "traefik.http.services.gitlab.loadbalancer.server.port=80" diff --git a/docker/dev/hocuspocus/.env b/docker/dev/hocuspocus/.env new file mode 100644 index 00000000000..c962deb2abe --- /dev/null +++ b/docker/dev/hocuspocus/.env @@ -0,0 +1 @@ +OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/hocuspocus/docker-compose.yml b/docker/dev/hocuspocus/docker-compose.yml index 3888ab9e86b..4419c6e4365 100644 --- a/docker/dev/hocuspocus/docker-compose.yml +++ b/docker/dev/hocuspocus/docker-compose.yml @@ -5,7 +5,7 @@ services: image: openproject/hocuspocus:latest labels: - "traefik.enable=true" - - "traefik.http.routers.hocuspocus.rule=Host(`hocuspocus.local`)" + - "traefik.http.routers.hocuspocus.rule=Host(`hocuspocus.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.hocuspocus.service=hocuspocus-service" - "traefik.http.routers.hocuspocus.tls=true" - "traefik.http.services.hocuspocus-service.loadbalancer.server.port=1234" @@ -14,7 +14,7 @@ services: networks: - gateway environment: - - ALLOWED_DOMAINS=openproject.local,localhost + - ALLOWED_DOMAINS=openproject.${OPENPROJECT_DOCKER_DEV_TLD},localhost - NODE_TLS_REJECT_UNAUTHORIZED=0 - SECRET=secret12345 networks: diff --git a/docker/dev/keycloak/.env b/docker/dev/keycloak/.env new file mode 100644 index 00000000000..c962deb2abe --- /dev/null +++ b/docker/dev/keycloak/.env @@ -0,0 +1 @@ +OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/keycloak/docker-compose.yml b/docker/dev/keycloak/docker-compose.yml index a8cd4d2803a..4c5bf556edc 100644 --- a/docker/dev/keycloak/docker-compose.yml +++ b/docker/dev/keycloak/docker-compose.yml @@ -35,7 +35,7 @@ services: - KEYCLOAK_ADMIN=admin - KEYCLOAK_ADMIN_PASSWORD=admin - KC_DB_SCHEMA=public - - KC_HOSTNAME=keycloak.local + - KC_HOSTNAME=keycloak.${OPENPROJECT_DOCKER_DEV_TLD} - KC_TRANSACTION_XA_ENABLED=false volumes: - /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt:ro @@ -43,10 +43,8 @@ services: - ./themes/:/opt/keycloak/themes/ labels: - "traefik.enable=true" - - "traefik.http.routers.keycloak-sub-secure.rule=Host(`keycloak.local`)" + - "traefik.http.routers.keycloak-sub-secure.rule=Host(`keycloak.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.keycloak-sub-secure.entrypoints=websecure" - - "traefik.http.routers.keycloak-sub-secure.tls=true" - - "traefik.http.routers.keycloak-sub-secure.tls.certresolver=step" depends_on: - db-keycloak diff --git a/docker/dev/minio/.env b/docker/dev/minio/.env new file mode 100644 index 00000000000..c962deb2abe --- /dev/null +++ b/docker/dev/minio/.env @@ -0,0 +1 @@ +OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/minio/docker-compose.yml b/docker/dev/minio/docker-compose.yml index 778277abfc6..cecc8ee8df9 100644 --- a/docker/dev/minio/docker-compose.yml +++ b/docker/dev/minio/docker-compose.yml @@ -19,13 +19,13 @@ services: - "traefik.enable=true" # MinIO API - "traefik.http.routers.minio.entrypoints=websecure" - - "traefik.http.routers.minio.rule=Host(`minio.local`)" + - "traefik.http.routers.minio.rule=Host(`minio.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.minio.service=minio" - "traefik.http.routers.minio.tls.certresolver=step" - "traefik.http.services.minio.loadbalancer.server.port=9000" # MinIO Admin Console (Management UI) - "traefik.http.routers.minioadmin.entrypoints=websecure" - - "traefik.http.routers.minioadmin.rule=Host(`minioadmin.local`)" + - "traefik.http.routers.minioadmin.rule=Host(`minioadmin.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.minioadmin.service=minioadmin" - "traefik.http.routers.minioadmin.tls.certresolver=step" - "traefik.http.services.minioadmin.loadbalancer.server.port=9001" diff --git a/docker/dev/nextcloud/.env b/docker/dev/nextcloud/.env new file mode 100644 index 00000000000..c962deb2abe --- /dev/null +++ b/docker/dev/nextcloud/.env @@ -0,0 +1 @@ +OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/nextcloud/docker-compose.yml b/docker/dev/nextcloud/docker-compose.yml index 9db2fa4ef1a..100a37dbc49 100644 --- a/docker/dev/nextcloud/docker-compose.yml +++ b/docker/dev/nextcloud/docker-compose.yml @@ -11,7 +11,7 @@ services: # - ../nextcloud_apps:/var/www/html/custom_apps labels: - "traefik.enable=true" - - "traefik.http.routers.nextcloud.rule=Host(`nextcloud.local`)" + - "traefik.http.routers.nextcloud.rule=Host(`nextcloud.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.nextcloud.entrypoints=websecure" cron: diff --git a/docker/dev/tls/.env b/docker/dev/tls/.env new file mode 100644 index 00000000000..c962deb2abe --- /dev/null +++ b/docker/dev/tls/.env @@ -0,0 +1 @@ +OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/tls/docker-compose.core-override.example.yml b/docker/dev/tls/docker-compose.core-override.example.yml index cf33272417c..d45a9be091b 100644 --- a/docker/dev/tls/docker-compose.core-override.example.yml +++ b/docker/dev/tls/docker-compose.core-override.example.yml @@ -6,30 +6,30 @@ x-op-env-override: &environment SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt # uncomment and set all the envs below to integrate keycloak with OpenProject # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_DISPLAY__NAME: Keycloak - # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_HOST: keycloak.local - # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_IDENTIFIER: https://openproject.local + # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_HOST: keycloak.${OPENPROJECT_DOCKER_DEV_TLD} + # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_IDENTIFIER: https://openproject.${OPENPROJECT_DOCKER_DEV_TLD} # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_SECRET: - # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_ISSUER: https://keycloak.local/realms/ + # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_ISSUER: https://keycloak.${OPENPROJECT_DOCKER_DEV_TLD}/realms/ # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_AUTHORIZATION__ENDPOINT: /realms//protocol/openid-connect/auth # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_TOKEN__ENDPOINT: /realms//protocol/openid-connect/token # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_USERINFO__ENDPOINT: /realms//protocol/openid-connect/userinfo - # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_END__SESSION__ENDPOINT: https://keycloak.local/realms//protocol/openid-connect/logout + # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_END__SESSION__ENDPOINT: https://keycloak.${OPENPROJECT_DOCKER_DEV_TLD}/realms//protocol/openid-connect/logout # uncomment the following for using minio (local S3) as file storage with TLS support: # OPENPROJECT_ATTACHMENTS__STORAGE: "fog" # OPENPROJECT_FOG_DIRECTORY: "openproject-uploads" # OPENPROJECT_FOG_CREDENTIALS_PROVIDER: "AWS" # Minio is S3 compliant, so we can use the AWS provider - # OPENPROJECT_FOG_CREDENTIALS_ENDPOINT: "https://minio.local" + # OPENPROJECT_FOG_CREDENTIALS_ENDPOINT: "https://minio.${OPENPROJECT_DOCKER_DEV_TLD}" # OPENPROJECT_FOG_CREDENTIALS_AWS__ACCESS__KEY__ID: "minioadmin" # OPENPROJECT_FOG_CREDENTIALS_AWS__SECRET__ACCESS__KEY: "minioadmin" # OPENPROJECT_FOG_CREDENTIALS_PATH__STYLE: "true" # OPENPROJECT_FOG_CREDENTIALS_REGION: "us-east-1" - # OPENPROJECT_DEV_EXTRA_HOSTS: "${OPENPROJECT_DEV_HOST},minio.local" + # OPENPROJECT_DEV_EXTRA_HOSTS: "${OPENPROJECT_DEV_HOST},minio.${OPENPROJECT_DOCKER_DEV_TLD}" services: backend: environment: <<: *environment - OPENPROJECT_CLI_PROXY: "https://openproject-assets.local" + OPENPROJECT_CLI_PROXY: "https://openproject-assets.${OPENPROJECT_DOCKER_DEV_TLD}" networks: - external volumes: @@ -42,7 +42,7 @@ services: # - ~/.step/certs:/usr/local/share/ca-certificates labels: - "traefik.enable=true" - - "traefik.http.routers.openproject.rule=Host(`openproject.local`)" + - "traefik.http.routers.openproject.rule=Host(`openproject.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.openproject.entrypoints=websecure" worker: @@ -77,7 +77,7 @@ services: - external labels: - "traefik.enable=true" - - "traefik.http.routers.openproject-assets.rule=Host(`openproject-assets.local`)" + - "traefik.http.routers.openproject-assets.rule=Host(`openproject-assets.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.openproject-assets.entrypoints=websecure" # You need to define the same external network diff --git a/docker/dev/tls/docker-compose.override.example.yml b/docker/dev/tls/docker-compose.override.example.yml index 37ab60bb7ed..f2f6b439f5f 100644 --- a/docker/dev/tls/docker-compose.override.example.yml +++ b/docker/dev/tls/docker-compose.override.example.yml @@ -1,17 +1,20 @@ services: traefik: - networks: - external: - aliases: - # Traefik is aliased to all the services that run behind it here. - # The reason to do this is that step-ca tries to validate the domains - # by connecting to them, and we'd like it to go through traefik, instead - # of calling the service containers directly. - - openproject.local - - openproject-assets.local - - nextcloud.local - - gitlab.local - - keycloak.local - - hocuspocus.local - - minio.local - - minioadmin.local + # Overwrite to enable Let's encrypt instead of using Step CA for certificate generation + # command: > + # --entryPoints.websecure.http.tls.certresolver=letsencrypt + # --certificatesresolvers.letsencrypt.acme.email=you@example.com + + # For step CA only: Overwrite trusted CA certificates with Step root CA (not needed for Let's encrypt) + environment: + - LEGO_CA_CERTIFICATES=/step/certs/root_ca.crt + + # Necessary for certificates via Step CA only + depends_on: + step: + condition: service_healthy + + # Enabling traefik for the traefik container itself will give you access to the Traefik Dashboard, which can be a useful + # tool to inspect whether everything is working fine. It's not advised to publicly expose this dashboard. + labels: + - "traefik.enable=true" diff --git a/docker/dev/tls/docker-compose.yml b/docker/dev/tls/docker-compose.yml index d9172f23f87..1d4542081c1 100644 --- a/docker/dev/tls/docker-compose.yml +++ b/docker/dev/tls/docker-compose.yml @@ -1,44 +1,29 @@ services: traefik: image: traefik:latest - command: > - --log.level=INFO - --providers.docker - --providers.docker.network=gateway - --providers.docker.exposedByDefault=false - --api.dashboard=true - --api.disabledashboardad=true - --entryPoints.web.address=:80 - --entryPoints.web.http.redirections.entrypoint.to=websecure - --entryPoints.websecure.address=:443 - --entryPoints.websecure.http.tls=true - --entryPoints.websecure.http.tls.certresolver=step - --certificatesresolvers.step.acme.caserver=https://step:9000/acme/acme/directory - --certificatesresolvers.step.acme.tlschallenge=true - --certificatesresolvers.step.acme.email=root@localhost - --certificatesresolvers.step.acme.keytype=RSA4096 - --certificatesresolvers.step.acme.storage=acme.json - environment: - - TZ=UTC - - LEGO_CA_CERTIFICATES=/step/certs/root_ca.crt ports: - "80:80" - "443:443" volumes: + - ./traefik.yaml:/etc/traefik/traefik.yaml:ro - /var/run/docker.sock:/var/run/docker.sock - ./acme.json:/acme.json - step:/step:ro restart: unless-stopped - depends_on: - step: - condition: service_healthy networks: external: aliases: - - traefik.local + - traefik.${OPENPROJECT_DOCKER_DEV_TLD} + - openproject.${OPENPROJECT_DOCKER_DEV_TLD} + - openproject-assets.${OPENPROJECT_DOCKER_DEV_TLD} + - nextcloud.${OPENPROJECT_DOCKER_DEV_TLD} + - gitlab.${OPENPROJECT_DOCKER_DEV_TLD} + - keycloak.${OPENPROJECT_DOCKER_DEV_TLD} + - hocuspocus.${OPENPROJECT_DOCKER_DEV_TLD} + - minio.${OPENPROJECT_DOCKER_DEV_TLD} + - minioadmin.${OPENPROJECT_DOCKER_DEV_TLD} labels: - - "traefik.enable=true" - - "traefik.http.routers.traefik.rule=Host(`traefik.local`)" + - "traefik.http.routers.traefik.rule=Host(`traefik.${OPENPROJECT_DOCKER_DEV_TLD}`)" - "traefik.http.routers.traefik.service=api@internal" - "traefik.http.routers.traefik.entrypoints=websecure" diff --git a/docker/dev/tls/traefik.yaml b/docker/dev/tls/traefik.yaml new file mode 100644 index 00000000000..7f19e16d581 --- /dev/null +++ b/docker/dev/tls/traefik.yaml @@ -0,0 +1,39 @@ +log: + level: INFO + +api: + dashboard: true + disabledashboardad: true + +providers: + docker: + network: gateway + exposedByDefault: false + +entryPoints: + web: + address: ":80" + http: + redirections: + entrypoint: + to: websecure + websecure: + address: ":443" + http: + tls: + certresolver: step # Using step by default, overwritable via CLI + +certificatesresolvers: + step: + acme: + caserver: https://step:9000/acme/acme/directory + tlschallenge: true + email: root@localhost + keytype: RSA4096 + storage: acme.json + letsencrypt: + acme: + keytype: RSA4096 + storage: acme.json + httpChallenge: + entryPoint: web diff --git a/docs/development/development-environment/docker/README.md b/docs/development/development-environment/docker/README.md index e372b8f1e28..18b9d22921b 100644 --- a/docs/development/development-environment/docker/README.md +++ b/docs/development/development-environment/docker/README.md @@ -264,13 +264,6 @@ define for your services to your `/etc/hosts`. ::1 openproject.local openproject-assets.local traefik.local ``` -#### DNS? Where are you? - -We have plans to add a local DNS to this development setup, making two things possible: - -1. No requirement to amend your `/etc/hosts` file anymore. -2. Being accessible from another device within your internal network (e.g. a cellphone). - ### Local certificate authority We use [traefik](https://traefik.io/) as a reverse proxy and [step-ca](https://smallstep.com/docs/step-ca/) as a local @@ -457,6 +450,23 @@ to have Nextcloud running to test the Nextcloud-OpenProject integration. To do t 2. Make sure step-ca can reach it to validate it for SSH. In `docker/dev/tls/docker-compose.override.yml`, add the host to the `aliases` section of the traefik networking. +### Alternative: Using Let's encrypt + +An alternative approach is to issue certificates through Let's encrypt. This allows you to skip steps related to usage and setup +of a custom, non-trusted CA. However, it requires that you have access to a domain name that you control and requires additional +step to make the reverse proxy publicly reachable, which is not in scope of what this documentation can cover. + +If you need such a setup, you can change the `docker-compose.override.yml` for the reverse proxy, to use `letsencrypt` (see the +corresponding `docker-compose.override.example.yml`). Make sure to export an environment variable with your alternative DNS zone +before starting anything via docker compose. For example: + +```bash +export OPENPROJECT_DOCKER_DEV_TLD=dev.example.com +docker compose up -d backend frontend +``` + +Will make your containers available under openproject.dev.example.com and openproject-assets.dev.example.com respectively. + ### Troubleshooting After this setup you should be able to access your OpenProject development instance at `https://openproject.local`. If @@ -564,7 +574,7 @@ Upon setting up all the things correctly, we can see a login with `keycloak` opt ## MinIO Service (local S3 storage backend) -Within `docker/dev/minio` a compose file is provided for running a local MinIO instance with TLS support which can be used as a S3 storage for uploading files. +Within `docker/dev/minio` a compose file is provided for running a local MinIO instance with TLS support which can be used as a S3 storage for uploading files. When running with TLS support, the MinIO instance will be accessible on `https://minio.local` and a management UI (MinIO Console) will be available on `https://minioadmin.local/`. ### Running the MinIO Instance @@ -578,8 +588,8 @@ docker compose --project-directory docker/dev/minio up -d ``` This will automatically create a bucket named `openproject-uploads` which is used to store uploaded files. -If you want to use TLS support, make sure to copy and uncomment the MinIO configuration environment variables in `docker/dev/tls/docker-compose.core.override.example.yml` to your `docker-compose.override.yml` file in the project root directory. If you want to use MinIO without TLS support, make sure to copy the environment variables from `docker/dev/minio/docker-compose.core-override.example.yml` to your `docker-compose.override.yml` file (in the project root directory). -After that, hard restart the `backend` service to apply the changes: +If you want to use TLS support, make sure to copy and uncomment the MinIO configuration environment variables in `docker/dev/tls/docker-compose.core.override.example.yml` to your `docker-compose.override.yml` file in the project root directory. If you want to use MinIO without TLS support, make sure to copy the environment variables from `docker/dev/minio/docker-compose.core-override.example.yml` to your `docker-compose.override.yml` file (in the project root directory). +After that, hard restart the `backend` service to apply the changes: ``` docker compose down backend