Skip to content

Commit 3efe652

Browse files
committed
extend doc for secret provider
1 parent b38bc44 commit 3efe652

File tree

1 file changed

+186
-13
lines changed

1 file changed

+186
-13
lines changed

docs/recipes/secret-provider.md

+186-13
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ Lucee 7 introduces built-in support for secrets management, allowing you to secu
2323

2424
In Lucee 7, secret providers can be configured similarly to datasources, caches, or AI connections, either in the Lucee Administrator (work in progress) or directly in `.CFConfig.json`. Here are sample configurations:
2525

26-
**Environment Variables Example:**
26+
### Environment Variables Provider
2727

28-
Read the secret directly from the environment variables.
28+
Read the secret directly from the environment variables:
2929

3030
```json
3131
"secretProviders": {
@@ -38,9 +38,9 @@ Read the secret directly from the environment variables.
3838
}
3939
```
4040

41-
**File Example:**
41+
### File Provider
4242

43-
Read the secrets from a file, format can be json or env.
43+
Read the secrets from a file, format can be json or env:
4444

4545
```json
4646
"secretProviders": {
@@ -55,9 +55,9 @@ Read the secrets from a file, format can be json or env.
5555
}
5656
```
5757

58-
**Combined Providers Example:**
58+
### Combined Providers
5959

60-
The `AndSecretProvider` allows you to combine multiple providers, checking each one in the order specified until a secret is found.
60+
The `AndSecretProvider` allows you to combine multiple providers, checking each one in the order specified until a secret is found:
6161

6262
```json
6363
"secretProviders": {
@@ -70,18 +70,95 @@ The `AndSecretProvider` allows you to combine multiple providers, checking each
7070
}
7171
```
7272

73+
### Using External Classes
74+
75+
Similar to other Lucee features, you can specify external classes for your secret providers using various methods:
76+
77+
#### OSGi Bundles
78+
79+
```json
80+
"secretProviders": {
81+
"custom": {
82+
"class": "com.mycompany.secrets.CustomSecretProvider",
83+
"bundleName": "com.mycompany.secrets",
84+
"bundleVersion": "1.2.0",
85+
"custom": {
86+
"configParam1": "value1",
87+
"configParam2": "value2"
88+
}
89+
}
90+
}
91+
```
92+
93+
#### Maven Dependencies
94+
95+
Since Lucee 6.2, you can load classes directly from Maven repositories:
96+
97+
```json
98+
"secretProviders": {
99+
"vault": {
100+
"class": "com.company.secrets.VaultSecretProvider",
101+
"maven": "com.company:vault-provider:1.0.0,com.company:common-utils:1.5.0",
102+
"custom": {
103+
"url": "https://vault.example.com",
104+
"timeout": 5000
105+
}
106+
}
107+
}
108+
```
109+
110+
This allows you to specify one or more comma-separated Maven dependencies in gradle-style format.
111+
112+
#### CFML Components (Lucee 7+)
113+
114+
You can implement your own secret provider as a CFML component:
115+
116+
```json
117+
"secretProviders": {
118+
"myCustom": {
119+
"component": "path.to.MySecretProviderComponent",
120+
"custom": {
121+
"configParam1": "value1"
122+
}
123+
}
124+
}
125+
```
126+
127+
The component must implement `lucee.runtime.secrets.SecretProvider` via `implementsJava="lucee.runtime.secrets.SecretProvider"` annotation.
128+
73129
## Supported Providers
74130

75131
Lucee includes several built-in secret providers:
76132

77-
- **EnvVarSecretProvider**: Reads secrets from environment variables
78-
- **FileSecretProvider**: Reads secrets from a .json or .env file (more formats following)
79-
- **AndSecretProvider**: Combines multiple providers into one
133+
- **lucee.runtime.secrets.EnvVarSecretProvider**: Reads secrets from environment variables
134+
- **lucee.runtime.security.FileSecretProvider**: Reads secrets from a .json or .env file (more formats following)
135+
- **lucee.runtime.security.AndSecretProvider**: Combines multiple providers into one
80136
- **AWSSecretsManagerProvider**: Connects to AWS Secrets Manager (coming soon)
81137
- **GoogleSecretManagerProvider**: Uses Google Cloud Secret Manager (coming soon)
82138
- **DockerSecretsProvider**: Reads secrets from Docker secrets (coming soon)
83139

84-
Each provider has specific configuration options as seen above.
140+
## Provider Configuration Options
141+
142+
### EnvVarSecretProvider
143+
144+
| Option | Description | Default | Notes |
145+
|--------|-------------|---------|-------|
146+
| `caseSensitive` | Determines if environment variable names are case-sensitive | `true` | Set to `false` to allow case-insensitive lookups |
147+
148+
### FileSecretProvider
149+
150+
| Option | Description | Default | Notes |
151+
|--------|-------------|---------|-------|
152+
| `type` | File format | `env` | Supported values: `env`, `json` |
153+
| `file` | Path to the secrets file | None (Required) | Can use any Lucee-supported resource path (local, S3, HTTP, etc.) |
154+
| `caseSensitive` | Determines if secret keys are case-sensitive | `true` | Set to `false` to allow case-insensitive lookups |
155+
| `refreshInterval` | How often to check for file changes (in milliseconds) | `60000` (1 minute) | Set to `0` to disable auto-refresh |
156+
157+
### AndSecretProvider
158+
159+
| Option | Description | Default | Notes |
160+
|--------|-------------|---------|-------|
161+
| `providers` | Comma-separated list of provider names to check | None (Required) | Providers are checked in the specified order |
85162

86163
## Using Secrets in Your Application
87164

@@ -170,6 +247,70 @@ The `AndSecretProvider` allows you to chain multiple providers together, checkin
170247
apiKey = GetSecret("API_KEY", "combined");
171248
```
172249

250+
## Creating Custom Secret Providers
251+
252+
You can create your own secret provider by implementing the `lucee.runtime.secrets.SecretProvider` interface. This can be done either in Java (compiled into a JAR and loaded via OSGi or Maven) or directly in CFML.
253+
254+
### CFML Component Implementation
255+
256+
```cfml
257+
// MySecretProvider.cfc
258+
component implementsJava="lucee.runtime.secrets.SecretProvider" {
259+
260+
// Initialize the provider with custom configuration
261+
function init(struct config) {
262+
variables.config = config;
263+
return this;
264+
}
265+
266+
// Get a secret by key
267+
function getSecret(string key) {
268+
// Implementation to retrieve the secret
269+
// Return null if the secret doesn't exist
270+
271+
// Example implementation (database-stored secrets)
272+
var q = queryExecute(
273+
"SELECT value FROM app_secrets WHERE key = :key",
274+
{key: key},
275+
{datasource: config.datasource}
276+
);
277+
278+
if (q.recordCount) {
279+
return q.value;
280+
}
281+
282+
return javaNull();
283+
}
284+
285+
// Check if a secret exists
286+
function hasSecret(string key) {
287+
// Return true if the secret exists, false otherwise
288+
289+
// Example implementation
290+
var q = queryExecute(
291+
"SELECT COUNT(*) as count FROM app_secrets WHERE key = :key",
292+
{key: key},
293+
{datasource: config.datasource}
294+
);
295+
296+
return q.count > 0;
297+
}
298+
}
299+
```
300+
301+
To use this custom provider, configure it in your `.CFConfig.json`:
302+
303+
```json
304+
"secretProviders": {
305+
"database": {
306+
"component": "path.to.MySecretProvider",
307+
"custom": {
308+
"datasource": "secretsDB"
309+
}
310+
}
311+
}
312+
```
313+
173314
## Security Considerations
174315

175316
### Secret Rotation
@@ -186,21 +327,23 @@ Each provider has its own security considerations. For example:
186327

187328
- AWS Secrets Manager requires proper IAM roles and permissions
188329
- Environment variables should be set securely through the operating system
330+
- File-based secrets need strict file permissions
189331

190332
## Troubleshooting
191333

192334
### Debugging
193335

194336
Lucee logs every use of every key to the `application` log with the log level `trace`, this includes the stacktrace, the key used and the name of the secret provider itself.
195-
So when yu enable that log level you will see how and where you secrets get used (not set).
337+
When you enable that log level, you will see how and where your secrets get used (not the values themselves).
196338

197-
In addition Lucee also logs in case it fails to load a secret provider.
339+
In addition, Lucee also logs in case it fails to load a secret provider.
198340

199341
### Common Issues
200342

201343
1. **Secret not found**: Ensure the secret exists in the provider and that the key is correct.
202344
2. **Provider configuration**: Verify that the provider is correctly configured and accessible.
203345
3. **Permission issues**: Check that the application has the necessary permissions to access the secrets.
346+
4. **Class not found**: When using external providers, ensure all required dependencies are available.
204347

205348
## Best Practices
206349

@@ -209,6 +352,8 @@ In addition Lucee also logs in case it fails to load a secret provider.
209352
3. **Implement least privilege**: Only grant access to the specific secrets an application needs.
210353
4. **Monitor usage**: Regularly audit secret access and usage patterns.
211354
5. **Layer providers**: Use the `AndSecretProvider` to implement fallback mechanisms.
355+
6. **Environment segregation**: Use different secret providers for development, staging, and production environments.
356+
7. **Regular rotation**: Rotate secrets regularly and verify that your application handles rotation gracefully.
212357

213358
## Reference
214359

@@ -221,4 +366,32 @@ GetSecret(key [, name])
221366
- **key**: Key to read from the Secret Provider
222367
- **name**: (Optional) Name of the Secret Provider to read from. If not provided, all configured providers are checked in order.
223368

224-
Returns a reference to the secret value that is automatically resolved when used in a context requiring a simple value.
369+
Returns a reference to the secret value that is automatically resolved when used in a context requiring a simple value.
370+
371+
### SecretProvider Interface
372+
373+
```java
374+
package lucee.runtime.secrets;
375+
376+
public interface SecretProvider {
377+
/**
378+
* Initialize the provider with the given configuration
379+
* @param config Provider-specific configuration
380+
*/
381+
public void init(java.util.Map<String, String> config);
382+
383+
/**
384+
* Get a secret by key
385+
* @param key Secret key
386+
* @return Secret value or null if not found
387+
*/
388+
public String getSecret(String key);
389+
390+
/**
391+
* Check if a secret exists
392+
* @param key Secret key
393+
* @return true if the secret exists, false otherwise
394+
*/
395+
public boolean hasSecret(String key);
396+
}
397+
```

0 commit comments

Comments
 (0)