OpenTelemetry Collector PKI engine receiver
This OpenTelemetry Collector Receiver scrapes metrics for OpenBao and Vault PKI engines by auto-discovering mounts, issuers, certificate revocation lists (CRLs) and leaf certificates. It supports setups where the CRLs are hosted outside of the secret store, e.g., in multi-tier PKI setups with an offline root.
flowchart LR
OTEL[OTEL Collector]
subgraph Store [OpenBao/Vault]
direction TB
subgraph PKI1 [PKI engine]
direction LR
I1[Issuers]
C1[CRLs]
C2[Leafs]
end
end
subgraph External [External]
direction TB
style External stroke-dasharray: 5 5
CRL_Ext[CRLs]
end
OTEL -- "1. Scrape" --> Store
I1 -- "2. Reference" --> CRL_Ext
OTEL -- "3. Scrape" --> CRL_Ext
Features
- Auto discover engines of type
pki, issuers, CRLs (base & delta) and leaf certificates.
- Collect referenced CRLs hosted outside of the secret store.
- Tested against OpenBao and Vault.
- Support for namespaces.
- Resolve AIA URL templating.
- Implemented CRL protocols:
- Efficient CRL fetching:
- Prevents thundering hurds.
- Respects
ETag and Last-Modified HTTP headers.
- Implemented auth methods:
- Token
- AppRole
- Kubernetes
- JWT
Metrics
For collected metrics and attributes, see documentation.md.
Considerations
-
Collection of leaf certificates is disabled by default. It's recommended to monitor leaf certificates where they are actively used via e.g. tlscheckreceiver. When using cert-manager on Kubernetes consider collecting Certificate resources via the k8sobjectsreceiver.
-
Tidy metrics which includes counters for issued and revoked certificates can be collected via the exposed telemetry endpoints by the secret store and scraped via e.g. Prometheus jobs. See secrets.pki.tidy.* metrics (OpenBao | Vault). For extra large deployments consider disabling metric pkiengine.mount.certificates_stored and rely on Tidy metrics as secret stores efficiently track changes to maintain internal counters.
Installation
Adding components to an OTeL Collector requires building a custom collector via OpenTelemetry Collector builder (or ocb for short).
When building a custom collector you can add this receiver to the manifest file like the following:
receivers:
- gomod: github.com/cvdtang/pkienginereceiver v0.156.0
Configuration
Example
receivers:
pkiengine:
address: https://openbao.example.com
namespace: tenant-a
collection_interval: 5m
crl:
retries: 1
auth:
type: approle
approle:
role_id: "my-role-id"
secret_id: "my-secret-id"
Options
address (string): (default = http://127.0.0.1:8200) Address of secret store. Must be formatted as {protocol}://{host}.
collection_interval (string): (default = 5m) How frequently the scraper should be called. This value must be a string readable by Golang's time.ParseDuration. Valid time units are ns, us (or µs), ms, s, m, h.
initial_delay (string): (default = 0s) Initial start delay for the scraper.
timeout (string): Optional value used to set scraper's context deadline.
namespace (string): (default = "") Secret store namespace path.
match_regex (string): (default = ".*") Regular expression in RE2 syntax of allowed mount paths, e.g. pki/v1/ica/v\d.
concurrency_limit (int): Maximum number of concurrent worker tasks (mount/issuer/CRL). Defaults to the number of CPU cores determined by GOMAXPROCS.
Leaf certificates
leaf_certificates.enabled (bool): (default = false) Enable collection of stored leaf certificates (no_store=false). Use with care in large deployments due to high cardinality and higher API load.
CRL
crl.enabled (bool): (default = true) Enable CRL processing.
crl.scrape_parent (bool): (default = true) Enable scraping issuer-role (parent) CRLs referenced in issuer certificates. When false, only subject-role CRLs from issuer API fields are scraped.
crl.cache_size (int): (default = 50) Maximum number of entries in the LRU cache for processed CRLs. Set to 0 to disable CRL caching.
crl.timeout (string): (default = 5s) Maximum time to wait for a CRL fetch response.
crl.retries (int): (default = 0) Number of retry attempts for CRL fetching after the initial attempt.
crl.retry_interval (string): (default = 3s) Wait interval between CRL fetch retry attempts.
Authentication
auth.type (string): (default = token) Authentication method to use. Allowed values: token, approle, kubernetes and jwt.
Token
Docs: OpenBao | Vault
auth.token.token (string): Static authentication token.
receivers:
pkiengine:
auth:
type: token
token:
token: ${env:TOKEN}
AppRole
Docs: OpenBao | Vault
auth.approle.role_id (string): AppRole RoleID.
auth.approle.secret_id (string): AppRole SecretID.
auth.approle.wrapping_token (bool): (default = false) Use single-use token that prevents the underlying credential from being exposed in transit.
auth.approle.mount_path (string): (default = approle) Mount path of the AppRole auth engine.
receivers:
pkiengine:
auth:
type: approle
approle:
role_id: ${env:APPROLE_ROLE_ID}
secret_id: ${env:APPROLE_SECRET_ID}
Kubernetes
Docs: OpenBao | Vault
auth.kubernetes.role_name (string): Name of the secret store role with Kubernetes service account bound to it.
auth.kubernetes.service_account_token (string): Override Kubernetes service account JWT.
auth.kubernetes.service_account_token_path (string): Path where the Kubernetes service account token is mounted in the pod.
auth.kubernetes.mount_path (string): (default = kubernetes) Mount path of the Kubernetes auth engine.
The kubernetes auth method can be used to authenticate with the secret stores using a Bound Service Account Token. Defaults to /var/run/secrets/kubernetes.io/serviceaccount/token:
receivers:
pkiengine:
auth:
type: kubernetes
kubernetes:
role_name: my-role
Alternatively, long-lived tokens can be used:
receivers:
pkiengine:
auth:
type: kubernetes
kubernetes:
role_name: my-role
service_account_token: ${env:SERVICE_ACCOUNT_TOKEN}
JWT
Docs: OpenBao | Vault
auth.jwt.role_name (string): Name of the role in OpenBao/Vault with JWT claims bound to it.
auth.jwt.token (string): JWT token to use for authentication.
auth.jwt.token_path (string): Path to a file containing the JWT token.
auth.jwt.mount_path (string): (default = jwt) Mount path of the JWT auth engine.
From value:
receivers:
pkiengine:
auth:
type: jwt
jwt:
role_name: my-role
token: ${env:JWT_TOKEN}
From file:
receivers:
pkiengine:
auth:
type: jwt
jwt:
role_name: my-role
token_path: /my/token
Metrics & Resource Attributes
metrics.<name>.enabled (bool): (default = true) Enable/disable emitting the specified metric.
resource_attributes.<name>.enabled (bool): (default = true) Enable/disable emitting the specified resource attribute.
Additionally, the Vault Go SDK environment variables can be set, e.g. VAULT_SKIP_VERIFY.
Authorization policy
Required permissions used by the receiver.
Note that the pki/* paths can differ depending on where the PKI engines are mounted.
# ref: https://developer.hashicorp.com/vault/api-docs/system/mounts#list-mounted-secrets-engines
path "sys/mounts" {
capabilities = [ "read" ]
}
# ref: https://developer.hashicorp.com/vault/api-docs/secret/pki#read-cluster-configuration
path "pki/config/cluster" {
capabilities = [ "read" ]
}
# ref: https://developer.hashicorp.com/vault/api-docs/secret/pki#list-certificates
path "pki/certs" {
capabilities = [ "list" ]
}
# ref: https://developer.hashicorp.com/vault/api-docs/secret/pki#read-issuer
path "pki/issuer/+" {
capabilities = [ "read" ]
}
Troubleshooting
Enable OTeL Collector debug logging:
receivers:
pkiengine:
address: http://127.0.0.1:8200
initial_delay: 0s
collection_interval: 1m
service:
telemetry:
logs:
level: debug
pipelines:
metrics:
receivers: [pkiengine]
exporters: [debug]
Development
For development, internals and testing, see DEVELOPMENT.md.