From 00d8dd75d6f13175ee4d6a64c5889d95217bf1e5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 15 Aug 2025 00:38:39 +0000 Subject: [PATCH 01/27] Update docker.io/valkey/valkey Docker tag to v9 --- docker-compose.test.yml | 2 +- docker-compose.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.test.yml b/docker-compose.test.yml index bbc139c..17ba358 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -49,7 +49,7 @@ services: retries: 5 redis: &redis - image: docker.io/valkey/valkey:8.1-alpine + image: docker.io/valkey/valkey:9.0-alpine command: - sh - -c # this is to evaluate the $REDIS_PASSWORD from the env diff --git a/docker-compose.yml b/docker-compose.yml index 3ba2fd4..da03da6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -59,7 +59,7 @@ services: # redis redis: - image: docker.io/valkey/valkey:8.1-alpine + image: docker.io/valkey/valkey:9.0-alpine command: - sh - -c # this is to evaluate the $REDIS_PASSWORD from the env @@ -74,7 +74,7 @@ services: volumes: - netbox-redis-data:/data redis-cache: - image: docker.io/valkey/valkey:8.1-alpine + image: docker.io/valkey/valkey:9.0-alpine command: - sh - -c # this is to evaluate the $REDIS_PASSWORD from the env From 88a2e489230518ba01541525c2b007a5b4179419 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 02:25:40 +0000 Subject: [PATCH 02/27] Update docker.io/postgres Docker tag to v18 --- docker-compose.test.yml | 2 +- docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 9388022..c75b13f 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -30,7 +30,7 @@ services: interval: 15s postgres: - image: docker.io/postgres:17-alpine + image: docker.io/postgres:18-alpine env_file: env/postgres.env healthcheck: test: pg_isready -q -t 2 -d $$POSTGRES_DB -U $$POSTGRES_USER ## $$ because of docker-compose diff --git a/docker-compose.yml b/docker-compose.yml index a5e78d4..907a76f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,7 +34,7 @@ services: # postgres postgres: - image: docker.io/postgres:17-alpine + image: docker.io/postgres:18-alpine healthcheck: test: pg_isready -q -t 2 -d $$POSTGRES_DB -U $$POSTGRES_USER start_period: 20s From 22688dedd44a45ecb136d304734652f609a6ecb0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 20:51:29 +0000 Subject: [PATCH 03/27] Update dependency dulwich to v0.24.10 --- requirements-container.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-container.txt b/requirements-container.txt index a42196f..22bdc09 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -1,5 +1,5 @@ django-auth-ldap==5.2.0 -dulwich==0.24.8 +dulwich==0.24.10 python3-saml==1.16.0 --no-binary lxml --no-binary xmlsec From f474cc585c609355facd72fb4f522d28f75be87f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 13:43:40 +0000 Subject: [PATCH 04/27] Update dependency sentry-sdk to v2.44.0 --- requirements-container.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-container.txt b/requirements-container.txt index 22bdc09..65dc789 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -3,4 +3,4 @@ dulwich==0.24.10 python3-saml==1.16.0 --no-binary lxml --no-binary xmlsec -sentry-sdk[django]==2.43.0 +sentry-sdk[django]==2.44.0 From 07fcb205b4f6bd00d98c39fe84629ca7f1f76325 Mon Sep 17 00:00:00 2001 From: Tobias Genannt Date: Wed, 12 Nov 2025 13:57:50 +0100 Subject: [PATCH 05/27] Switch server to Granian (#1561) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Switch server to Granian Because the development of Nginx Unit has been stopped, switch the server to Granian which still allows to serve the Python application and the static files from one server. * Addressed review comments * Update docker/launch-netbox.sh Co-authored-by: Christian Mäder * Update docker/launch-netbox.sh Co-authored-by: Christian Mäder --------- Co-authored-by: Christian Mäder --- .github/workflows/push.yml | 1 + Dockerfile | 24 ++++----- docker-compose.override.yml.example | 4 -- docker-compose.test.yml | 2 +- docker-compose.yml | 2 +- docker/granian.py | 13 +++++ docker/launch-netbox.sh | 73 +++++++------------------ docker/nginx-unit.json | 82 ----------------------------- docker/unit.list | 1 - env/netbox.env | 2 + requirements-container.txt | 1 + 11 files changed, 47 insertions(+), 158 deletions(-) create mode 100644 docker/granian.py delete mode 100644 docker/nginx-unit.json delete mode 100644 docker/unit.list diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index f4b6305..fa6bab1 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -42,6 +42,7 @@ jobs: VALIDATE_GITHUB_ACTIONS_ZIZMOR: false VALIDATE_GITLEAKS: false VALIDATE_JSCPD: false + VALIDATE_PYTHON_PYLINT: false VALIDATE_TRIVY: false FILTER_REGEX_EXCLUDE: (.*/)?(LICENSE|configuration/.*) EDITORCONFIG_FILE_NAME: .editorconfig-checker.json diff --git a/Dockerfile b/Dockerfile index b398e9c..753200a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,7 +27,7 @@ ARG NETBOX_PATH COPY ${NETBOX_PATH}/requirements.txt requirements-container.txt / ENV VIRTUAL_ENV=/opt/netbox/venv RUN \ - # Gunicorn is not needed because we use Nginx Unit + # Gunicorn is not needed because we use Granian sed -i -e '/gunicorn/d' /requirements.txt && \ # We need 'social-auth-core[all]' in the Docker image. But if we put it in our own requirements-container.txt # we have potential version conflicts and the build will fail. @@ -46,8 +46,6 @@ RUN \ ARG FROM FROM ${FROM} AS main -COPY docker/unit.list /etc/apt/sources.list.d/unit.list -ADD --chmod=444 --chown=0:0 https://unit.nginx.org/keys/nginx-keyring.gpg /usr/share/keyrings/nginx-keyring.gpg RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get update -qq \ && apt-get upgrade \ @@ -64,8 +62,6 @@ RUN export DEBIAN_FRONTEND=noninteractive \ openssl \ python3 \ tini \ - unit-python3.12=1.34.2-1~noble \ - unit=1.34.2-1~noble \ && rm -rf /var/lib/apt/lists/* # Copy the modified 'requirements*.txt' files, to have the files actually used during installation @@ -81,21 +77,21 @@ COPY docker/ldap_config.docker.py /opt/netbox/netbox/netbox/ldap_config.py COPY docker/docker-entrypoint.sh /opt/netbox/docker-entrypoint.sh COPY docker/launch-netbox.sh /opt/netbox/launch-netbox.sh COPY configuration/ /etc/netbox/config/ -COPY docker/nginx-unit.json /etc/unit/ +COPY docker/granian.py /opt/netbox/netbox/netbox/granian.py COPY VERSION /opt/netbox/VERSION WORKDIR /opt/netbox/netbox # Must set permissions for '/opt/netbox/netbox/media' directory # to g+w so that pictures can be uploaded to netbox. -RUN mkdir -p static media /opt/unit/state/ /opt/unit/tmp/ \ - && chown -R unit:root /opt/unit/ media reports scripts \ - && chmod -R g+w /opt/unit/ media reports scripts \ - && cd /opt/netbox/ && SECRET_KEY="dummyKeyWithMinimumLength-------------------------" /opt/netbox/venv/bin/python -m mkdocs build \ - --config-file /opt/netbox/mkdocs.yml --site-dir /opt/netbox/netbox/project-static/docs/ \ - && DEBUG="true" SECRET_KEY="dummyKeyWithMinimumLength-------------------------" /opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py collectstatic --no-input \ - && mkdir /opt/netbox/netbox/local \ - && echo "build: Docker-$(cat /opt/netbox/VERSION)" > /opt/netbox/netbox/local/release.yaml +RUN useradd --home-dir /opt/netbox/ --no-create-home --no-user-group --system --shell /bin/false --uid 999 --gid 0 netbox \ + && mkdir -p static media local \ + && chown -R netbox:root media reports scripts \ + && chmod -R g+w media reports scripts \ + && cd /opt/netbox/ && SECRET_KEY="dummyKeyWithMinimumLength-------------------------" /opt/netbox/venv/bin/python -m mkdocs build \ + --config-file /opt/netbox/mkdocs.yml --site-dir /opt/netbox/netbox/project-static/docs/ \ + && DEBUG="true" SECRET_KEY="dummyKeyWithMinimumLength-------------------------" /opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py collectstatic --no-input \ + && echo "build: Docker-$(cat /opt/netbox/VERSION)" > /opt/netbox/netbox/local/release.yaml ENV LANG=C.utf8 PATH=/opt/netbox/venv/bin:$PATH VIRTUAL_ENV=/opt/netbox/venv UV_NO_CACHE=1 ENTRYPOINT [ "/usr/bin/tini", "--" ] diff --git a/docker-compose.override.yml.example b/docker-compose.override.yml.example index d7ef961..1394814 100644 --- a/docker-compose.override.yml.example +++ b/docker-compose.override.yml.example @@ -2,9 +2,6 @@ services: netbox: ports: - "8000:8080" - # If you want the Nginx unit status page visible from the - # outside of the container add the following port mapping: - # - "8001:8081" # healthcheck: # Time for which the health check can fail after the container is started. # This depends mostly on the performance of your database. On the first start, @@ -19,4 +16,3 @@ services: # SUPERUSER_EMAIL: "" # SUPERUSER_NAME: "" # SUPERUSER_PASSWORD: "" - diff --git a/docker-compose.test.yml b/docker-compose.test.yml index d5acd95..8e22aa6 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -9,7 +9,7 @@ services: redis-cache: condition: service_healthy env_file: env/netbox.env - user: "unit:root" + user: "netbox:root" volumes: - ./test-configuration/test_config.py:/etc/netbox/config/test_config.py:z,ro healthcheck: diff --git a/docker-compose.yml b/docker-compose.yml index 2049c33..86586c0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: - redis - redis-cache env_file: env/netbox.env - user: "unit:root" + user: "netbox:root" healthcheck: test: curl -f http://localhost:8080/login/ || exit 1 start_period: 90s diff --git a/docker/granian.py b/docker/granian.py new file mode 100644 index 0000000..651b7da --- /dev/null +++ b/docker/granian.py @@ -0,0 +1,13 @@ +from granian.utils.proxies import wrap_wsgi_with_proxy_headers +from netbox.wsgi import application + +application = wrap_wsgi_with_proxy_headers( + application, + trusted_hosts=[ + "10.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16", + "fc00::/7", + "fe80::/10", + ], +) diff --git a/docker/launch-netbox.sh b/docker/launch-netbox.sh index 11da1de..a61e076 100755 --- a/docker/launch-netbox.sh +++ b/docker/launch-netbox.sh @@ -1,57 +1,20 @@ #!/bin/bash -UNIT_CONFIG="${UNIT_CONFIG-/etc/unit/nginx-unit.json}" -# Also used in "nginx-unit.json" -UNIT_SOCKET="/opt/unit/unit.sock" - -load_configuration() { - MAX_WAIT=10 - WAIT_COUNT=0 - while [ ! -S $UNIT_SOCKET ]; do - if [ $WAIT_COUNT -ge $MAX_WAIT ]; then - echo "⚠️ No control socket found; configuration will not be loaded." - return 1 - fi - - WAIT_COUNT=$((WAIT_COUNT + 1)) - echo "⏳ Waiting for control socket to be created... (${WAIT_COUNT}/${MAX_WAIT})" - - sleep 1 - done - - # even when the control socket exists, it does not mean unit has finished initialisation - # this curl call will get a reply once unit is fully launched - curl --silent --output /dev/null --request GET --unix-socket $UNIT_SOCKET http://localhost/ - - echo "⚙️ Applying configuration from $UNIT_CONFIG" - - RESP_CODE=$( - curl \ - --silent \ - --output /dev/null \ - --write-out '%{http_code}' \ - --request PUT \ - --data-binary "@${UNIT_CONFIG}" \ - --unix-socket $UNIT_SOCKET \ - http://localhost/config - ) - if [ "$RESP_CODE" != "200" ]; then - echo "⚠️ Could not load Unit configuration" - kill "$(cat /opt/unit/unit.pid)" - return 1 - fi - - echo "✅ Unit configuration loaded successfully" -} - -load_configuration & - -exec unitd \ - --no-daemon \ - --control unix:$UNIT_SOCKET \ - --pid /opt/unit/unit.pid \ - --log /dev/stdout \ - --statedir /opt/unit/state/ \ - --tmpdir /opt/unit/tmp/ \ - --user unit \ - --group root +exec granian \ + --host "::" \ + --port "8080" \ + --interface "wsgi" \ + --no-ws \ + --workers "${GRANIAN_WORKERS:-4}" \ + --respawn-failed-workers \ + --backpressure "${GRANIAN_BACKPRESSURE:-${GRANIAN_WORKERS:-4}}" \ + --loop "uvloop" \ + --log \ + --log-level "info" \ + --access-log \ + --working-dir "/opt/netbox/netbox/" \ + --static-path-route "/static" \ + --static-path-mount "/opt/netbox/netbox/static/" \ + --pid-file "/tmp/granian.pid" \ + "${GRANIAN_EXTRA_ARGS[@]}" \ + "netbox.granian:application" diff --git a/docker/nginx-unit.json b/docker/nginx-unit.json deleted file mode 100644 index 95fa1ef..0000000 --- a/docker/nginx-unit.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "listeners": { - "0.0.0.0:8080": { - "pass": "routes/main", - "forwarded": { - "client_ip": "X-Forwarded-For", - "protocol": "X-Forwarded-Proto", - "source": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] - } - }, - "0.0.0.0:8081": { - "pass": "routes/status", - "forwarded": { - "client_ip": "X-Forwarded-For", - "protocol": "X-Forwarded-Proto", - "source": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] - } - }, - "[::]:8080": { - "pass": "routes/main", - "forwarded": { - "client_ip": "X-Forwarded-For", - "protocol": "X-Forwarded-Proto", - "source": ["fc00::/7", "fe80::/10"] - } - }, - "[::]:8081": { - "pass": "routes/status", - "forwarded": { - "client_ip": "X-Forwarded-For", - "protocol": "X-Forwarded-Proto", - "source": ["fc00::/7", "fe80::/10"] - } - } - }, - "routes": { - "main": [ - { - "match": { - "uri": "/static/*" - }, - "action": { - "share": "/opt/netbox/netbox${uri}" - } - }, - { - "action": { - "pass": "applications/netbox" - } - } - ], - "status": [ - { - "match": { - "uri": "/status/*" - }, - "action": { - "proxy": "http://unix:/opt/unit/unit.sock" - } - } - ] - }, - "applications": { - "netbox": { - "type": "python 3", - "path": "/opt/netbox/netbox/", - "module": "netbox.wsgi", - "home": "/opt/netbox/venv", - "processes": { - "max": 4, - "spare": 1, - "idle_timeout": 120 - } - } - }, - "access_log": "/dev/stdout", - "settings": { - "http": { - "max_body_size": 104857600 - } - } -} diff --git a/docker/unit.list b/docker/unit.list deleted file mode 100644 index 6193723..0000000 --- a/docker/unit.list +++ /dev/null @@ -1 +0,0 @@ -deb [signed-by=/usr/share/keyrings/nginx-keyring.gpg] http://packages.nginx.org/unit/ubuntu/ noble unit diff --git a/env/netbox.env b/env/netbox.env index 52fca3b..88c8705 100644 --- a/env/netbox.env +++ b/env/netbox.env @@ -15,6 +15,8 @@ EMAIL_USERNAME=netbox # EMAIL_USE_SSL and EMAIL_USE_TLS are mutually exclusive, i.e. they can't both be `true`! EMAIL_USE_SSL=false EMAIL_USE_TLS=false +GRANIAN_BACKPRESSURE=4 +GRANIAN_WORKERS=4 GRAPHQL_ENABLED=true MEDIA_ROOT=/opt/netbox/netbox/media METRICS_ENABLED=false diff --git a/requirements-container.txt b/requirements-container.txt index 65dc789..74b4b1e 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -1,5 +1,6 @@ django-auth-ldap==5.2.0 dulwich==0.24.10 +granian[uvloop]==2.5.7 python3-saml==1.16.0 --no-binary lxml --no-binary xmlsec From db027b14a309b600ee633826d5d782bbb6139195 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 16 Nov 2025 17:56:30 +0000 Subject: [PATCH 06/27] Update dependency granian to v2.6.0 --- requirements-container.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-container.txt b/requirements-container.txt index 74b4b1e..5e50235 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -1,6 +1,6 @@ django-auth-ldap==5.2.0 dulwich==0.24.10 -granian[uvloop]==2.5.7 +granian[uvloop]==2.6.0 python3-saml==1.16.0 --no-binary lxml --no-binary xmlsec From 74b0a41f3ed55c44dccf6452f48ddfc0d0a9b454 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 22:51:24 +0000 Subject: [PATCH 07/27] Update dependency sentry-sdk to v2.45.0 --- requirements-container.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-container.txt b/requirements-container.txt index 5e50235..cd5bf73 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -4,4 +4,4 @@ granian[uvloop]==2.6.0 python3-saml==1.16.0 --no-binary lxml --no-binary xmlsec -sentry-sdk[django]==2.44.0 +sentry-sdk[django]==2.45.0 From 0f2fb47d9c61283a2ba42d069b8bcbe4064b4314 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 18:38:17 +0000 Subject: [PATCH 08/27] Update actions/checkout action to v6 --- .github/workflows/push.yml | 4 ++-- .github/workflows/release.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index fa6bab1..4eb4efd 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -23,7 +23,7 @@ jobs: packages: read statuses: write steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: # Full git history is needed to get a proper # list of changed files within `super-linter` @@ -74,7 +74,7 @@ jobs: steps: - id: git-checkout name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - id: buildx-setup name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2d83729..9314b8f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,7 +32,7 @@ jobs: steps: - id: source-checkout name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ matrix.build.branch }} - id: set-netbox-docker-version From 10031c29902bb56b4abb63238f3f88253362c719 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 13:50:32 +0000 Subject: [PATCH 09/27] Update dependency sentry-sdk to v2.46.0 --- requirements-container.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-container.txt b/requirements-container.txt index cd5bf73..8725c57 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -4,4 +4,4 @@ granian[uvloop]==2.6.0 python3-saml==1.16.0 --no-binary lxml --no-binary xmlsec -sentry-sdk[django]==2.45.0 +sentry-sdk[django]==2.46.0 From 6967b2ca34353383283b6afddd530ed2e94cf585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Bouynot?= Date: Mon, 1 Dec 2025 14:57:07 +0100 Subject: [PATCH 10/27] feat: bump the version to 3.4.2 so it match a newer docker image --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 0af361c..c6df938 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: netbox: &netbox - image: docker.io/netboxcommunity/netbox:${VERSION-v4.4-3.4.1} + image: docker.io/netboxcommunity/netbox:${VERSION-v4.4-3.4.2} depends_on: - postgres - redis From 9a68d6fd7015df922ebee62646cd8d8cd401a746 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 19:34:23 +0000 Subject: [PATCH 11/27] Update dependency sentry-sdk to v2.47.0 --- requirements-container.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-container.txt b/requirements-container.txt index 8725c57..65a5f82 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -4,4 +4,4 @@ granian[uvloop]==2.6.0 python3-saml==1.16.0 --no-binary lxml --no-binary xmlsec -sentry-sdk[django]==2.46.0 +sentry-sdk[django]==2.47.0 From ac419312ae13b239b082d03271958ca8b7b03884 Mon Sep 17 00:00:00 2001 From: Nahuel Cassinari Date: Tue, 9 Dec 2025 15:27:25 +0100 Subject: [PATCH 12/27] Update STORAGES example --- configuration/extra.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/configuration/extra.py b/configuration/extra.py index 8bd1337..3a10ea2 100644 --- a/configuration/extra.py +++ b/configuration/extra.py @@ -33,13 +33,20 @@ ## By default uploaded media is stored on the local filesystem. Using Django-storages is also supported. Provide the -## class path of the storage driver in STORAGE_BACKEND and any configuration options in STORAGE_CONFIG. For example: -# STORAGE_BACKEND = 'storages.backends.s3boto3.S3Boto3Storage' -# STORAGE_CONFIG = { -# 'AWS_ACCESS_KEY_ID': 'Key ID', -# 'AWS_SECRET_ACCESS_KEY': 'Secret', -# 'AWS_STORAGE_BUCKET_NAME': 'netbox', -# 'AWS_S3_REGION_NAME': 'eu-west-1', +## class path of the storage driver and any configuration options in STORAGES. For example: +# STORAGES = { +# 'default': { +# 'BACKEND': 'storages.backends.s3boto3.S3Boto3Storage', +# 'OPTIONS': { +# 'access_key': 'Key ID', +# 'secret_key': 'Secret', +# 'bucket_name': 'netbox', +# 'region_name': 'us-west-1', +# } +# }, +# 'staticfiles': { +# 'BACKEND': 'django.contrib.staticfiles.storage.StaticFilesStorage', +# } # } From 8387b4d0f6df6b0f79c4017b343b6c4777bcade9 Mon Sep 17 00:00:00 2001 From: Skye Fugate Date: Wed, 10 Dec 2025 00:51:30 -0600 Subject: [PATCH 13/27] NetBox-Docker Okta & Google SSO Environment Additions (#1475) * feat: add SSO environment variable support for OKTA and Google OAuth2 Add native support for SSO configuration through environment variables and Docker secrets, eliminating the need to modify configuration.py for common SSO providers. Changes: - Add OKTA OpenID Connect configuration variables: - SOCIAL_AUTH_OKTA_OPENIDCONNECT_KEY (env var) - SOCIAL_AUTH_OKTA_OPENIDCONNECT_SECRET (env var + Docker secret: okta_openidconnect_secret) - SOCIAL_AUTH_OKTA_OPENIDCONNECT_API_URL (env var) - Add Google OAuth2 configuration variables: - SOCIAL_AUTH_GOOGLE_OAUTH2_KEY (env var) - SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET (env var + Docker secret: google_oauth2_secret) Follows existing patterns with _read_secret() for sensitive data and environ.get() for non-sensitive configuration. Resolves: netbox-community/netbox-docker#1139 * Secrets example * fix: add newline at end of file --------- Co-authored-by: skyefugate --- configuration/configuration.py | 6 ++++++ docker-compose.override.yml.example | 15 +++++++++++++++ env/netbox.env | 8 ++++++++ 3 files changed, 29 insertions(+) diff --git a/configuration/configuration.py b/configuration/configuration.py index 8dfa736..f71786a 100644 --- a/configuration/configuration.py +++ b/configuration/configuration.py @@ -310,6 +310,12 @@ REMOTE_AUTH_SUPERUSER_GROUPS = _environ_get_and_map('REMOTE_AUTH_SUPERUSER_GROUP REMOTE_AUTH_SUPERUSERS = _environ_get_and_map('REMOTE_AUTH_SUPERUSERS', '', _AS_LIST) REMOTE_AUTH_STAFF_GROUPS = _environ_get_and_map('REMOTE_AUTH_STAFF_GROUPS', '', _AS_LIST) REMOTE_AUTH_STAFF_USERS = _environ_get_and_map('REMOTE_AUTH_STAFF_USERS', '', _AS_LIST) +# SSO Configuration +SOCIAL_AUTH_OKTA_OPENIDCONNECT_KEY = environ.get('SOCIAL_AUTH_OKTA_OPENIDCONNECT_KEY') +SOCIAL_AUTH_OKTA_OPENIDCONNECT_SECRET = _read_secret('okta_openidconnect_secret', environ.get('SOCIAL_AUTH_OKTA_OPENIDCONNECT_SECRET', '')) +SOCIAL_AUTH_OKTA_OPENIDCONNECT_API_URL = environ.get('SOCIAL_AUTH_OKTA_OPENIDCONNECT_API_URL') +SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = environ.get('SOCIAL_AUTH_GOOGLE_OAUTH2_KEY') +SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = _read_secret('google_oauth2_secret', environ.get('SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET', '')) # This repository is used to check whether there is a new release of NetBox available. Set to None to disable the # version check or use the URL below to check for release in the official NetBox repository. diff --git a/docker-compose.override.yml.example b/docker-compose.override.yml.example index 1394814..8c2ff70 100644 --- a/docker-compose.override.yml.example +++ b/docker-compose.override.yml.example @@ -16,3 +16,18 @@ services: # SUPERUSER_EMAIL: "" # SUPERUSER_NAME: "" # SUPERUSER_PASSWORD: "" + # SSO Configuration + # SOCIAL_AUTH_OKTA_OPENIDCONNECT_KEY: "your_okta_client_id" + # SOCIAL_AUTH_OKTA_OPENIDCONNECT_API_URL: "https://your-domain.okta.com" + # SOCIAL_AUTH_GOOGLE_OAUTH2_KEY: "your_google_client_id" + # secrets: + # - okta_openidconnect_secret + # - google_oauth2_secret + +# Uncomment to use Docker secrets for SSO credentials +# secrets: +# okta_openidconnect_secret: +# file: ./secrets/okta_secret.txt +# google_oauth2_secret: +# file: ./secrets/google_secret.txt + diff --git a/env/netbox.env b/env/netbox.env index 88c8705..c7b97db 100644 --- a/env/netbox.env +++ b/env/netbox.env @@ -33,4 +33,12 @@ REDIS_SSL=false RELEASE_CHECK_URL=https://api.github.com/repos/netbox-community/netbox/releases SECRET_KEY='r(m)9nLGnz$(_q3N4z1k(EFsMCjjjzx08x9VhNVcfd%6RF#r!6DE@+V5Zk2X' SKIP_SUPERUSER=true +# SSO Configuration (uncomment and configure as needed) +# OKTA OpenID Connect +# SOCIAL_AUTH_OKTA_OPENIDCONNECT_KEY=your_okta_client_id +# SOCIAL_AUTH_OKTA_OPENIDCONNECT_SECRET=your_okta_client_secret +# SOCIAL_AUTH_OKTA_OPENIDCONNECT_API_URL=https://your-domain.okta.com +# Google OAuth2 +# SOCIAL_AUTH_GOOGLE_OAUTH2_KEY=your_google_client_id +# SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET=your_google_client_secret WEBHOOKS_ENABLED=true From 021c3fea99950150a10846d07c0e6f9798cbe539 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 19:14:21 +0000 Subject: [PATCH 14/27] chore(deps): update dependency sentry-sdk to v2.48.0 (#1579) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- requirements-container.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-container.txt b/requirements-container.txt index 65a5f82..c597266 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -4,4 +4,4 @@ granian[uvloop]==2.6.0 python3-saml==1.16.0 --no-binary lxml --no-binary xmlsec -sentry-sdk[django]==2.47.0 +sentry-sdk[django]==2.48.0 From 968bef976ca068a23f8e19d02730721db189db1c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Dec 2025 00:05:52 +0000 Subject: [PATCH 15/27] chore(deps): update dependency dulwich to v0.25.0 --- requirements-container.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-container.txt b/requirements-container.txt index c597266..0145b21 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -1,5 +1,5 @@ django-auth-ldap==5.2.0 -dulwich==0.24.10 +dulwich==0.25.0 granian[uvloop]==2.6.0 python3-saml==1.16.0 --no-binary lxml From 36f075e407d843d423ccabc6bed9754ae9b1d305 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Dec 2025 18:05:18 +0000 Subject: [PATCH 16/27] chore(deps): update dependency django-auth-ldap to v5.3.0 --- requirements-container.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-container.txt b/requirements-container.txt index 0145b21..1e4b906 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -1,4 +1,4 @@ -django-auth-ldap==5.2.0 +django-auth-ldap==5.3.0 dulwich==0.25.0 granian[uvloop]==2.6.0 python3-saml==1.16.0 From ebba20d02b5b827be1ef14984262b7fac7cf5584 Mon Sep 17 00:00:00 2001 From: Tobias Genannt Date: Mon, 1 Dec 2025 12:50:47 +0100 Subject: [PATCH 17/27] Changed volume to match upstream image --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 86586c0..d358001 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -43,7 +43,7 @@ services: retries: 5 env_file: env/postgres.env volumes: - - netbox-postgres-data:/var/lib/postgresql/data + - netbox-postgres:/var/lib/postgresql # redis redis: @@ -75,7 +75,7 @@ services: volumes: netbox-media-files: driver: local - netbox-postgres-data: + netbox-postgres: driver: local netbox-redis-cache-data: driver: local From da0784992eb5ee55efac30338432d7f5aebe2d74 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 Jan 2026 13:54:12 +0000 Subject: [PATCH 18/27] chore(deps): update dependency granian to v2.6.1 --- requirements-container.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-container.txt b/requirements-container.txt index 1e4b906..82db07d 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -1,6 +1,6 @@ django-auth-ldap==5.3.0 dulwich==0.25.0 -granian[uvloop]==2.6.0 +granian[uvloop]==2.6.1 python3-saml==1.16.0 --no-binary lxml --no-binary xmlsec From 20cccc3869aeb4455501fc0622b340cc9ababb86 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 Jan 2026 10:09:25 +0000 Subject: [PATCH 19/27] chore(deps): update dependency sentry-sdk to v2.49.0 --- requirements-container.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-container.txt b/requirements-container.txt index 82db07d..3242b8f 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -4,4 +4,4 @@ granian[uvloop]==2.6.1 python3-saml==1.16.0 --no-binary lxml --no-binary xmlsec -sentry-sdk[django]==2.48.0 +sentry-sdk[django]==2.49.0 From fd6a75ac96185921ad004967c2878ad18516e8d2 Mon Sep 17 00:00:00 2001 From: Tobias Genannt Date: Thu, 8 Jan 2026 13:36:54 +0100 Subject: [PATCH 20/27] Support new API token format (#1591) Support new API token format --- .flake8 | 2 +- Dockerfile | 1 + docker/docker-entrypoint.sh | 37 ++----------------------------------- docker/super_user.py | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 36 deletions(-) create mode 100644 docker/super_user.py diff --git a/.flake8 b/.flake8 index 83a86a2..6872797 100644 --- a/.flake8 +++ b/.flake8 @@ -4,4 +4,4 @@ extend-ignore = E203, W503 per-file-ignores = configuration/*:E131,E251,E266,E302,E305,E501,E722 startup_scripts/startup_script_utils/__init__.py:F401 - docker/*:E266,E722 + docker/*:E266,E722,E501 diff --git a/Dockerfile b/Dockerfile index 753200a..91f814f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -76,6 +76,7 @@ COPY docker/configuration.docker.py /opt/netbox/netbox/netbox/configuration.py COPY docker/ldap_config.docker.py /opt/netbox/netbox/netbox/ldap_config.py COPY docker/docker-entrypoint.sh /opt/netbox/docker-entrypoint.sh COPY docker/launch-netbox.sh /opt/netbox/launch-netbox.sh +COPY docker/super_user.py /opt/netbox/super_user.py COPY configuration/ /etc/netbox/config/ COPY docker/granian.py /opt/netbox/netbox/netbox/granian.py COPY VERSION /opt/netbox/VERSION diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh index fa5930d..1d36167 100755 --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -54,43 +54,10 @@ fi if [ "$SKIP_SUPERUSER" == "true" ]; then echo "↩️ Skip creating the superuser" else - if [ -z ${SUPERUSER_NAME+x} ]; then - SUPERUSER_NAME='admin' - fi - if [ -z ${SUPERUSER_EMAIL+x} ]; then - SUPERUSER_EMAIL='admin@example.com' - fi - if [ -f "/run/secrets/superuser_password" ]; then - SUPERUSER_PASSWORD="$( str | None: + try: + f = open("/run/secrets/" + secret_name, "r", encoding="utf-8") + except EnvironmentError: + return default + else: + with f: + return f.readline().strip() + + +su_name = environ.get("SUPERUSER_NAME", "admin") +su_email = environ.get("SUPERUSER_EMAIL", "admin@example.com") +su_password = _read_secret("superuser_password", environ.get("SUPERUSER_PASSWORD", "admin")) +su_api_token = _read_secret( + "superuser_api_token", + environ.get("SUPERUSER_API_TOKEN", "0123456789abcdef0123456789abcdef01234567"), +) + +if not User.objects.filter(username=su_name): + u = User.objects.create_superuser(su_name, su_email, su_password) + msg = "" + if not settings.API_TOKEN_PEPPERS: + print("⚠️ No API token will be created as API_TOKEN_PEPPERS is not set") + msg = f"💡 Superuser Username: {su_name}, E-Mail: {su_email}" + else: + t = Token.objects.create(user=u, token=su_api_token, version=TokenVersionChoices.V2) + msg = f"💡 Superuser Username: {su_name}, E-Mail: {su_email}, API Token: {t} (use with '{t.get_auth_header_prefix()}')" + print(msg) From c8232c058eb391a34f7a1b8dfe7d88d8372d5f64 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 9 Jan 2026 00:43:09 +0000 Subject: [PATCH 21/27] chore(deps): update dependency dulwich to v0.25.1 --- requirements-container.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-container.txt b/requirements-container.txt index 3242b8f..2502274 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -1,5 +1,5 @@ django-auth-ldap==5.3.0 -dulwich==0.25.0 +dulwich==0.25.1 granian[uvloop]==2.6.1 python3-saml==1.16.0 --no-binary lxml From 4f60741e791bec506d9df94ed42fcead1e85af7f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 Jan 2026 01:54:47 +0000 Subject: [PATCH 22/27] chore(deps): update dependency dulwich to v0.25.2 --- requirements-container.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-container.txt b/requirements-container.txt index 2502274..9d9f910 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -1,5 +1,5 @@ django-auth-ldap==5.3.0 -dulwich==0.25.1 +dulwich==0.25.2 granian[uvloop]==2.6.1 python3-saml==1.16.0 --no-binary lxml From 050f3134851d47bafec8df5c921863153849cb02 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 18 Jan 2026 02:03:43 +0000 Subject: [PATCH 23/27] chore(deps): update dependency dulwich to v1 --- requirements-container.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-container.txt b/requirements-container.txt index 9d9f910..bbd3ec0 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -1,5 +1,5 @@ django-auth-ldap==5.3.0 -dulwich==0.25.2 +dulwich==1.0.0 granian[uvloop]==2.6.1 python3-saml==1.16.0 --no-binary lxml From 909c234af641155c3e3f238709ee72eeb7b3a4ca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 20 Jan 2026 14:45:03 +0000 Subject: [PATCH 24/27] chore(deps): update dependency sentry-sdk to v2.50.0 --- requirements-container.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-container.txt b/requirements-container.txt index bbd3ec0..118d963 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -4,4 +4,4 @@ granian[uvloop]==2.6.1 python3-saml==1.16.0 --no-binary lxml --no-binary xmlsec -sentry-sdk[django]==2.49.0 +sentry-sdk[django]==2.50.0 From a4a83453d95b4ce1d545d9a6ebec0e6f5ccb1e15 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 11:07:37 +0000 Subject: [PATCH 25/27] chore(deps): update dependency sentry-sdk to v2.51.0 --- requirements-container.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-container.txt b/requirements-container.txt index 118d963..2535bbd 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -4,4 +4,4 @@ granian[uvloop]==2.6.1 python3-saml==1.16.0 --no-binary lxml --no-binary xmlsec -sentry-sdk[django]==2.50.0 +sentry-sdk[django]==2.51.0 From 52b1e23c80c3c878ea5f238e9f41b0212b7d1d24 Mon Sep 17 00:00:00 2001 From: Tobias Genannt Date: Mon, 2 Feb 2026 12:46:02 +0100 Subject: [PATCH 26/27] Fixed serving of the local documentation --- docker/launch-netbox.sh | 1 + requirements-container.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/launch-netbox.sh b/docker/launch-netbox.sh index a61e076..d68f9bc 100755 --- a/docker/launch-netbox.sh +++ b/docker/launch-netbox.sh @@ -15,6 +15,7 @@ exec granian \ --working-dir "/opt/netbox/netbox/" \ --static-path-route "/static" \ --static-path-mount "/opt/netbox/netbox/static/" \ + --static-path-dir-to-file index.html \ --pid-file "/tmp/granian.pid" \ "${GRANIAN_EXTRA_ARGS[@]}" \ "netbox.granian:application" diff --git a/requirements-container.txt b/requirements-container.txt index 2535bbd..ebe01cb 100644 --- a/requirements-container.txt +++ b/requirements-container.txt @@ -1,6 +1,6 @@ django-auth-ldap==5.3.0 dulwich==1.0.0 -granian[uvloop]==2.6.1 +granian[uvloop]==2.7.0 python3-saml==1.16.0 --no-binary lxml --no-binary xmlsec From 01a36c69c4998dd191f406fbf78129497f75c421 Mon Sep 17 00:00:00 2001 From: Tobias Genannt Date: Mon, 2 Feb 2026 12:46:51 +0100 Subject: [PATCH 27/27] Preparation for 4.0.0 --- VERSION | 2 +- docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index a423d42..0c89fc9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.4.2 \ No newline at end of file +4.0.0 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 1734760..64c67ba 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: netbox: &netbox - image: docker.io/netboxcommunity/netbox:${VERSION-v4.4-3.4.2} + image: docker.io/netboxcommunity/netbox:${VERSION-v4.5-4.0.0} depends_on: - postgres - redis