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.
- Honors
Retry-After rate limiting header for API calls.
- 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 and can be enabled when desired. It's recommended to monitor leaf certificates where they are actively used via e.g. tlscheckreceiver rather than tracking down each certificate's location after the fact.
-
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.160.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.