-
Notifications
You must be signed in to change notification settings - Fork 3
feat(e2e): implement local https proxy for e2e testing #457
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ryanbas21
wants to merge
1
commit into
main
Choose a base branch
from
nginx-https-webauthn
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| # Local HTTPS for E2E apps | ||
|
|
||
| This guide explains how to generate and refresh the TLS certificates used by the shared HTTPS reverse proxy in the `e2e` stack. | ||
|
|
||
| ## Why this exists | ||
|
|
||
| Some capabilities (for example WebAuthn) require that the browser sees a fully trusted HTTPS origin. Instead of teaching every test app to serve HTTPS, we terminate TLS once at a lightweight proxy container and keep the individual apps on HTTP. The proxy reads a single certificate/key pair from `e2e/certs` and routes traffic (e.g., `/davinci`, `/ping-am`) to the existing services. | ||
|
|
||
| ## One-time prerequisites | ||
|
|
||
| 1. Install [`mkcert`](https://github.com/FiloSottile/mkcert): | ||
| - macOS: `brew install mkcert nss` | ||
| - Windows (Powershell): `choco install mkcert` or `scoop install mkcert` | ||
| - Linux: use your package manager or download the binary | ||
| 2. Trust the local root into the OS/browser store. Run `mkcert -install` (the script below will do this automatically if it has not been run before). Administrator/root approval may be needed. | ||
|
|
||
| If your device already trusts the Ping internal CA that issues the certificates, you can skip `mkcert` and instead place the relevant certificate/key in `e2e/certs`. For the default workflow we ship, we rely on `mkcert`. | ||
|
|
||
| ## Bootstrap the certificate | ||
|
|
||
| From the repository root run: | ||
|
|
||
| ```bash | ||
| pnpm run setup:https | ||
| ``` | ||
|
|
||
| `pnpm run setup:https` is a thin wrapper around `scripts/bootstrap-https.sh`. The script: | ||
|
|
||
| - Ensures `mkcert` is installed | ||
| - Installs the mkcert root CA into the system trust store if it is not present | ||
| - Creates (or refreshes) `e2e/certs/proxy-cert.pem` and `e2e/certs/proxy-key.pem` with SANs for `localhost`, `127.0.0.1`, and `::1` | ||
|
|
||
| > The files can safely be committed to your local clone—they should **not** be committed to git. | ||
|
|
||
| ## Regenerating or customizing | ||
|
|
||
| - Re-run `pnpm run setup:https` at any time; it overwrites the pem files in place. | ||
| - To add additional hostnames (for example `dev.ping.local`), edit `DOMAIN_LIST` inside `scripts/bootstrap-https.sh` before re-running the script. Make sure the new hostnames resolve to your proxy (via `/etc/hosts`, corporate DNS, etc.). | ||
|
|
||
| ## Docker integration | ||
|
|
||
| The `e2e/docker-compose.yml` proxy service mounts the mkcert outputs directly (`./certs/proxy-cert.pem` and `./certs/proxy-key.pem`) into `/etc/nginx/tls/`, matching the defaults baked into the Docker image. | ||
|
|
||
| When you run `pnpm https-proxy:up` (or directly `docker compose -f e2e/docker-compose.yml up`), the proxy serves `https://localhost:8443/...` using the freshly generated certificate. Because the root CA is trusted, browsers treat the origin as fully secure and WebAuthn flows work without security errors. | ||
|
|
||
| If you are using a corporate-managed device, your IT team can distribute the mkcert root CA (or an equivalent internal CA) via MDM so that new developers do not need to run `mkcert -install` manually. | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| - **Browser warning persists**: Confirm the mkcert root CA is installed in the OS trust store (run `mkcert -CAROOT` to locate it). Remove any stale certificates and rerun the bootstrap script. | ||
| - **mkcert not found**: Ensure it is on your `PATH`. Open a new shell after installation. | ||
| - **Permission issues on install**: `mkcert -install` modifies OS certificate stores and may require elevated privileges. Run the script again with the necessary rights. | ||
|
|
||
| With the certificate in place, the HTTPS proxy is ready and the E2E apps can rely on secure origins without per-app TLS configuration. | ||
|
|
||
| ## Running Applications Behind the Proxy | ||
|
|
||
| When running an application that needs to be accessed by the HTTPS proxy, you must ensure that its development server is accessible from within the Docker network. | ||
|
|
||
| For Vite-based applications (like `oidc-app` or `davinci-app`), you need to start the dev server with the `--host=0.0.0.0` flag. This tells the server to listen on all available network interfaces, not just `localhost`. This is essential for the `nginx` proxy container to be able to connect to your application's dev server. | ||
|
|
||
| Here is an example command: | ||
|
|
||
| ```bash | ||
| pnpm nx run @forgerock/davinci-app:nxServe --port=5173 --host=0.0.0.0 | ||
| ``` | ||
|
|
||
| If you forget to add `--host=0.0.0.0`, the proxy will not be able to reach your application, and you will see a "502 Bad Gateway" error in your browser. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| services: | ||
| https-proxy: | ||
| build: | ||
| context: ./https-proxy | ||
| image: ping-local-https | ||
| ports: | ||
| - '8443:8443' | ||
| environment: | ||
| LISTEN_PORT: 8443 | ||
| SSL_CERT_PATH: /etc/nginx/tls/proxy-cert.pem | ||
| SSL_CERT_KEY_PATH: /etc/nginx/tls/proxy-key.pem | ||
| DAVINCI_UPSTREAM: host.docker.internal:5829 | ||
| OIDC_UPSTREAM: host.docker.internal:5173 | ||
| PROTECT_UPSTREAM: host.docker.internal:4300 | ||
| DEVICE_UPSTREAM: host.docker.internal:4301 | ||
| MOCK_API_UPSTREAM: host.docker.internal:9443 | ||
| volumes: | ||
| - ./certs:/etc/nginx/tls:ro | ||
| extra_hosts: | ||
| - 'host.docker.internal:host-gateway' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| FROM nginx:1.27-alpine | ||
|
|
||
| RUN apk add --no-cache gettext | ||
|
|
||
| ENV LISTEN_PORT=8443 \ | ||
| SSL_CERT_PATH=/etc/nginx/tls/proxy-cert.pem \ | ||
| SSL_CERT_KEY_PATH=/etc/nginx/tls/proxy-key.pem \ | ||
| DAVINCI_UPSTREAM=host.docker.internal:5829 \ | ||
| OIDC_UPSTREAM=host.docker.internal:5173 \ | ||
| PROTECT_UPSTREAM=host.docker.internal:4300 \ | ||
| DEVICE_UPSTREAM=host.docker.internal:4301 \ | ||
| MOCK_API_UPSTREAM=host.docker.internal:9443 | ||
|
|
||
| COPY default.conf.template /etc/nginx/templates/default.conf.template | ||
| COPY docker-entrypoint.d/ /docker-entrypoint.d/ | ||
|
|
||
| EXPOSE 8443 | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| map $http_upgrade $connection_upgrade { | ||
| default upgrade; | ||
| '' close; | ||
| } | ||
|
|
||
| server { | ||
| listen ${LISTEN_PORT} ssl http2; | ||
| listen [::]:${LISTEN_PORT} ssl http2; | ||
| server_name _; | ||
|
|
||
| ssl_certificate ${SSL_CERT_PATH}; | ||
| ssl_certificate_key ${SSL_CERT_KEY_PATH}; | ||
| ssl_session_cache shared:SSL:10m; | ||
| ssl_session_timeout 10m; | ||
| ssl_protocols TLSv1.2 TLSv1.3; | ||
| ssl_prefer_server_ciphers on; | ||
|
|
||
| keepalive_timeout 65; | ||
|
|
||
| if ($scheme = http) { | ||
| return 301 https://$host$request_uri; | ||
| } | ||
|
|
||
| add_header Strict-Transport-Security "max-age=31536000" always; | ||
| add_header X-Content-Type-Options nosniff; | ||
| add_header X-Frame-Options SAMEORIGIN; | ||
| add_header X-XSS-Protection "1; mode=block"; | ||
|
|
||
| proxy_set_header Host $host; | ||
| proxy_set_header X-Real-IP $remote_addr; | ||
| proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||
| proxy_set_header X-Forwarded-Proto $scheme; | ||
| proxy_http_version 1.1; | ||
| proxy_set_header Upgrade $http_upgrade; | ||
| proxy_set_header Connection $connection_upgrade; | ||
| proxy_cache_bypass $http_upgrade; | ||
| proxy_redirect off; | ||
|
|
||
| client_max_body_size 20m; | ||
|
|
||
| location /davinci/ { | ||
| rewrite ^/davinci/(.*)$ /$1 break; | ||
| proxy_pass http://${DAVINCI_UPSTREAM}; | ||
| } | ||
|
|
||
| location /ping-am/ { | ||
| proxy_pass http://${OIDC_UPSTREAM}; | ||
| } | ||
|
|
||
| location /ping-one/ { | ||
| proxy_pass http://${OIDC_UPSTREAM}; | ||
| } | ||
|
|
||
| location /protect/ { | ||
| rewrite ^/protect/(.*)$ /$1 break; | ||
| proxy_pass http://${PROTECT_UPSTREAM}; | ||
| } | ||
|
|
||
| location /device-client/ { | ||
| rewrite ^/device-client/(.*)$ /$1 break; | ||
| proxy_pass http://${DEVICE_UPSTREAM}; | ||
| } | ||
|
|
||
| location /mock-api/ { | ||
| rewrite ^/mock-api/(.*)$ /$1 break; | ||
| proxy_pass http://${MOCK_API_UPSTREAM}; | ||
| } | ||
|
|
||
| # Fallback to oidc app for everything else | ||
| location / { | ||
| proxy_pass http://${OIDC_UPSTREAM}; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| #!/bin/sh | ||
| set -eu | ||
|
|
||
| template="/etc/nginx/templates/default.conf.template" | ||
| output="/etc/nginx/conf.d/default.conf" | ||
|
|
||
| echo "[https-proxy] Rendering nginx config..." | ||
| envsubst '\ | ||
| ${LISTEN_PORT} \ | ||
| ${SSL_CERT_PATH} \ | ||
| ${SSL_CERT_KEY_PATH} \ | ||
| ${DAVINCI_UPSTREAM} \ | ||
| ${OIDC_UPSTREAM} \ | ||
| ${PROTECT_UPSTREAM} \ | ||
| ${DEVICE_UPSTREAM} \ | ||
| ${MOCK_API_UPSTREAM} \ | ||
| ' < "$template" > "$output" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| CERT_DIR="$(git rev-parse --show-toplevel)/e2e/certs" | ||
| DOMAIN_LIST=("localhost" "127.0.0.1" "::1") | ||
|
|
||
| mkdir -p "${CERT_DIR}" | ||
|
|
||
| if ! command -v mkcert >/dev/null 2>&1; then | ||
| echo "mkcert not found; install it first." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| pushd "${CERT_DIR}" >/dev/null | ||
|
|
||
| if [ ! -f "$(mkcert -CAROOT)/rootCA.pem" ]; then | ||
| echo "Installing mkcert root CA..." | ||
| mkcert -install | ||
| fi | ||
|
|
||
| echo "Generating proxy certificate..." | ||
| mkcert -cert-file proxy-cert.pem -key-file proxy-key.pem "${DOMAIN_LIST[@]}" | ||
|
|
||
| popd >/dev/null | ||
|
|
||
| echo "Certificate material written to ${CERT_DIR}" |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarify the wording about certificate files.
The statement "The files can safely be committed to your local clone—they should not be committed to git" is confusing because a "local clone" is still a git repository. This creates a contradiction.
Apply this diff to clarify:
📝 Committable suggestion
🤖 Prompt for AI Agents