NetBox Certificates Plugin
NetBox Certificates Plugin adds X.509 certificate inventory, encrypted private-key storage, CSRs, cryptographic bundles, root CA identities, hierarchical artifact groups, relationship tracking, import/export workflows, and certificate-expiration alerting to NetBox.
AI-assisted development disclosure: Substantial portions of the initial implementation, testing strategy, technical documentation, and release engineering for this project were produced with assistance from ChatGPT by OpenAI, under human direction and review. The human maintainer is responsible for the code and releases. OpenAI does not maintain, sponsor, or endorse this project. See AI_ASSISTANCE.md and NOTICE.
Status
Release: 0.4.11
This release has been extensively validated on NetBox 4.5.9. The plugin declares compatibility with NetBox 4.5.9 through 4.5.10.
| NetBox version | Status | Reason |
|---|---|---|
| 4.5.9 | Validated / supported | Full live integration, API, cryptographic, authentication, and ObjectPermission testing was performed on 4.5.9. |
| 4.5.10 | Supported by compatibility gate | Same 4.5 patch series; 4.5.10 is a bug-fix release. It has not received the same exhaustive live validation as 4.5.9. |
| 4.5.8 and below | Unsupported / rejected | PluginConfig.min_version is 4.5.9. NetBox 4.5.9 also fixed constrained ObjectPermission scope filtering, which this plugin relies on. |
| 4.6.0 and above | Unsupported / rejected | PluginConfig.max_version is 4.5.10. NetBox 4.6 moved to Django 6.0 and introduced/deprecated plugin APIs; a separate compatibility release and test cycle is required. |
Python 3.12+ is required by the package. NetBox 4.5 itself requires Python 3.12, 3.13, or 3.14. This plugin's production validation was performed with Python 3.12.
Do not bypass the NetBox version gate in production. A future NetBox minor release can change plugin APIs even when the Python package imports successfully.
Why this plugin exists
NetBox is excellent at modeling infrastructure, but it does not natively model the complete cryptographic lifecycle of certificates, private keys, CSRs, matching identities, exportable bundles, and expiration-delivery history. This plugin adds that layer while using NetBox-native concepts such as PrimaryModel metadata, owners, tags, custom fields, ObjectPermissions, REST APIs, jobs, and change logging.
The plugin is designed for operators who need to answer questions such as:
- Which certificates are about to expire?
- Which private key belongs to a certificate or CSR?
- Is a bundle cryptographically complete?
- Which objects were imported together?
- Which root CA does a certificate chain resolve to?
- Who may view metadata versus download sensitive material?
- Can a non-admin export a public bundle without gaining access to a private key?
- Which expiration notifications were actually delivered?
Data model
flowchart LR
G[Artifact Group] --- C[Certificate]
G --- K[Private Key]
G --- R[CSR]
G --- B[Bundle]
CA[Certificate Authority\nroot identity] -->|authority| C
C -->|primary certificate| B
K -->|primary private key| B
R -->|primary CSR| B
C2[Chain Certificates] -->|chain members| B
C -. public-key identity .- K
K -. public-key identity .- R
C -. public-key identity .- R
L[Artifact Link] -. generic relation .-> C
L -. generic relation .-> K
L -. generic relation .-> R
L -. generic relation .-> B
CFG[Expiration Alert Configuration] --> EV[Expiration Alert Events]
EV --> C
Certificate
Represents an X.509 certificate and stores the parsed cryptographic metadata required for inventory and expiration management. Important fields include SHA-256 fingerprint, public-key fingerprint, serial number, subject, issuer, SANs, validity window, signature algorithm, key type/size/curve, CA flag, root CA identity, parent/supersession relationships, owner, groups, tags, and alert trigger settings.
Certificate material is stored because certificates are public objects by design. Downloading it through the protected download action still requires the plugin's custom download permission and a write-enabled NetBox API token for API access.
Private Key
Represents a private key. Raw private-key material is never exposed in normal list/detail serializers. It is encrypted before database storage using a Fernet key supplied through PLUGINS_CONFIG.
The model records metadata such as SHA-256 material fingerprint, public-key fingerprint, key type/size, import-encryption state, groups, owner, tags, description, and comments.
Private-key material has an additional security boundary: creation/replacement/download of material through the API is restricted to a NetBox superuser using a write-enabled API token, even when a normal user has the model's add/change/download ObjectPermission.
CSR
Represents a PKCS#10 Certificate Signing Request. The plugin parses subject, SANs, signature algorithm, key properties, request fingerprint, and public-key fingerprint. The public-key fingerprint allows a CSR to be matched to a certificate and/or private key.
The UI/API can also generate a CSR and a matching private key. Because generation creates private-key material, the generation action is superuser-only through the API.
Bundle
A Bundle represents a set of cryptographic objects sharing the same public-key identity.
The three primary artifacts are:
- Certificate
- Private Key
- CSR
A Bundle is:
- Partial when any two matching primary artifacts are present.
- Complete only when all three matching primary artifacts are present.
A Bundle can additionally hold chain certificates, import metadata, source/archive format, an optional preserved encrypted archive, groups, owner, tags, description, and comments.
Direct REST POST to the Bundle endpoint is intentionally disabled. Use Import Objects (/import-objects/) so cryptographic identity validation occurs before the Bundle is created.
Certificate Authority
Represents a root CA identity derived from imported self-signed root certificates. It is not a second copy of a certificate. Certificates point to the resolved root authority when a complete root path can be established.
The REST Certificate Authority endpoint is intentionally read-only. CA identities are maintained from certificate material rather than manually created through the API.
Artifact Group
A user-managed hierarchical grouping mechanism for Certificates, Private Keys, CSRs, and Bundles. Groups can have a parent group; cycles and self-parenting are rejected.
Groups are independent of NetBox authentication groups. ArtifactGroup is an inventory organization object; users.Group is an authorization object.
Artifact Link
Represents a generic relationship between supported artifact objects. Links may be:
- automatic, generated from cryptographic relationships; or
- manual, created by an operator.
Automatic links cannot be changed/deleted through the REST API. Manual links can be managed when the user has the appropriate ObjectPermission and can see both endpoints.
Expiration Alert Configuration
A singleton object controlling the expiration worker policy and notification transport configuration. It includes scan interval/repeat behavior, SMTP configuration, webhook configuration, and encrypted transport secrets.
The singleton cannot be deleted through the API. Disable the configured delivery methods instead.
Expiration Alert Event
A delivery-history record generated by the expiration worker for a certificate/method/trigger occurrence. Events are intentionally read/delete only through the API; operators do not create or edit them manually.
Imports and cryptographic matching
The Import Objects workflow inspects uploaded content instead of trusting the filename extension. Supported content includes PEM/DER X.509 certificates, private keys, CSRs, PKCS#7/CMS containers, PKCS#12/PFX, and supported archives. Optional RAR support is available through the rar package extra.
The main safety rule is the public-key fingerprint. A certificate, private key, and CSR may become primary members of the same Bundle only when their public keys match. A mismatched primary set is rejected atomically: the transaction is rolled back rather than leaving partially-created artifacts.
Current upload limits are:
- Combined upload: 25 MiB
- Archive entries: 250 files
- Maximum uncompressed archive content: 100 MiB
Export behavior
Bundles can be exported as ZIP or TAR. Public-only bundles may be exported by authorized non-superusers. If the Bundle contains a Private Key, export becomes a sensitive operation and requires superuser access through the API.
PFX/PKCS#12 export requires a Certificate, matching Private Key, and password, and is always treated as a sensitive operation.
Downloaded sensitive responses set cache-prevention/security headers, and archive members containing cryptographic material are created with restrictive file modes.
UI
The plugin base URL is /plugins/ssl-certificates/.
Important routes:
expiration-dashboard/— expiration overviewinventory/— consolidated artifact inventorycertificate-authorities/— root CA identitiescertificates/— certificate managementprivate-keys/— private-key metadata managementcsrs/— CSR management and generationbundles/— Bundle management/exportgroups/— hierarchical artifact groupsimport/— unified importexpiration-alerts/— alert configuration/history
Most PrimaryModel objects support NetBox-native filtering, bulk editing, ownership, tags, custom fields, descriptions/comments, and change logging.
REST API
The API base is:
/api/plugins/ssl-certificates/
See docs/API.md for endpoint details, authentication rules, custom actions, examples, status-code behavior, and security restrictions.
Permissions
This plugin uses NetBox ObjectPermissions. Standard CRUD actions use NetBox's native actions (view, add, change, delete). The plugin also defines custom actions.
Standard and custom actions
| Object | Standard actions | Custom action string | Django permission codename |
|---|---|---|---|
| Artifact Group | view/add/change/delete | — | — |
| Certificate Authority | view/add/change/delete* | — | — |
| Certificate | view/add/change/delete | download |
netbox_certificates.download_certificate |
| Private Key | view/add/change/delete | download |
netbox_certificates.download_privatekey |
| CSR | view/add/change/delete | download |
netbox_certificates.download_csr |
| Bundle | view/add/change/delete | export, export_pfx |
netbox_certificates.export_bundle, netbox_certificates.export_pfx_bundle |
| Artifact Link | view/add/change/delete | — | — |
| Expiration Alert Configuration | view/add/change | test |
netbox_certificates.test_expiryalertconfiguration |
| Expiration Alert Event | view/delete | — | — |
* The Certificate Authority model has normal model permissions, but its REST API is deliberately read-only. A permission does not override an endpoint's method contract.
Creating permissions in the NetBox UI
Go to Admin → Object Permissions and create a permission with:
- The plugin object type(s).
- One or more users/groups.
- Standard actions and/or the custom action.
- Optional JSON constraints.
On NetBox versions where a plugin custom action is not shown as a dedicated checkbox, use the ObjectPermission form's Additional actions field and enter the action string exactly as listed above: download, export, export_pfx, or test.
Custom actions still require the object's view scope to resolve the target object. In practical terms, grant the corresponding view action together with a custom action unless the user already receives view permission from another ObjectPermission.
Creating custom-action ObjectPermissions from the shell
The following example creates permissions directly using NetBox's real ObjectPermission objects. It is useful when the NetBox 4.5 UI does not surface a plugin action the way you want.
cd /opt/netbox
sudo -u netbox ./venv/bin/python ./netbox/manage.py shell <<'PY'
from core.models import ObjectType
from users.models import Group, ObjectPermission
from netbox_certificates.models import Certificate, CSR, Bundle
operator_group = Group.objects.get(name="Certificate Operators")
def grant(name, model, actions, constraints=None):
permission, _ = ObjectPermission.objects.get_or_create(
name=name,
defaults={"enabled": True, "actions": list(actions), "constraints": constraints or {}},
)
permission.enabled = True
permission.actions = list(actions)
permission.constraints = constraints or {}
permission.save()
permission.object_types.set([ObjectType.objects.get_for_model(model)])
operator_group.object_permissions.add(permission)
return permission
grant("Certificate Operators - certificate download", Certificate, ["view", "download"])
grant("Certificate Operators - CSR download", CSR, ["view", "download"])
grant("Certificate Operators - public Bundle export", Bundle, ["view", "export"])
PY
NetBox constraints can be added as normal JSON/ORM-style filters, for example:
{"name__startswith": "PROD-"}
The plugin has been validated with constrained reads and constrained creates, including NetBox's transactional rollback when a newly-created object falls outside the permission constraint.
Permissions do not bypass sensitive-operation overlays
Even if a non-superuser is granted add_privatekey, change_privatekey, download_privatekey, export_pfx_bundle, or other related permissions, the following API operations remain superuser-only where private-key material is involved:
- creating/replacing Private Key material;
- downloading Private Key material;
- generating CSR + Private Key;
- exporting a Bundle that contains a Private Key;
- exporting PFX/PKCS#12.
This is deliberate defense in depth.
Configuration
Enable the plugin with its Python module name, not its PyPI distribution name:
PLUGINS = [
"netbox_certificates",
]
The plugin currently has one required PLUGINS_CONFIG setting: encryption_key.
PLUGINS_CONFIG = {
"netbox_certificates": {
"encryption_key": "REPLACE_WITH_A_FERNET_KEY",
}
}
Generate the encryption key
Generate a Fernet key with the same Python/cryptography environment used by NetBox:
/opt/netbox/venv/bin/python -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())'
The result is a URL-safe base64 Fernet key. Copy it exactly into the plugin configuration or provide it from a secret-management/environment mechanism.
A configuration using an environment variable can look like:
import os
PLUGINS_CONFIG = {
"netbox_certificates": {
"encryption_key": os.environ["NETBOX_CERTIFICATES_ENCRYPTION_KEY"],
}
}
If you use an environment variable, make sure the NetBox WSGI and RQ systemd services receive it; setting it only in your interactive shell is not enough.
Critical: Back up the encryption key separately from the database. Do not casually rotate it. Existing encrypted Private Keys, SMTP passwords, webhook URLs/tokens, and other plugin secrets become undecryptable if the key changes without a migration/re-encryption procedure.
SMTP and webhook values are configured in the plugin's Expiration Alerts UI/database singleton. They are not additional configuration.py plugin settings.
Installation from PyPI
The distribution name is netbox-certificates-plugin and the NetBox plugin module is netbox_certificates.
For a production NetBox installation, record the package in /opt/netbox/local_requirements.txt so NetBox reinstalls it when rebuilding the virtual environment:
netbox-certificates-plugin==0.4.11
For RAR import support:
netbox-certificates-plugin[rar]==0.4.11
Install/update local requirements using NetBox's normal upgrade process, then enable/configure the plugin and apply migrations/static assets:
sudo /opt/netbox/upgrade.sh
cd /opt/netbox
sudo -u netbox ./venv/bin/python ./netbox/manage.py migrate
sudo ./venv/bin/python ./netbox/manage.py collectstatic --no-input
sudo -u netbox ./venv/bin/python ./netbox/manage.py check
sudo systemctl restart netbox netbox-rq
If /opt/netbox/upgrade.sh was run after the plugin was already enabled and configured, it may already have performed the migration/static steps. Running migrate and collectstatic again is safe and makes the installation state explicit.
Installation from a GitHub tag
Git installation is useful for validating a release tag before/alongside PyPI:
netbox-certificates-plugin @ git+https://github.com/Fokkert/netbox-certificates-plugin.git@v0.4.11
Put that line in /opt/netbox/local_requirements.txt, run /opt/netbox/upgrade.sh, configure the plugin, migrate, collect static files, run manage.py check, and restart NetBox.
Existing package/module name collision
There is an older third-party GitHub project named NetworkSeb/netbox-certificates which also uses the Python import module netbox_certificates. It is a different project and data model.
Because Python packages with the same import module cannot safely coexist in one virtual environment, do not install both plugins into the same NetBox environment. The PyPI distribution name for this project is different (netbox-certificates-plugin), but the import-module collision still matters.
Background jobs and maintenance commands
The plugin registers background behavior for certificate status and expiration processing. Two useful management commands are also available:
cd /opt/netbox
sudo -u netbox ./venv/bin/python ./netbox/manage.py refresh_certificate_status
sudo -u netbox ./venv/bin/python ./netbox/manage.py reconcile_certificate_links
refresh_certificate_status recalculates stored status from certificate validity timestamps. reconcile_certificate_links rebuilds automatic cryptographic/issuer relationships.
Validation summary
Release 0.4.11 was validated on a live NetBox 4.5.9 installation with API authentication, cryptographic identity checks, import/export behavior, PFX/ZIP handling, read-only and write-enabled NetBox v2 tokens, non-admin permission boundaries, and all plugin ObjectPermission actions.
The final non-admin ObjectPermission matrix executed 146 successful checks, 0 failures, 1 intentional skip, covering 39/39 permission actions. The single skip avoided sending an additional real SMTP/webhook test message to production destinations.
See VALIDATION.md for release notes and maintain your own staging/integration validation before upgrading NetBox or this plugin.
API documentation
Full API documentation: docs/API.md.
Publishing and release engineering
Maintainer instructions for GitHub, TestPyPI, PyPI Trusted Publishing, and release tags are in docs/PUBLISHING.md.
Removal
Destructive clean-removal instructions are in docs/UNINSTALL.md. Back up the database and encryption key before removing the plugin.
License and attribution
Licensed under the Apache License 2.0. See LICENSE and NOTICE.
Apache-2.0 allows use, modification, redistribution, commercial use, and forks. Distributed derivatives must comply with the license's preservation/attribution requirements, including applicable NOTICE content and notices of modified files. The canonical-source attribution is intentionally placed in NOTICE so it travels with redistributed derivatives.
See CONTRIBUTING.md for contribution guidelines.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file netbox_certificates_plugin-0.4.11.tar.gz.
File metadata
- Download URL: netbox_certificates_plugin-0.4.11.tar.gz
- Upload date:
- Size: 120.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a4d39d877c4e7da464564ff3af278561585398f1e8bff806d5747fdbd985b00
|
|
| MD5 |
e17c3ee3ca94a8a68c2a1d75cf887141
|
|
| BLAKE2b-256 |
4fa927db258b3147240180c9ded7fdee22c5f16779e0815a1b6e3ad74aaefc07
|
Provenance
The following attestation bundles were made for netbox_certificates_plugin-0.4.11.tar.gz:
Publisher:
release.yml on Fokkert/netbox-certificates-plugin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
netbox_certificates_plugin-0.4.11.tar.gz -
Subject digest:
7a4d39d877c4e7da464564ff3af278561585398f1e8bff806d5747fdbd985b00 - Sigstore transparency entry: 2688864463
- Sigstore integration time:
-
Permalink:
Fokkert/netbox-certificates-plugin@21d40c7305a96e2fbd28032d9dddfa708135f17c -
Branch / Tag:
refs/tags/v0.4.11 - Owner: https://github.com/Fokkert
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@21d40c7305a96e2fbd28032d9dddfa708135f17c -
Trigger Event:
push
-
Statement type:
File details
Details for the file netbox_certificates_plugin-0.4.11-py3-none-any.whl.
File metadata
- Download URL: netbox_certificates_plugin-0.4.11-py3-none-any.whl
- Upload date:
- Size: 128.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
154d00ee4d1667b730de193495a9afa1d07f3b1e0308a006e7c56ced710d4d73
|
|
| MD5 |
1c7827c418144bc46de646672d2fdda0
|
|
| BLAKE2b-256 |
6418bea6ff3aa3167e937d581aaad322de36e38586d43203073d1d3a234acf59
|
Provenance
The following attestation bundles were made for netbox_certificates_plugin-0.4.11-py3-none-any.whl:
Publisher:
release.yml on Fokkert/netbox-certificates-plugin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
netbox_certificates_plugin-0.4.11-py3-none-any.whl -
Subject digest:
154d00ee4d1667b730de193495a9afa1d07f3b1e0308a006e7c56ced710d4d73 - Sigstore transparency entry: 2688864574
- Sigstore integration time:
-
Permalink:
Fokkert/netbox-certificates-plugin@21d40c7305a96e2fbd28032d9dddfa708135f17c -
Branch / Tag:
refs/tags/v0.4.11 - Owner: https://github.com/Fokkert
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@21d40c7305a96e2fbd28032d9dddfa708135f17c -
Trigger Event:
push
-
Statement type: