Most APIs receive their database passwords and API keys through environment variables. The values are then visible to anyone who can inspect the container or its deployment manifest, they are copied into every environment that needs them, and changing one means finding every place it was copied to. Security reviews and certifications such as SOC 2 and ISO 27001 ask for credentials to be held in a system that controls and records access to them.
HashiCorp Vault is the most widely used such system, and OpenBao is its open-source fork under the Linux Foundation, with the same API. The Vault driver connects Kipchak APIs to either. It is part of Kipchak Enterprise, and was listed on the public roadmap.
Secrets in configuration
A Kipchak API configures its drivers with PHP configuration files. With the Vault driver, any value in those files can refer to a secret in Vault instead of an environment variable:
use function Kipchak\Driver\Vault\vault;
'user' => vault('secret/orders-api/database', 'username'),
'password' => vault('secret/orders-api/database', 'password'),
When a worker starts, the Vault driver reads each referenced secret once and replaces every reference with its value. The Doctrine, S3, Kafka and other drivers that start after it receive ordinary values, so none of them needs to change. A value keeps its type, so a JSON object stored in Vault becomes an array; a complete list of API keys can be held in a single secret.
If a secret or a key does not exist, if the API is not permitted to read it, or if Vault cannot be reached, the worker does not start, and the container platform restarts the container. An API therefore never runs with a credential missing. For local development without Vault, the driver can be disabled, and each reference then takes a default value given beside it.
Authentication
The driver supports the authentication methods that production platforms use:
| Method | Typical use |
|---|---|
| Kubernetes | Pods authenticate with their service account token |
| AppRole | Virtual machines and container platforms other than Kubernetes |
| JWT | Workloads with a token from a CI system or an identity provider |
| TLS certificate | Environments that already issue client certificates |
| Token | A token written by Vault Agent, or a token for development |
Credentials can be read from files, and files are read again at each login, so a Kubernetes token or a Vault Agent token that is rotated on disk is used without a restart.
A FrankenPHP worker runs for hours or days, longer than a Vault token usually lives. The driver logs in on first use, and logs in again or renews the token before it expires. If Vault revokes the token, the driver detects it, logs in again and repeats the request once.
Secrets at runtime
The API can also use Vault while it runs:
- KV, versions 1 and 2, detected for each mount: reading a secret or an earlier version of it, writing with check-and-set, partial updates, deletion and listing.
- Transit, which encrypts and decrypts data with keys that never leave Vault. An API can encrypt personal data before storing it, without holding the key itself.
- Any other secrets engine, through generic read, write, list and delete operations that return the lease details.
Testing
The driver is tested against OpenBao 2.4 and HashiCorp Vault 1.20, and against OpenBao over TLS with certificate authentication.
In worker-mode testing on the Kipchak Enterprise runtime image, an API whose database password and API keys were held only in OpenBao served 33,900 requests in 60 seconds, each reading from KV, encrypting with Transit or querying PostgreSQL. Its token expired every five seconds. Every request succeeded and memory use remained constant. With OpenBao paused, runtime reads failed after the configured timeout while database queries continued, and the API recovered as soon as OpenBao returned.
A related improvement
The same testing showed that Laravel’s HTTP client, on which the Kipchak HTTP driver is built, opens a new connection for every request. In a long-lived worker under load, closed connections accumulate faster than the operating system releases them, and new connections begin to fail. Version 2.14 of the HTTP driver keeps connections open and reuses them for the life of the worker. An API calling Open Policy Agent through the OPA driver lost 15% of 33,300 requests with the previous version and none with version 2.14. The OPA, ClickHouse, Vector, Cloudflare DNS, ClouDNS and OAuth 2 Token drivers benefit without any change.
Installation
The Vault driver is part of Kipchak Enterprise and is installed from the Enterprise Composer repository:
composer require kipchak/driver-vault
The Vault driver documentation describes the configuration, the authentication methods and the runtime interface. Dynamic secrets, such as database credentials that Vault creates and expires, can be read in this release. Renewing their leases and rotating them automatically are planned for a later release.