Compare commits

..
Author SHA1 Message Date
Viktor Liu a0f4a3564a Add CrowdSec AppSec mode to reverse proxy access control 2026-07-27 15:56:09 +02:00
385 changed files with 8417 additions and 52632 deletions

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
{
"extends": ["next/core-web-vitals","prettier"],
"plugins": ["simple-import-sort"],
"rules": {
"simple-import-sort/imports": [
"warn",
{
"groups": [["^\\u0000", "^@?\\w", "^[^.]", "^\\."]]
}
],
"simple-import-sort/exports": "warn"
}
}
+2 -34
View File
@@ -98,33 +98,13 @@ jobs:
type=ref,event=pr type=ref,event=pr
type=sha type=sha
- name: Docker meta (rootless)
id: meta_rootless
uses: docker/metadata-action@v5
with:
images: |
${{ env.DOCKERHUB_IMAGE }}
${{ env.GHCR_IMAGE }}
flavor: |
latest=false
suffix=-rootless,onlatest=true
tags: |
type=schedule
type=ref,event=branch
type=ref,event=tag
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') }}
type=ref,event=pr
type=sha
- name: Log in to Docker Hub - name: Log in to Docker Hub
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
username: ${{ secrets.NB_DOCKER_USER }} username: ${{ secrets.NB_DOCKER_USER }}
password: ${{ secrets.NB_DOCKER_TOKEN }} password: ${{ secrets.NB_DOCKER_TOKEN }}
- name: Log in to the GitHub Container registry - name: Log in to the GitHub Container registry
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
@@ -136,23 +116,11 @@ jobs:
with: with:
context: . context: .
file: docker/Dockerfile file: docker/Dockerfile
push: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} push: true
platforms: linux/amd64,linux/arm64,linux/arm platforms: linux/amd64,linux/arm64,linux/arm
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
- name: Build and push rootless Docker image - run: |
uses: docker/build-push-action@v5
with:
context: .
file: docker/Dockerfile.rootless
push: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
platforms: linux/amd64,linux/arm64,linux/arm
tags: ${{ steps.meta_rootless.outputs.tags }}
labels: ${{ steps.meta_rootless.outputs.labels }}
- if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
run: |
echo '### Pushed tags' >> $GITHUB_STEP_SUMMARY echo '### Pushed tags' >> $GITHUB_STEP_SUMMARY
echo '${{ steps.meta.outputs.tags }}' >> $GITHUB_STEP_SUMMARY echo '${{ steps.meta.outputs.tags }}' >> $GITHUB_STEP_SUMMARY
echo '${{ steps.meta_rootless.outputs.tags }}' >> $GITHUB_STEP_SUMMARY
-81
View File
@@ -1,81 +0,0 @@
name: Bump netbird wasm client
on:
workflow_dispatch:
inputs:
tag:
description: 'NetBird release tag (e.g. v0.78.0)'
required: true
type: string
concurrency:
group: ${{ github.workflow }}-${{ inputs.tag }}
cancel-in-progress: false
jobs:
bump:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: main
token: ${{ secrets.NETBIRD_DEV_BOT_GITHUB_TOKEN }}
- name: Configure git
run: |
git config --global user.email "dev@netbird.io"
git config --global user.name "netbirddev"
# The tag rides in env and is validated before use, so a crafted dispatch
# input cannot expand into shell source or reach the sed expression.
- name: Update the default wasm path
id: bump
env:
TAG: ${{ inputs.tag }}
run: |
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Refusing tag '$TAG': expected vMAJOR.MINOR.PATCH"
exit 1
fi
sed -i "s|https://pkgs.netbird.io/wasm/client/v[0-9.]*|https://pkgs.netbird.io/wasm/client/${TAG}|" src/utils/config.ts
branch="bump-netbird-wasm-${TAG#v}"
echo "branch=${branch}" >> "$GITHUB_OUTPUT"
# The branch name is derived from the tag, so a re-dispatch of the
# same release would collide with the branch and pull request an
# earlier run left behind.
if git ls-remote --exit-code --heads origin "${branch}" >/dev/null 2>&1; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "Branch ${branch} already exists, ${TAG} was handled by an earlier run"
exit 0
fi
git checkout -b "${branch}"
git add src/utils/config.ts
if git diff --cached --quiet; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "Default wasm path already at ${TAG}, nothing to do"
exit 0
fi
echo "skip=false" >> "$GITHUB_OUTPUT"
git commit -m "Bump the WASM client to ${TAG}"
git push --set-upstream origin "${branch}"
- name: Create pull request
if: steps.bump.outputs.skip != 'true'
env:
GH_TOKEN: ${{ secrets.NETBIRD_DEV_BOT_GITHUB_TOKEN }}
TAG: ${{ inputs.tag }}
BRANCH: ${{ steps.bump.outputs.branch }}
run: |
gh pr create \
--base main \
--head "$BRANCH" \
--title "Bump the WASM client to ${TAG}" \
--body "Automated wasm path bump triggered by netbird release ${TAG}."
+3 -47
View File
@@ -101,61 +101,17 @@ jobs:
echo "Using reverse-proxy tag: $TAG" echo "Using reverse-proxy tag: $TAG"
echo "tag=$TAG" >> "$GITHUB_OUTPUT" echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Restore geolocation database cache - name: Setup test environment
id: geo-cache
uses: actions/cache/restore@v4
with:
path: e2e/environment/geo-cache
key: geolite-dbs-v1
- name: Build dashboard and set up test environment
env: env:
MANAGEMENT_IMAGE_TAG: ${{ steps.management_tag.outputs.tag }} MANAGEMENT_IMAGE_TAG: ${{ steps.management_tag.outputs.tag }}
REVERSE_PROXY_IMAGE_TAG: ${{ steps.reverse_proxy_tag.outputs.tag }} REVERSE_PROXY_IMAGE_TAG: ${{ steps.reverse_proxy_tag.outputs.tag }}
# The docker environment (image pulls, Zitadel bootstrap) is run: cd ./e2e/environment && bash create-test-env.sh
# network-bound and the Next build is CPU-bound, so they overlap.
# The build reads .test-config.json via the @/config/test alias,
# and create-test-env.sh renders that file only after the Zitadel
# bootstrap — so the build waits for it before starting.
run: |
(cd ./e2e/environment && bash create-test-env.sh) > env-setup.log 2>&1 &
ENV_PID=$!
until [ -s .test-config.json ] && jq empty .test-config.json 2>/dev/null; do
if ! kill -0 "$ENV_PID" 2>/dev/null; then break; fi
sleep 2
done
if ! [ -s .test-config.json ]; then
echo "::error::create-test-env.sh exited before rendering .test-config.json"
cat env-setup.log
exit 1
fi
# Lowest CPU priority: the containers must win the scheduler, or
# the build can starve management past the proxy-registration wait.
if ! nice -n 19 env APP_ENV=test npx next build; then
kill "$ENV_PID" 2>/dev/null || true
exit 1
fi
if ! wait "$ENV_PID"; then
echo "::error::Test environment setup failed"
cat env-setup.log
exit 1
fi
cat env-setup.log
# Saved right after env setup rather than in a post step, so a later
# test failure doesn't lose the cache.
- name: Save geolocation database cache
if: always() && steps.geo-cache.outputs.cache-hit != 'true' && hashFiles('e2e/environment/geo-cache/**') != ''
uses: actions/cache/save@v4
with:
path: e2e/environment/geo-cache
key: geolite-dbs-v1
- name: Run Playwright tests - name: Run Playwright tests
id: playwright id: playwright
run: | run: |
set -o pipefail set -o pipefail
npx playwright test --config=e2e/playwright.config.ts 2>&1 | tee playwright-output.log npm run test:ci 2>&1 | tee playwright-output.log
- name: Append Playwright summary to job summary - name: Append Playwright summary to job summary
if: always() && hashFiles('e2e/test-results/results.json') != '' if: always() && hashFiles('e2e/test-results/results.json') != ''
-32
View File
@@ -1,32 +0,0 @@
name: Unit Tests
on:
push:
branches:
- main
pull_request:
# Cancel superseded runs on the same ref so a new push doesn't queue behind an
# outdated one (mirrors build_and_push.yml).
concurrency:
group: unit-tests-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
vitest:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: setup-node
uses: actions/setup-node@v5
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm run test:unit
-1
View File
@@ -44,7 +44,6 @@ e2e/test-results/
e2e/playwright-report/ e2e/playwright-report/
/test-results/ /test-results/
/playwright-report/ /playwright-report/
playwright-report*.zip
.configs/.local-config.zitadel.json .configs/.local-config.zitadel.json
.configs/.staging-config.json .configs/.staging-config.json
.configs/.temp-config.json .configs/.temp-config.json
+12 -24
View File
@@ -11,7 +11,6 @@
# ./build.sh # build + export netbird-dashboard.tar.gz # ./build.sh # build + export netbird-dashboard.tar.gz
# IMAGE_TAG=v1 ./build.sh # override the image tag # IMAGE_TAG=v1 ./build.sh # override the image tag
# SKIP_BUILD=1 ./build.sh # reuse an existing out/ (skip npm build) # SKIP_BUILD=1 ./build.sh # reuse an existing out/ (skip npm build)
# ROOTLESS=1 ./build.sh # build the amd64-rootless image variant
# #
set -euo pipefail set -euo pipefail
@@ -20,26 +19,8 @@ cd "$(dirname "$0")"
IMAGE_NAME="${IMAGE_NAME:-netbird-dashboard}" IMAGE_NAME="${IMAGE_NAME:-netbird-dashboard}"
IMAGE_TAG="${IMAGE_TAG:-amd64}" IMAGE_TAG="${IMAGE_TAG:-amd64}"
DOCKERFILE="docker/Dockerfile"
CONTAINER_PORT=80
OUTPUT_SUFFIX=""
case "${ROOTLESS:-0}" in
0) ;;
1)
DOCKERFILE="docker/Dockerfile.rootless"
CONTAINER_PORT=8080
OUTPUT_SUFFIX="-rootless"
[[ "${IMAGE_TAG}" == *-rootless ]] || IMAGE_TAG="${IMAGE_TAG}-rootless"
;;
*)
echo "ERROR: ROOTLESS must be 0 or 1." >&2
exit 1
;;
esac
IMAGE="${IMAGE_NAME}:${IMAGE_TAG}" IMAGE="${IMAGE_NAME}:${IMAGE_TAG}"
OUTPUT="${OUTPUT:-${IMAGE_NAME}${OUTPUT_SUFFIX}.tar.gz}" OUTPUT="${OUTPUT:-${IMAGE_NAME}.tar.gz}"
# Build the static export (out/) unless told to reuse an existing one. Next 16 # Build the static export (out/) unless told to reuse an existing one. Next 16
# no longer runs ESLint during `next build`; types are checked by the build via # no longer runs ESLint during `next build`; types are checked by the build via
@@ -58,10 +39,17 @@ if [[ ! -d out ]]; then
exit 1 exit 1
fi fi
# The .dockerignore lives under docker/, but Docker reads it from the build
# context root. Stage it for the duration of the build so node_modules/.next
# don't get shipped into the build context.
cleanup() { rm -f .dockerignore; }
trap cleanup EXIT
cp docker/.dockerignore .dockerignore
echo "==> Building ${IMAGE} for linux/amd64" echo "==> Building ${IMAGE} for linux/amd64"
docker build \ docker build \
--platform linux/amd64 \ --platform linux/amd64 \
-f "${DOCKERFILE}" \ -f docker/Dockerfile \
-t "${IMAGE}" \ -t "${IMAGE}" \
. .
@@ -72,13 +60,13 @@ echo "==> Done: ${OUTPUT} ($(du -h "${OUTPUT}" | cut -f1))"
echo echo
echo "Copy and run on the server:" echo "Copy and run on the server:"
echo " scp ${OUTPUT} user@your-server:/tmp/" echo " scp ${OUTPUT} user@your-server:/tmp/"
echo " docker load < /tmp/${OUTPUT##*/}" echo " docker load < /tmp/${OUTPUT}"
echo echo
echo "Run (nginx serves on container port ${CONTAINER_PORT}). Set the runtime env vars the" echo "Run (nginx serves on container port 80). Set the runtime env vars the"
echo "dashboard templates at startup — for the Agent Network onboarding test," echo "dashboard templates at startup — for the Agent Network onboarding test,"
echo "flip NETBIRD_AGENT_NETWORK_ONLY=true:" echo "flip NETBIRD_AGENT_NETWORK_ONLY=true:"
echo " docker run -d --name ${IMAGE_NAME} --restart unless-stopped \\" echo " docker run -d --name ${IMAGE_NAME} --restart unless-stopped \\"
echo " -p 8080:${CONTAINER_PORT} \\" echo " -p 8080:80 \\"
echo " -e NETBIRD_MGMT_API_ENDPOINT=https://your-mgmt:443 \\" echo " -e NETBIRD_MGMT_API_ENDPOINT=https://your-mgmt:443 \\"
echo " -e AUTH_AUTHORITY=https://your-idp/realms/netbird \\" echo " -e AUTH_AUTHORITY=https://your-idp/realms/netbird \\"
echo " -e AUTH_CLIENT_ID=netbird-dashboard \\" echo " -e AUTH_CLIENT_ID=netbird-dashboard \\"
+1 -2
View File
@@ -24,6 +24,5 @@
"hubspotSignupFormId": "$NETBIRD_HUBSPOT_SIGNUP_FORM_ID", "hubspotSignupFormId": "$NETBIRD_HUBSPOT_SIGNUP_FORM_ID",
"hubspotOnboardingFormId": "$NETBIRD_HUBSPOT_ONBOARDING_FORM_ID", "hubspotOnboardingFormId": "$NETBIRD_HUBSPOT_ONBOARDING_FORM_ID",
"hubspotSurveyFormId": "$NETBIRD_HUBSPOT_SURVEY_FORM_ID", "hubspotSurveyFormId": "$NETBIRD_HUBSPOT_SURVEY_FORM_ID",
"analyticsExcludedEmails": "$NETBIRD_ANALYTICS_EXCLUDED_EMAILS", "analyticsExcludedEmails": "$NETBIRD_ANALYTICS_EXCLUDED_EMAILS"
"announcement": "$NETBIRD_ANNOUNCEMENT"
} }
File renamed without changes.
+1 -1
View File
@@ -1,4 +1,4 @@
FROM alpine:3.24 FROM alpine:3.14
RUN apk add --no-cache bash curl less ca-certificates git tzdata zip gettext \ RUN apk add --no-cache bash curl less ca-certificates git tzdata zip gettext \
nginx curl supervisor certbot-nginx && \ nginx curl supervisor certbot-nginx && \
-29
View File
@@ -1,29 +0,0 @@
FROM alpine:3.24
RUN apk add --no-cache bash ca-certificates gettext nginx tzdata && \
rm -rf /var/cache/apk/* && \
mkdir -p /tmp/nginx
STOPSIGNAL SIGINT
EXPOSE 8080
WORKDIR /usr/share/nginx/html
COPY docker/default.conf /etc/nginx/http.d/default.conf
COPY docker/nginx.conf /etc/nginx/nginx.conf
COPY docker/rootless.conf /etc/nginx/http.d/rootless.conf
COPY docker/init_react_envs.sh /usr/local/init_react_envs.sh
COPY out/ /usr/share/nginx/html/
RUN sed -i \
-e 's/listen 80 default_server;/listen 8080 default_server;/' \
-e 's/listen \[::\]:80 default_server;/listen [::]:8080 default_server;/' \
/etc/nginx/http.d/default.conf && \
sed -i '/^user nginx;$/d' /etc/nginx/nginx.conf && \
chmod +x /usr/local/init_react_envs.sh && \
chgrp -R 0 /etc/nginx/http.d /usr/share/nginx/html /tmp/nginx && \
chmod -R g=u /etc/nginx/http.d /usr/share/nginx/html /tmp/nginx
USER 10001:0
ENTRYPOINT ["/usr/local/init_react_envs.sh"]
CMD ["/usr/sbin/nginx", "-e", "/dev/stderr", "-g", "pid /tmp/nginx.pid;"]
+10 -46
View File
@@ -1,59 +1,23 @@
# NetBird Dashboard # NetBird Dashboard
NetBird Dashboard is NetBirds Management server UI. It allows users to signin, view setup keys and manage peers. This image is **not ready** for production use.
NetBird Dashboard is NetBird's management server UI.
## Tags ## Tags
```latest``` ```vX.X.X``` not available yet.
Each published image tag also has a rootless variant with the `-rootless` ```main``` built on every PR being merged to the repository
suffix. For example, `main` is also published as `main-rootless`.
## How to use this image ## How to use this image
HTTP run:
HTTP:
```shell ```shell
docker run -d --rm -p 80:80 netbirdio/dashboard:main docker run -d --rm -p 80:80 wiretrustee/dashboard:main
``` ```
Using SSL certificate from Let's Encrypt®:
Using an SSL certificate from Let's Encrypt:
```shell ```shell
docker run -d --rm -p 80:80 -p 443:443 \ docker run -d --rm -p 80:80 -p 443:443 \
-e LETSENCRYPT_DOMAIN=app.mydomain.com \ -e LETSENCRYPT_DOMAIN=app.mydomain.com \
-e LETSENCRYPT_EMAIL=hello@mydomain.com \ -e LETSENCRYPT_EMAIL=hello@mydomain.com \
netbirdio/dashboard:main netbirdio/dashboard:main
``` ```
> For SSL generation, you need to run this image in a server with proper public IP and a domain name pointing to it.
For certificate generation, the server needs a public IP and a domain name
pointing to it.
### Rootless and OpenShift
The rootless image listens on port `8080`, runs as UID `10001` by default, and
supports OpenShift's arbitrary UID when the container retains GID `0`:
```shell
docker run -d --rm \
--user 12345:0 \
-p 8080:8080 \
-e AUTH_AUTHORITY=https://idp.example.com \
-e AUTH_CLIENT_ID=netbird-dashboard \
-e AUTH_AUDIENCE=netbird-dashboard \
-e AUTH_SUPPORTED_SCOPES='openid profile email' \
-e USE_AUTH0=false \
-e NETBIRD_MGMT_API_ENDPOINT=https://api.example.com \
netbirdio/dashboard:main-rootless
```
The rootless image does not include Certbot or expose HTTPS. Terminate TLS at
the OpenShift Route, Ingress, or another reverse proxy.
## Environment variables ## Environment variables
* ```NGINX_SSL_PORT``` Changes the port that Nginx listens to. Defaults to ```443```
- `NGINX_SSL_PORT` changes the port that Nginx listens to. Defaults to `443`. * ```LETSENCRYPT_DOMAIN``` Enables Certbot`s client execution for the specified domain. Defaults to ```none```
- `LETSENCRYPT_DOMAIN` enables Certbot for the specified domain. Defaults to * ```LETSENCRYPT_EMAIL``` Email used in Certbot`s client execution to register the certificate request. Defaults to ```example@local```
`none`.
- `LETSENCRYPT_EMAIL` is used to register the certificate request. Defaults to
`example@local`.
These certificate variables apply only to the standard image.
+1 -9
View File
@@ -73,8 +73,6 @@ export NETBIRD_HUBSPOT_SIGNUP_FORM_ID=${NETBIRD_HUBSPOT_SIGNUP_FORM_ID}
export NETBIRD_HUBSPOT_ONBOARDING_FORM_ID=${NETBIRD_HUBSPOT_ONBOARDING_FORM_ID} export NETBIRD_HUBSPOT_ONBOARDING_FORM_ID=${NETBIRD_HUBSPOT_ONBOARDING_FORM_ID}
export NETBIRD_HUBSPOT_SURVEY_FORM_ID=${NETBIRD_HUBSPOT_SURVEY_FORM_ID} export NETBIRD_HUBSPOT_SURVEY_FORM_ID=${NETBIRD_HUBSPOT_SURVEY_FORM_ID}
export NETBIRD_ANALYTICS_EXCLUDED_EMAILS=${NETBIRD_ANALYTICS_EXCLUDED_EMAILS} export NETBIRD_ANALYTICS_EXCLUDED_EMAILS=${NETBIRD_ANALYTICS_EXCLUDED_EMAILS}
# A permanent banner for this deployment, shown to every user; empty for none.
export NETBIRD_ANNOUNCEMENT=${NETBIRD_ANNOUNCEMENT}
echo "NetBird latest version: ${NETBIRD_LATEST_VERSION}" echo "NetBird latest version: ${NETBIRD_LATEST_VERSION}"
@@ -165,7 +163,7 @@ sed -i "s|add_header Content-Security-Policy \"[^\"]*\" always;|$CSP_HEADER|g" /
} }
# replace ENVs in the config # replace ENVs in the config
ENV_STR="\$\$USE_AUTH0 \$\$AUTH_AUDIENCE \$\$AUTH_AUTHORITY \$\$AUTH_CLIENT_ID \$\$AUTH_CLIENT_SECRET \$\$AUTH_SUPPORTED_SCOPES \$\$NETBIRD_MGMT_API_ENDPOINT \$\$NETBIRD_MGMT_GRPC_API_ENDPOINT \$\$NETBIRD_HOTJAR_TRACK_ID \$\$NETBIRD_GOOGLE_ANALYTICS_ID \$\$NETBIRD_GOOGLE_TAG_MANAGER_ID \$\$AUTH_REDIRECT_URI \$\$AUTH_SILENT_REDIRECT_URI \$\$NETBIRD_TOKEN_SOURCE \$\$NETBIRD_DRAG_QUERY_PARAMS \$\$NETBIRD_AUTH_SERVICE_URL \$\$NETBIRD_WASM_PATH \$\$NETBIRD_LICENSED \$\$NETBIRD_CLOUD \$\$NETBIRD_AGENT_NETWORK_ONLY \$\$NETBIRD_AGENT_NETWORK_ENABLED \$\$NETBIRD_HUBSPOT_PORTAL_ID \$\$NETBIRD_HUBSPOT_SIGNUP_FORM_ID \$\$NETBIRD_HUBSPOT_ONBOARDING_FORM_ID \$\$NETBIRD_HUBSPOT_SURVEY_FORM_ID \$\$NETBIRD_ANALYTICS_EXCLUDED_EMAILS \$\$NETBIRD_ANNOUNCEMENT" ENV_STR="\$\$USE_AUTH0 \$\$AUTH_AUDIENCE \$\$AUTH_AUTHORITY \$\$AUTH_CLIENT_ID \$\$AUTH_CLIENT_SECRET \$\$AUTH_SUPPORTED_SCOPES \$\$NETBIRD_MGMT_API_ENDPOINT \$\$NETBIRD_MGMT_GRPC_API_ENDPOINT \$\$NETBIRD_HOTJAR_TRACK_ID \$\$NETBIRD_GOOGLE_ANALYTICS_ID \$\$NETBIRD_GOOGLE_TAG_MANAGER_ID \$\$AUTH_REDIRECT_URI \$\$AUTH_SILENT_REDIRECT_URI \$\$NETBIRD_TOKEN_SOURCE \$\$NETBIRD_DRAG_QUERY_PARAMS \$\$NETBIRD_AUTH_SERVICE_URL \$\$NETBIRD_WASM_PATH \$\$NETBIRD_LICENSED \$\$NETBIRD_CLOUD \$\$NETBIRD_AGENT_NETWORK_ONLY \$\$NETBIRD_AGENT_NETWORK_ENABLED \$\$NETBIRD_HUBSPOT_PORTAL_ID \$\$NETBIRD_HUBSPOT_SIGNUP_FORM_ID \$\$NETBIRD_HUBSPOT_ONBOARDING_FORM_ID \$\$NETBIRD_HUBSPOT_SURVEY_FORM_ID \$\$NETBIRD_ANALYTICS_EXCLUDED_EMAILS"
OIDC_TRUSTED_DOMAINS="/usr/share/nginx/html/OidcTrustedDomains.js" OIDC_TRUSTED_DOMAINS="/usr/share/nginx/html/OidcTrustedDomains.js"
envsubst "$ENV_STR" < "$OIDC_TRUSTED_DOMAINS".tmpl > "$OIDC_TRUSTED_DOMAINS" envsubst "$ENV_STR" < "$OIDC_TRUSTED_DOMAINS".tmpl > "$OIDC_TRUSTED_DOMAINS"
@@ -175,12 +173,6 @@ for f in $(grep -R -l AUTH_SUPPORTED_SCOPES /usr/share/nginx/html); do
rm "$f".copy rm "$f".copy
done done
# When a command is provided, initialization happens before the server starts.
# The rootless image uses this path so nginx never needs to be reloaded.
if (( $# > 0 )); then
exec "$@"
fi
# Reload nginx so the patched CSP header takes effect. # Reload nginx so the patched CSP header takes effect.
# supervisord starts nginx (priority 100) before this script (priority 201) and # supervisord starts nginx (priority 100) before this script (priority 201) and
# never reloads it, so without this nginx keeps serving the static default.conf # never reloads it, so without this nginx keeps serving the static default.conf
-7
View File
@@ -1,7 +0,0 @@
# nginx includes this file inside the http context.
# All runtime state stays in a directory writable by the arbitrary UID used by OpenShift.
client_body_temp_path /tmp/nginx/client_temp;
proxy_temp_path /tmp/nginx/proxy_temp;
fastcgi_temp_path /tmp/nginx/fastcgi_temp;
uwsgi_temp_path /tmp/nginx/uwsgi_temp;
scgi_temp_path /tmp/nginx/scgi_temp;
-1
View File
@@ -218,7 +218,6 @@ await visitByNavigation(page, "Policies"); // Click child
| Notifications | `settings-notifications-email.spec.ts`, `settings-notifications-slack.spec.ts`, `settings-notifications-webhook.spec.ts` | `@notifications` | | Notifications | `settings-notifications-email.spec.ts`, `settings-notifications-slack.spec.ts`, `settings-notifications-webhook.spec.ts` | `@notifications` |
| Team | `team-users.spec.ts`, `team-service-users.spec.ts`, `team-users-approval-and-billing.spec.ts` | `@team` | | Team | `team-users.spec.ts`, `team-service-users.spec.ts`, `team-users-approval-and-billing.spec.ts` | `@team` |
| Setup Keys | `setup-keys.spec.ts` | `@setup-keys` | | Setup Keys | `setup-keys.spec.ts` | `@setup-keys` |
| Control Center | `control-center-*.spec.ts` (`-draft`, `-draft-connections`, `-draft-matrix`, `-draft-extras`, `-draft-install`, `-draft-remove`, `-draft-shortcuts`, `-deploy`, `-drilldown`, `-live`, `-peers`) | `@control-center` |
## Debugging ## Debugging
+1 -4
View File
@@ -1,7 +1,6 @@
# Ignore zitadel environment # Ignore zitadel environment
.env .env
Caddyfile Caddyfile
agentgateway-stub.Caddyfile
management.json management.json
turnserver.conf turnserver.conf
zitadel.env zitadel.env
@@ -10,6 +9,4 @@ proxy-no-ports.env
proxy-certs/ proxy-certs/
proxy-certs-no-ports/ proxy-certs-no-ports/
docker-compose.yml docker-compose.yml
/machinekey /machinekey
geo-cache/
geo-dump/
+1 -1
View File
@@ -19,7 +19,7 @@ check_docker_compose() {
DOCKER_COMPOSE_COMMAND=$(check_docker_compose) DOCKER_COMPOSE_COMMAND=$(check_docker_compose)
$DOCKER_COMPOSE_COMMAND down --volumes $DOCKER_COMPOSE_COMMAND down --volumes
rm -f docker-compose.yml Caddyfile agentgateway-stub.Caddyfile zitadel.env .env dashboard.env machinekey/zitadel-admin-sa.token turnserver.conf management.json proxy.env proxy-no-ports.env rm -f docker-compose.yml Caddyfile zitadel.env .env dashboard.env machinekey/zitadel-admin-sa.token turnserver.conf management.json proxy.env proxy-no-ports.env
rm -rf proxy-certs proxy-certs-no-ports rm -rf proxy-certs proxy-certs-no-ports
rm -f ../../.test-config.json ../playwright.env.json rm -f ../../.test-config.json ../playwright.env.json
rm -f ../fixtures/auth/owner.json ../fixtures/auth/user.json rm -f ../fixtures/auth/owner.json ../fixtures/auth/user.json
+3 -97
View File
@@ -61,13 +61,7 @@ wait_proxy_cluster() {
SERVICE_NAME=${1:-reverse-proxy} SERVICE_NAME=${1:-reverse-proxy}
echo -n "Waiting for $SERVICE_NAME to register with management " echo -n "Waiting for $SERVICE_NAME to register with management "
set +e set +e
# 480s: management downloads its geolocation databases on first boot local attempts=60
# (the proxy specs need /locations/countries, so it cannot be disabled
# there), and a slow pkgs.netbird.io can stall that well past the old
# 120s budget. The proxies skip their download entirely via
# NB_PROXY_DISABLE_GEOLOCATION. The loop exits as soon as the sync
# lands, so healthy runs pay nothing.
local attempts=240
local i local i
for ((i = 1; i <= attempts; i++)); do for ((i = 1; i <= attempts; i++)); do
if $DOCKER_COMPOSE_COMMAND logs "$SERVICE_NAME" 2>&1 | grep -q "Initial mapping sync complete"; then if $DOCKER_COMPOSE_COMMAND logs "$SERVICE_NAME" 2>&1 | grep -q "Initial mapping sync complete"; then
@@ -532,7 +526,7 @@ initEnvironment() {
echo "Generated files already exist, if you want to reinitialize the environment, please remove them first." echo "Generated files already exist, if you want to reinitialize the environment, please remove them first."
echo "You can use the following commands:" echo "You can use the following commands:"
echo " $DOCKER_COMPOSE_COMMAND down --volumes # to remove all containers and volumes" echo " $DOCKER_COMPOSE_COMMAND down --volumes # to remove all containers and volumes"
echo " rm -f docker-compose.yml Caddyfile agentgateway-stub.Caddyfile zitadel.env dashboard.env machinekey/zitadel-admin-sa.token turnserver.conf management.json proxy.env proxy-no-ports.env && rm -rf proxy-certs proxy-certs-no-ports" echo " rm -f docker-compose.yml Caddyfile zitadel.env dashboard.env machinekey/zitadel-admin-sa.token turnserver.conf management.json proxy.env proxy-no-ports.env && rm -rf proxy-certs proxy-certs-no-ports"
echo "Be aware that this will remove all data from the database, and you will have to reconfigure the dashboard." echo "Be aware that this will remove all data from the database, and you will have to reconfigure the dashboard."
exit 1 exit 1
fi fi
@@ -540,7 +534,6 @@ initEnvironment() {
echo Rendering initial files... echo Rendering initial files...
renderDockerCompose > docker-compose.yml renderDockerCompose > docker-compose.yml
renderCaddyfile > Caddyfile renderCaddyfile > Caddyfile
renderAgentGatewayStub > agentgateway-stub.Caddyfile
renderZitadelEnv > zitadel.env renderZitadelEnv > zitadel.env
echo "" > turnserver.conf echo "" > turnserver.conf
echo "" > management.json echo "" > management.json
@@ -596,8 +589,6 @@ initEnvironment() {
echo -e "\nCreating proxy access tokens...\n" echo -e "\nCreating proxy access tokens...\n"
init_proxy_tokens init_proxy_tokens
exportGeoDatabases
echo -e "\nStarting reverse proxy services...\n" echo -e "\nStarting reverse proxy services...\n"
$DOCKER_COMPOSE_COMMAND up -d reverse-proxy reverse-proxy-no-ports $DOCKER_COMPOSE_COMMAND up -d reverse-proxy reverse-proxy-no-ports
@@ -613,30 +604,6 @@ initEnvironment() {
echo "Password: $ZITADEL_ADMIN_PASSWORD" | tee -a .env echo "Password: $ZITADEL_ADMIN_PASSWORD" | tee -a .env
} }
# Saving an Agent Network provider makes management call the upstream and
# refuses the save unless it answers a model listing, so a provider spec needs
# a reachable OpenAI-compatible endpoint. agentgateway is self-hosted and has
# no public one; this stub stands in for it, inside the compose network where
# management can reach it and without depending on a vendor being up.
renderAgentGatewayStub() {
cat <<'EOF'
{
admin off
auto_https off
}
:8088 {
handle /v1/models {
header Content-Type application/json
respond `{"object":"list","data":[{"id":"agentgateway-e2e-model","object":"model","owned_by":"netbird-e2e"}]}` 200
}
handle {
respond 404
}
}
EOF
}
renderCaddyfile() { renderCaddyfile() {
cat <<EOF cat <<EOF
{ {
@@ -793,41 +760,7 @@ ZITADEL_FIRSTINSTANCE_ORG_MACHINE_PAT_EXPIRATIONDATE=$ZIDATE_TOKEN_EXPIRATION_DA
EOF EOF
} }
# Copies management's geolocation databases out of its data volume into
# ./geo-cache so CI can cache them across runs. Management has finished
# loading them by the time its API serves requests. A no-op when the
# cache already holds both files, and never fatal: a failed export just
# means the next run downloads again.
exportGeoDatabases() {
mkdir -p geo-cache
if compgen -G "geo-cache/GeoLite2-City_*.mmdb" > /dev/null \
&& compgen -G "geo-cache/geonames_*.db" > /dev/null; then
return
fi
echo -e "\nExporting geolocation databases for caching...\n"
rm -rf geo-dump
if $DOCKER_COMPOSE_COMMAND cp management:/var/lib/netbird ./geo-dump; then
cp geo-dump/GeoLite2-City_*.mmdb geo-dump/geonames_*.db geo-cache/ 2>/dev/null \
|| echo "WARN: no geolocation databases found in management data dir"
rm -rf geo-dump
else
echo "WARN: could not copy management data dir; geolocation databases not cached"
fi
}
renderDockerCompose() { renderDockerCompose() {
# Cached geolocation databases (restored by CI into ./geo-cache) are
# mounted into management's data dir so it skips the slow first-boot
# download from pkgs.netbird.io. With no cache the mounts are omitted
# and management downloads as before.
local geo_mounts=""
local f
for f in geo-cache/GeoLite2-City_*.mmdb geo-cache/geonames_*.db; do
[ -f "$f" ] || continue
geo_mounts+="
- ./${f}:/var/lib/netbird/$(basename "$f")"
done
cat <<EOF cat <<EOF
version: "3.4" version: "3.4"
services: services:
@@ -843,15 +776,6 @@ services:
volumes: volumes:
- netbird_caddy_data:/data - netbird_caddy_data:/data
- ./Caddyfile:/etc/caddy/Caddyfile - ./Caddyfile:/etc/caddy/Caddyfile
# Stand-in upstream for the Agent Network provider specs: answers the model
# listing management probes before it accepts a provider save. Reachable
# only from inside the compose network, at http://agentgateway-stub:8088.
agentgateway-stub:
image: caddy
restart: unless-stopped
networks: [ netbird ]
volumes:
- ./agentgateway-stub.Caddyfile:/etc/caddy/Caddyfile
# Management # Management
management: management:
image: ghcr.io/netbirdio/management-cloud:${MANAGEMENT_IMAGE_TAG} image: ghcr.io/netbirdio/management-cloud:${MANAGEMENT_IMAGE_TAG}
@@ -870,7 +794,7 @@ services:
- NB_TRAFFIC_FLOW_INTERVAL=20s - NB_TRAFFIC_FLOW_INTERVAL=20s
- NB_SINGLE_INSTANCE_MODE=true - NB_SINGLE_INSTANCE_MODE=true
volumes: volumes:
- netbird_management:/var/lib/netbird${geo_mounts} - netbird_management:/var/lib/netbird
- ./management.json:/etc/netbird/management.json - ./management.json:/etc/netbird/management.json
command: [ command: [
"--port", "80", "--port", "80",
@@ -921,21 +845,6 @@ services:
networks: [netbird] networks: [netbird]
env_file: env_file:
- ./proxy.env - ./proxy.env
environment:
# No spec exercises country-based enforcement, and skipping the
# GeoLite2 download removes a startup stall of up to 2 minutes
# when pkgs.netbird.io is slow.
- NB_PROXY_DISABLE_GEOLOCATION=true
# Declare the private capability so this cluster can host the agent
# network endpoint. That endpoint is a private service — reachable only
# from connected peers — and management refuses to bootstrap it onto a
# cluster without a private-capable proxy; the connect-provider wizard
# reads the same flag (supports_private) to pick its cluster. Without
# it no cluster qualifies and the wizard stops at the provider tab,
# which strands every spec that walks through it. The no-ports proxy
# below stays centralised, so the pick has to skip past a cluster that
# does not qualify.
- NB_PROXY_PRIVATE=true
volumes: volumes:
- ./proxy-certs:/certs:ro - ./proxy-certs:/certs:ro
command: [ command: [
@@ -957,9 +866,6 @@ services:
networks: [netbird] networks: [netbird]
env_file: env_file:
- ./proxy-no-ports.env - ./proxy-no-ports.env
environment:
# See the primary proxy: no spec needs geo enforcement.
- NB_PROXY_DISABLE_GEOLOCATION=true
volumes: volumes:
# Distinct cert dir so this proxy has a distinct identity from the # Distinct cert dir so this proxy has a distinct identity from the
# primary proxy; a shared cert makes both register under the same # primary proxy; a shared cert makes both register under the same
+82 -269
View File
File diff suppressed because it is too large. Load diff
+2 -10
View File
@@ -33,7 +33,7 @@ export async function loginToApp(
// Use locators that match either outcome — Playwright auto-waits. // Use locators that match either outcome — Playwright auto-waits.
const appReady = page.getByTestId("left-navigation-item").first(); const appReady = page.getByTestId("left-navigation-item").first();
const setupModal = page.getByTestId("setup-netbird-modal"); const setupModal = page.getByTestId("setup-netbird-modal");
const approvalPending = page.getByTestId("pending-approval"); const approvalPending = page.getByText("User Approval Pending");
const onboarding = page.getByText("Add new device to your network"); const onboarding = page.getByText("Add new device to your network");
const selectAccount = page.getByText("Select account"); const selectAccount = page.getByText("Select account");
const loginInput = page.locator("input[id=loginName]"); const loginInput = page.locator("input[id=loginName]");
@@ -117,15 +117,7 @@ export async function loginToApp(
* Use this instead of page.goto() for in-app navigation after loginToApp(). * Use this instead of page.goto() for in-app navigation after loginToApp().
*/ */
export async function navigateTo(page: Page, path: string) { export async function navigateTo(page: Page, path: string) {
// A client-side route update still in flight from the previous test (the await page.goto(path, { waitUntil: "domcontentloaded" });
// dashboard page is shared per worker) aborts the navigation. Retry once —
// by then the app-driven navigation has landed.
try {
await page.goto(path, { waitUntil: "domcontentloaded" });
} catch (e) {
if (!String(e).includes("ERR_ABORTED")) throw e;
await page.goto(path, { waitUntil: "domcontentloaded" });
}
const modal = page.getByTestId("setup-netbird-modal"); const modal = page.getByTestId("setup-netbird-modal");
try { try {
await modal.waitFor({ state: "visible", timeout: 3_000 }); await modal.waitFor({ state: "visible", timeout: 3_000 });
Loaded 20 of 385 files, more files were not shown because too many files have changed in this diff. Show more