Skip to content

Commit 1324af7

Browse files
authored
Merge pull request #11 from AhmedElsayed101/main
Add KRM exec and Docker functions support
2 parents b1fb284 + 4c343b6 commit 1324af7

35 files changed

+1759
-42
lines changed

.dockerignore

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Test infrastructure (causes permission issues)
2+
test-infrastructure/
3+
4+
# Git files
5+
.git/
6+
.gitignore
7+
8+
# Documentation
9+
*.md
10+
docs/
11+
12+
# Examples (not needed for build)
13+
examples/
14+
15+
# Test files
16+
*_test.go
17+
test/
18+
19+
# IDE files
20+
.vscode/
21+
.idea/
22+
*.swp
23+
*.swo
24+
25+
# OS files
26+
.DS_Store
27+
Thumbs.db
28+
29+
# Build artifacts
30+
bin/
31+
dist/
32+
*.exe
33+
*.dll
34+
*.so
35+
*.dylib
36+
37+
# Logs
38+
*.log
39+
40+
# Temporary files
41+
tmp/
42+
temp/

Dockerfile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Build stage
2+
FROM golang:1.19-alpine AS builder
3+
4+
WORKDIR /app
5+
COPY go.mod go.sum ./
6+
RUN go mod download
7+
8+
COPY . .
9+
RUN CGO_ENABLED=0 GOOS=linux go build -o /secretize ./cmd/secretize
10+
11+
# Final stage
12+
FROM alpine:3.18
13+
14+
# Install ca-certificates for HTTPS connections
15+
RUN apk --no-cache add ca-certificates
16+
17+
COPY --from=builder /secretize /usr/local/bin/secretize
18+
19+
# KRM functions run as nobody user
20+
USER nobody
21+
22+
ENTRYPOINT ["/usr/local/bin/secretize"]

README.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,31 @@ It is possible to use multiple providers at once.
3030

3131
## Installation
3232

33+
Secretize now supports multiple installation methods:
34+
35+
### Method 1: KRM Function (Recommended)
36+
37+
Secretize supports modern Kubernetes Resource Model (KRM) Functions, which work with Kustomize 4.0.0+:
38+
39+
#### Exec KRM Function
40+
Download the binary and use it directly:
41+
```bash
42+
curl -L https://github.com/bbl/secretize/releases/download/v0.0.1/secretize-v0.0.1-linux-amd64.tar.gz | tar -xz
43+
chmod +x secretize
44+
```
45+
46+
#### Containerized KRM Function
47+
Use the Docker image (no installation required):
48+
```yaml
49+
# In your kustomization, reference the container image
50+
annotations:
51+
config.kubernetes.io/function: |
52+
container:
53+
image: ghcr.io/bbl/secretize:latest
54+
```
55+
56+
### Method 2: Legacy Plugin (Deprecated)
57+
3358
Install secretize to your `$XDG_CONFIG_HOME/kustomize/plugin` folder:
3459

3560
1. Export the `XDG_CONFIG_HOME` variable if it's not already set:
@@ -48,6 +73,62 @@ curl -L https://github.com/bbl/secretize/releases/download/v0.0.1/secretize-v0.0
4873

4974
## Usage
5075

76+
### Using KRM Functions (Recommended)
77+
78+
With KRM functions, add the `config.kubernetes.io/function` annotation to your SecretGenerator:
79+
80+
#### Exec KRM Function Example
81+
```yaml
82+
# secret-generator.yaml
83+
apiVersion: secretize/v1
84+
kind: SecretGenerator
85+
metadata:
86+
name: my-secrets
87+
annotations:
88+
config.kubernetes.io/function: |
89+
exec:
90+
path: ./secretize
91+
sources:
92+
- provider: env
93+
literals:
94+
- DATABASE_URL
95+
```
96+
97+
Run with: `kustomize build --enable-alpha-plugins --enable-exec .`
98+
99+
#### Containerized KRM Function Example
100+
```yaml
101+
# secret-generator.yaml
102+
apiVersion: secretize/v1
103+
kind: SecretGenerator
104+
metadata:
105+
name: my-secrets
106+
annotations:
107+
config.kubernetes.io/function: |
108+
container:
109+
image: ghcr.io/bbl/secretize:latest
110+
sources:
111+
- provider: env
112+
literals:
113+
- DATABASE_URL
114+
```
115+
116+
Run with: `kustomize build --enable-alpha-plugins .`
117+
118+
### Legacy Plugin Usage
119+
120+
For the legacy plugin, use without annotations:
121+
122+
```yaml
123+
# kustomization.yaml
124+
generators:
125+
- secret-generator.yaml
126+
```
127+
128+
Run with: `kustomize build --enable-alpha-plugins .`
129+
130+
### Provider Configuration
131+
51132
All providers can generate two types of secrets: `literals` and `kv` (Key-Value secrets).
52133
Literal secrets simply generate a single string output, while KV secrets will output with a dictionary of the key-value pairs.
53134

@@ -279,3 +360,29 @@ data:
279360
secret_key_1: c2VjcmV0X3ZhbHVlXzE=
280361
secret_key_2: c2VjcmV0X3ZhbHVlXzI=
281362
```
363+
364+
## Examples
365+
366+
Check out the [examples](./examples) directory for complete working examples:
367+
368+
- [Legacy Plugin Example](./examples/legacy) - Traditional Kustomize plugin approach
369+
- [Exec KRM Function Example](./examples/exec) - Modern exec-based KRM function
370+
- [Containerized KRM Function Example](./examples/docker) - Docker-based KRM function
371+
372+
## Test Infrastructure
373+
374+
For comprehensive testing with real secret stores, see the [test-infrastructure](./test-infrastructure/) directory which provides:
375+
376+
- **HashiCorp Vault** setup with test secrets
377+
- **AWS Secrets Manager** emulation via LocalStack
378+
- **Kubernetes** cluster with test secrets
379+
- **Automated testing** for all providers and execution modes
380+
381+
```bash
382+
cd test-infrastructure
383+
./test-all-providers.sh
384+
```
385+
386+
## Documentation
387+
388+
For detailed documentation on KRM Functions support, see [KRM Functions Documentation](./docs/KRM_FUNCTIONS.md).

cmd/secretize/main.go

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,40 @@ package main
22

33
import (
44
"fmt"
5+
"io/ioutil"
6+
57
"github.com/bbl/secretize/pkg/generator"
68
"github.com/bbl/secretize/pkg/utils"
79
log "github.com/sirupsen/logrus"
8-
"io/ioutil"
910

1011
"os"
1112
"path/filepath"
13+
14+
"sigs.k8s.io/kustomize/kyaml/fn/framework"
15+
"sigs.k8s.io/kustomize/kyaml/fn/framework/command"
16+
"sigs.k8s.io/kustomize/kyaml/yaml"
1217
)
1318

1419
func main() {
20+
// Check if running as KRM function (no args or stdin has content)
21+
if len(os.Args) == 1 || isKRMFunction() {
22+
runAsKRMFunction()
23+
} else {
24+
// Legacy mode
25+
runLegacyMode()
26+
}
27+
}
28+
29+
// isKRMFunction checks if stdin has content (indicating KRM function mode)
30+
func isKRMFunction() bool {
31+
stat, _ := os.Stdin.Stat()
32+
return (stat.Mode() & os.ModeCharDevice) == 0
33+
}
1534

35+
// runLegacyMode runs the original secretize behavior
36+
func runLegacyMode() {
1637
if len(os.Args) < 2 {
17-
log.Fatal(
18-
"No argument passed, use `secretize /path/to/generator-config.yaml`")
38+
log.Fatal("No argument passed, use `secretize /path/to/generator-config.yaml`")
1939
}
2040

2141
filename, _ := filepath.Abs(os.Args[1])
@@ -33,3 +53,63 @@ func main() {
3353
utils.FatalErrCheck(err)
3454
fmt.Println(out)
3555
}
56+
57+
// SecretGeneratorProcessor implements the KRM function processor
58+
type SecretGeneratorProcessor struct{}
59+
60+
// Process implements the framework.ResourceListProcessor interface
61+
func (p SecretGeneratorProcessor) Process(rl *framework.ResourceList) error {
62+
// Get the function config
63+
if rl.FunctionConfig == nil {
64+
return fmt.Errorf("no function config provided")
65+
}
66+
67+
// Convert function config to YAML string
68+
fcString, err := rl.FunctionConfig.String()
69+
if err != nil {
70+
return fmt.Errorf("failed to marshal function config: %w", err)
71+
}
72+
73+
// Parse as SecretGenerator
74+
secretGenerator, err := generator.ParseConfig([]byte(fcString))
75+
if err != nil {
76+
return fmt.Errorf("failed to parse config: %w", err)
77+
}
78+
79+
// Generate secrets
80+
secrets, err := secretGenerator.FetchSecrets(generator.ProviderRegistry)
81+
if err != nil {
82+
return fmt.Errorf("failed to fetch secrets: %w", err)
83+
}
84+
85+
// Generate the secret resource
86+
secret := secretGenerator.Generate(secrets)
87+
secretYaml, err := secret.ToYamlStr()
88+
if err != nil {
89+
return fmt.Errorf("failed to convert secret to yaml: %w", err)
90+
}
91+
92+
// Parse the generated secret as RNode
93+
rNode, err := yaml.Parse(secretYaml)
94+
if err != nil {
95+
return fmt.Errorf("failed to parse generated secret: %w", err)
96+
}
97+
98+
// Append to items
99+
rl.Items = append(rl.Items, rNode)
100+
101+
return nil
102+
}
103+
104+
// runAsKRMFunction runs secretize as a KRM function
105+
func runAsKRMFunction() {
106+
processor := SecretGeneratorProcessor{}
107+
cmd := command.Build(processor, command.StandaloneDisabled, false)
108+
109+
// Add dockerfile generation support
110+
command.AddGenerateDockerfile(cmd)
111+
112+
if err := cmd.Execute(); err != nil {
113+
os.Exit(1)
114+
}
115+
}

examples/docker/env/README.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Containerized KRM Function Example (Environment Variables)
2+
3+
This example demonstrates how to use Secretize as a containerized KRM function with Kustomize, sourcing secrets from environment variables set directly in the function config.
4+
5+
---
6+
7+
## How It Works
8+
9+
- The containerized KRM function runs in an isolated environment and **does not inherit environment variables from your shell**.
10+
- All required environment variables must be set explicitly in the `envs:` section of the function config in `secret-generator.yaml`.
11+
- This approach is simple, reproducible, and works reliably with Kustomize and Secretize.
12+
13+
---
14+
15+
## Step-by-Step Usage
16+
17+
1. **(Optional) Build the Secretize Docker image (if using `image: secretize:local`):**
18+
```bash
19+
cd ../../..
20+
docker build -t secretize:local .
21+
cd examples/docker/env
22+
```
23+
24+
2. **Export the required environment variables in your shell:**
25+
```bash
26+
export DATABASE_URL="postgresql://user:pass@localhost/db"
27+
export API_KEY="your-secret-api-key"
28+
export JWT_SECRET="your-jwt-secret"
29+
export CONFIG_JSON='{"feature_new_ui": "true", "feature_beta": "false"}'
30+
```
31+
These variables will be referenced by the YAML configuration.
32+
33+
3. **Reference the environment variables in your YAML file:**
34+
In `secret-generator.yaml`, you can reference these variables using the `literals` and `kv` fields:
35+
```yaml
36+
sources:
37+
- provider: env
38+
literals:
39+
- DATABASE_URL # Reads from $DATABASE_URL
40+
- API_KEY # Reads from $API_KEY
41+
- JWT_SECRET # Reads from $JWT_SECRET
42+
kv:
43+
- CONFIG_JSON # Reads from $CONFIG_JSON and parses as JSON
44+
```
45+
- `literals`: Direct environment variable values
46+
- `kv`: Environment variables containing JSON that gets parsed into key-value pairs
47+
48+
4. **Run Kustomize build with containerized KRM function enabled:**
49+
```bash
50+
kustomize build --enable-alpha-plugins .
51+
```
52+
53+
---
54+
55+
## Why Not YAML Anchors for Env Vars?
56+
- YAML anchors are useful for repeating static YAML blocks, but **they do not help with dynamic environment variable substitution**.
57+
- For dynamic values, set them directly in the `envs:` list as shown above.
58+
- If you want to use exported environment variables from your shell, use the [exec approach](../exec/env/README.md) instead.
59+
60+
---
61+
62+
## Troubleshooting
63+
64+
- If secrets are not found, make sure you set all required env vars in the function config.
65+
- If you see an error about `secretize:local` not found, make sure you built the image as described above.
66+
- If you see errors about `$` or `${VAR}` in the output, make sure you have replaced all placeholders with actual values.
67+
68+
---
69+
70+
## Security Considerations
71+
72+
- **Never hardcode real secrets in your configs for production.**
73+
- Use this approach for local development, testing, or with non-sensitive values.
74+
- For production, consider using a secrets manager (like Vault) and the appropriate Secretize provider.
75+
76+
---
77+
78+
## Reference: Using Host Environment Variables
79+
80+
- If you want to use environment variables exported in your shell, use the [exec KRM function approach](../exec/env/README.md), which can access your host environment directly.

0 commit comments

Comments
 (0)