Vulnerability Scanning¶
OpenDepot integrates Trivy to scan both provider artifacts and module archives. Scanning is split into two tiers:
- Module IaC scanning — enabled by
scanning.enabled: true. Detects HCL misconfigurations in module archives using Trivy's bundled config rules. Requires no external infrastructure. Setting this flag automatically switches the version-controller to the-scanningimage variant, which bundles the Trivy binary. - Provider scanning — enabled by
scanning.providerScanning: true(requiresscanning.enabled: true). Scans provider binaries and source dependencies against the Trivy vulnerability database. Requires a shared PersistentVolumeClaim and a CronJob to keep the database current.
Provider Scanning¶
For each provider version, the Version controller performs two scans:
- Binary scan — runs
trivy rootfsagainst the compiled provider binary extracted from the HashiCorp release archive. Results are stored perVersionresource inVersion.status.binaryScanbecause each OS/architecture binary may embed different Go standard library versions or runtime dependencies. - Source scan — fetches
go.modfrom the provider's GitHub repository and runstrivy fsto find vulnerable source dependencies. Results are stored on theProviderresource inProvider.status.sourceScanand deduplicated across OS/architecture variants since all variants share the same source code.
Note
status.binaryScan will be empty for any Version that was synced before scanning was enabled. The controller does not automatically re-scan on restart to avoid re-downloading potentially hundreds of large provider binaries. To trigger a one-time re-download and re-scan, set forceSync: true on the Version resource:
kubectl patch version aws-5-80-0-linux-amd64 -n opendepot-system \
--type merge -p '{"spec":{"forceSync":true}}'
The controller resets forceSync to false after reconciliation completes. See Force re-sync a specific provider version for the full Version name pattern.
See Consuming Providers for examples of reading scan results.
Module IaC Scanning¶
For each module version, the Version controller runs an IaC scan on the extracted module archive:
- IaC scan — extracts the module archive (
.zipor.tar.gz) to a temporary directory and runstrivy fstargeting Trivy's config-class checks. This detects HCL misconfigurations — for example, S3 buckets with public ACLs, security groups open to0.0.0.0/0, or unencrypted storage resources.
Findings are stored in Version.status.sourceScan and use the same SecurityFinding struct as provider scans. The vulnerabilityID field contains a Trivy rule ID (e.g. AVD-AWS-0057) rather than a CVE identifier.
See Consuming Modules for examples of reading scan results.
Scanning Image Variant¶
When scanning.enabled: true, the Helm chart automatically selects the version-controller image tagged with the -scanning suffix (e.g. 0.2.7-scanning). This variant is built from the same Dockerfile as the standard image using the INCLUDE_TRIVY=true build argument, which copies the Trivy binary into the final image. The standard image (0.2.7) does not include Trivy, keeping it lean for installations that do not require any scanning.
Prerequisites¶
Provider scanning requires a shared PersistentVolumeClaim for the Trivy vulnerability database and a CronJob that keeps it up to date. Both are created automatically when scanning.providerScanning is set to true.
The PVC must use a StorageClass that supports ReadWriteMany access so that the trivy-db-updater CronJob and the version-controller pod can mount it simultaneously. For single-node environments such as Kind, ReadWriteOnce with the default storage class is sufficient.
Note
The Trivy DB (PVC + CronJob) is only required for provider binary and source scans. Module IaC scanning uses config rules bundled in the Trivy binary and works without any DB infrastructure — just set scanning.enabled: true.
Enabling Module IaC Scanning¶
To enable lightweight module scanning with no additional infrastructure:
Apply via Helm upgrade:
helm upgrade opendepot opendepot/opendepot \
--namespace opendepot-system \
--set scanning.enabled=true
Enabling Provider Scanning¶
To enable full provider binary and source scanning alongside module IaC scanning:
scanning:
enabled: true
providerScanning: true
blockOnCritical: false
blockOnHigh: false
cache:
storageClassName: "efs-sc" # must support ReadWriteMany in multi-node clusters
accessMode: ReadWriteMany
size: 1Gi
Apply via Helm upgrade:
helm upgrade opendepot opendepot/opendepot \
--namespace opendepot-system \
--set scanning.enabled=true \
--set scanning.providerScanning=true \
--set scanning.cache.storageClassName=efs-sc
Policy Enforcement¶
When blockOnCritical or blockOnHigh is set to true, the Version controller will stop reconciliation for any provider or module version that has findings at or above the configured severity threshold. The Version resource will remain in an unsynced state with a descriptive syncStatus message until the vulnerability is resolved or the policy flag is relaxed.
Warning
Policy enforcement halts reconciliation for the affected Version resource only. Other versions that do not have findings at the blocked severity level will continue to reconcile normally.
Offline Mode¶
By default, provider scanning runs in offline mode (scanning.offline: true). Trivy reads the vulnerability database from the shared PVC populated by the trivy-db-updater CronJob and makes no outbound network calls during a scan. This is the recommended configuration for production clusters.
Set scanning.offline: false only if you want Trivy to download the database directly at scan time instead of relying on the CronJob. This requires the version-controller pod to have egress access to ghcr.io.
Note
scanning.offline only applies to provider scanning. Module IaC scanning uses bundled config rules and makes no network calls regardless of this setting.
Trivy DB Updater CronJob¶
The trivy-db-updater CronJob runs trivy image --download-db-only on a schedule to refresh the local vulnerability database cache. It is only created when scanning.providerScanning: true. By default it runs daily at 02:00 UTC.
Provider Source Repository Resolution¶
When performing a source scan, the Version controller resolves the provider's GitHub repository using the following chain:
- Explicit override — if
spec.providerConfig.sourceRepositoryis set, it is used directly and no lookup is performed. - OpenTofu registry lookup — queries
api.opentofu.org/registry/docs/providers/{namespace}/{name}to retrieve the provider's registered VCS URL. This works for any provider published in the OpenTofu registry, regardless of the owning organization. - Heuristic fallback — constructs
https://github.com/{namespace}/terraform-provider-{name}from the configured namespace and provider name.
If all three steps fail to produce a usable URL, the Version controller logs a warning and skips the source scan. The binary scan still runs.
namespace field
The namespace field on ProviderConfig controls which organisation is used in the registry lookup (step 2) and the heuristic fallback (step 3). It defaults to hashicorp, so existing Provider resources continue to work without any changes.
Set namespace when using a provider that is not published under the hashicorp organization:
sourceRepository override
Use sourceRepository to pin a specific GitHub URL when the OpenTofu registry returns the wrong repository or is unreachable:
spec:
providerConfig:
name: myprovider
namespace: my-org
sourceRepository: "https://github.com/my-org/terraform-provider-myprovider"
Authenticated Source Scanning¶
By default, the Version controller uses an unauthenticated GitHub client when fetching a provider's go.mod for source scanning. This is sufficient for public provider repositories.
For private source repositories or to avoid API rate limits in high-volume environments, set githubClientConfig.useAuthenticatedClient: true on the ProviderConfig:
spec:
providerConfig:
name: myprovider
namespace: my-org
githubClientConfig:
useAuthenticatedClient: true
Note
If the opendepot-github-application-secret Secret is missing from the Version resource's namespace or the authenticated client cannot be initialised, the controller falls back to an unauthenticated client automatically. Source scanning continues; no manual intervention is required.
See GitHub Authentication for instructions on creating the opendepot-github-application-secret Secret.
Helm Values Reference¶
| Value | Default | Description |
|---|---|---|
scanning.enabled | false | Enable Trivy-based scanning. Switches the version-controller to the -scanning image variant and activates module IaC scanning. No PVC or CronJob is created at this level |
scanning.providerScanning | false | Enable provider binary and source scanning. Requires scanning.enabled=true. Creates the Trivy DB PVC and trivy-db-updater CronJob and mounts the cache volume |
scanning.cacheMountPath | /var/cache/trivy | Mount path inside the version-controller container for the Trivy DB cache |
scanning.offline | true | Pass --offline-scan to Trivy. Prevents network calls during scans. Only applies to provider scanning |
scanning.blockOnCritical | false | Halt reconciliation when CRITICAL findings are present (modules or providers) |
scanning.blockOnHigh | false | Halt reconciliation when HIGH findings are present (modules or providers) |
scanning.cache.storageClassName | "" | StorageClass for the Trivy cache PVC (must support ReadWriteMany for multi-node). Omitted from the PVC manifest when blank, allowing the cluster default to apply stably across upgrades |
scanning.cache.accessMode | ReadWriteMany | Access mode for the Trivy cache PVC |
scanning.cache.size | 1Gi | Size of the Trivy DB cache PVC |
scanning.dbUpdater.schedule | "0 2 * * *" | Cron schedule for the Trivy DB update job |
scanning.dbUpdater.image.repository | aquasec/trivy | Trivy image repository for the db-updater CronJob |
scanning.dbUpdater.image.tag | "0.70.0" | Trivy image tag for the db-updater CronJob |