Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Test infrastructure (causes permission issues)
test-infrastructure/

# Git files
.git/
.gitignore

# Documentation
*.md
docs/

# Examples (not needed for build)
examples/

# Test files
*_test.go
test/

# IDE files
.vscode/
.idea/
*.swp
*.swo

# OS files
.DS_Store
Thumbs.db

# Build artifacts
bin/
dist/
*.exe
*.dll
*.so
*.dylib

# Logs
*.log

# Temporary files
tmp/
temp/
28 changes: 17 additions & 11 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
on: push
name: CI
on:
push: {}
pull_request: {}
jobs:
checks:
name: run
name: Run
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master

- name: build
uses: cedrickring/[email protected]
- name: Setup Golang
uses: actions/setup-go@v5
with:
args: make build
go-version-file: go.mod
cache: false

- name: test
uses: cedrickring/[email protected]
with:
args: make test
- name: Build
run: |
make build

- name: Test
run: |
make test

- name: publish code coverage results
- name: Publish code coverage results
run: |
go test -race -coverprofile=coverage.txt -covermode=atomic ./...
bash <(curl -s https://codecov.io/bash)
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
22 changes: 22 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Build stage
FROM golang:1.19-alpine AS builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /secretize ./cmd/secretize

# Final stage
FROM alpine:3.18

# Install ca-certificates for HTTPS connections
RUN apk --no-cache add ca-certificates

COPY --from=builder /secretize /usr/local/bin/secretize

# KRM functions run as nobody user
USER nobody

ENTRYPOINT ["/usr/local/bin/secretize"]
117 changes: 112 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
It's like a swiss army knife, but for kubernetes secrets. </i>
<br>
<br>
<img src="https://goreportcard.com/badge/github.com/bbl/secretize" />
<img src="https://github.com/bbl/secretize/workflows/CI/badge.svg">
<a href="https://codecov.io/gh/bbl/secretize">
<img src="https://codecov.io/gh/bbl/secretize/branch/main/graph/badge.svg" />
<img src="https://goreportcard.com/badge/github.com/DevOpsHiveHQ/secretize" />
<img src="https://github.com/DevOpsHiveHQ/secretize/workflows/CI/badge.svg">
<a href="https://codecov.io/gh/DevOpsHiveHQ/secretize">
<img src="https://codecov.io/gh/DevOpsHiveHQ/secretize/branch/main/graph/badge.svg" />
</a>

</p>
Expand All @@ -30,6 +30,31 @@ It is possible to use multiple providers at once.

## Installation

Secretize now supports multiple installation methods:

### Method 1: KRM Function (Recommended)

Secretize supports modern Kubernetes Resource Model (KRM) Functions, which work with Kustomize 4.0.0+:

#### Exec KRM Function
Download the binary and use it directly:
```bash
curl -L https://github.com/DevOpsHiveHQ/secretize/releases/download/v0.0.1/secretize-v0.0.1-linux-amd64.tar.gz | tar -xz
chmod +x secretize
```

#### Containerized KRM Function
Use the Docker image (no installation required):
```yaml
# In your kustomization, reference the container image
annotations:
config.kubernetes.io/function: |
container:
image: ghcr.io/DevOpsHiveHQ/secretize:v0.1.0
```

### Method 2: Legacy Plugin (Deprecated)

Install secretize to your `$XDG_CONFIG_HOME/kustomize/plugin` folder:

1. Export the `XDG_CONFIG_HOME` variable if it's not already set:
Expand All @@ -43,11 +68,67 @@ export XDG_CONFIG_HOME=~/.config
```bash
export SECRETIZE_DIR="$XDG_CONFIG_HOME/kustomize/plugin/secretize/v1/secretgenerator"
mkdir -p "$SECRETIZE_DIR"
curl -L https://github.com/bbl/secretize/releases/download/v0.0.1/secretize-v0.0.1-linux-amd64.tar.gz | tar -xz -C $SECRETIZE_DIR
curl -L https://github.com/DevOpsHiveHQ/secretize/releases/download/v0.0.1/secretize-v0.0.1-linux-amd64.tar.gz | tar -xz -C $SECRETIZE_DIR
```

## Usage

### Using KRM Functions (Recommended)

With KRM functions, add the `config.kubernetes.io/function` annotation to your SecretGenerator:

#### Exec KRM Function Example
```yaml
# secret-generator.yaml
apiVersion: secretize/v1
kind: SecretGenerator
metadata:
name: my-secrets
annotations:
config.kubernetes.io/function: |
exec:
path: ./secretize
sources:
- provider: env
literals:
- DATABASE_URL
```

Run with: `kustomize build --enable-alpha-plugins --enable-exec .`

#### Containerized KRM Function Example
```yaml
# secret-generator.yaml
apiVersion: secretize/v1
kind: SecretGenerator
metadata:
name: my-secrets
annotations:
config.kubernetes.io/function: |
container:
image: ghcr.io/DevOpsHiveHQ/secretize:v0.1.0 #TODO: Upload the image.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove TODO comment and pin container image version.

The TODO comment indicates the image hasn't been uploaded yet, and the version should be pinned to avoid non-deterministic builds.

-        image: ghcr.io/DevOpsHiveHQ/secretize:v0.1.0 #TODO: Upload the image.
+        image: ghcr.io/DevOpsHiveHQ/secretize:v0.1.0
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
image: ghcr.io/DevOpsHiveHQ/secretize:v0.1.0 #TODO: Upload the image.
image: ghcr.io/DevOpsHiveHQ/secretize:v0.1.0
🤖 Prompt for AI Agents
In README.md at line 109, remove the TODO comment indicating the image upload is
pending and ensure the container image version is explicitly pinned to a
specific, stable version to prevent non-deterministic builds.

sources:
- provider: env
literals:
- DATABASE_URL
```

Run with: `kustomize build --enable-alpha-plugins .`

### Legacy Plugin Usage

For the legacy plugin, use without annotations:

```yaml
# kustomization.yaml
generators:
- secret-generator.yaml
```

Run with: `kustomize build --enable-alpha-plugins .`

### Provider Configuration

All providers can generate two types of secrets: `literals` and `kv` (Key-Value secrets).
Literal secrets simply generate a single string output, while KV secrets will output with a dictionary of the key-value pairs.

Expand Down Expand Up @@ -279,3 +360,29 @@ data:
secret_key_1: c2VjcmV0X3ZhbHVlXzE=
secret_key_2: c2VjcmV0X3ZhbHVlXzI=
```

## Examples

Check out the [examples](./examples) directory for complete working examples:

- [Legacy Plugin Example](./examples/legacy) - Traditional Kustomize plugin approach
- [Exec KRM Function Example](./examples/exec) - Modern exec-based KRM function
- [Containerized KRM Function Example](./examples/docker) - Docker-based KRM function

## Test Infrastructure

For comprehensive testing with real secret stores, see the [test-infrastructure](./test-infrastructure/) directory which provides:

- **HashiCorp Vault** setup with test secrets
- **AWS Secrets Manager** emulation via LocalStack
- **Kubernetes** cluster with test secrets
- **Automated testing** for all providers and execution modes

```bash
cd test-infrastructure
./test-all-providers.sh
```

## Documentation

For detailed documentation on KRM Functions support, see [KRM Functions Documentation](./docs/KRM_FUNCTIONS.md).
94 changes: 86 additions & 8 deletions cmd/secretize/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,42 @@ package main

import (
"fmt"
"github.com/bbl/secretize/pkg/generator"
"github.com/bbl/secretize/pkg/utils"
log "github.com/sirupsen/logrus"
"io/ioutil"

"os"
"path/filepath"

"github.com/DevOpsHiveHQ/secretize/pkg/generator"
"github.com/DevOpsHiveHQ/secretize/pkg/utils"
log "github.com/sirupsen/logrus"

"sigs.k8s.io/kustomize/kyaml/fn/framework"
"sigs.k8s.io/kustomize/kyaml/fn/framework/command"
"sigs.k8s.io/kustomize/kyaml/yaml"
)

func main() {
// Check if running as KRM function (no args or stdin has content)
if len(os.Args) == 1 || isKRMFunction() {
runAsKRMFunction()
} else {
// Legacy mode
runLegacyMode()
}
}

// isKRMFunction checks if stdin has content (indicating KRM function mode)
func isKRMFunction() bool {
stat, _ := os.Stdin.Stat()
return (stat.Mode() & os.ModeCharDevice) == 0
}

// runLegacyMode runs the original secretize behavior
func runLegacyMode() {
if len(os.Args) < 2 {
log.Fatal(
"No argument passed, use `secretize /path/to/generator-config.yaml`")
log.Fatal("No argument passed, use `secretize /path/to/generator-config.yaml`")
}

filename, _ := filepath.Abs(os.Args[1])
yamlFile, err := ioutil.ReadFile(filename)
yamlFile, err := os.ReadFile(filename)
utils.FatalErrCheck(err)

secretGenerator, err := generator.ParseConfig(yamlFile)
Expand All @@ -33,3 +51,63 @@ func main() {
utils.FatalErrCheck(err)
fmt.Println(out)
}

// SecretGeneratorProcessor implements the KRM function processor
type SecretGeneratorProcessor struct{}

// Process implements the framework.ResourceListProcessor interface
func (p SecretGeneratorProcessor) Process(rl *framework.ResourceList) error {
// Get the function config
if rl.FunctionConfig == nil {
return fmt.Errorf("no function config provided")
}

// Convert function config to YAML string
fcString, err := rl.FunctionConfig.String()
if err != nil {
return fmt.Errorf("failed to marshal function config: %w", err)
}

// Parse as SecretGenerator
secretGenerator, err := generator.ParseConfig([]byte(fcString))
if err != nil {
return fmt.Errorf("failed to parse config: %w", err)
}

// Generate secrets
secrets, err := secretGenerator.FetchSecrets(generator.ProviderRegistry)
if err != nil {
return fmt.Errorf("failed to fetch secrets: %w", err)
}

// Generate the secret resource
secret := secretGenerator.Generate(secrets)
secretYaml, err := secret.ToYamlStr()
if err != nil {
return fmt.Errorf("failed to convert secret to yaml: %w", err)
}

// Parse the generated secret as RNode
rNode, err := yaml.Parse(secretYaml)
if err != nil {
return fmt.Errorf("failed to parse generated secret: %w", err)
}

// Append to items
rl.Items = append(rl.Items, rNode)

return nil
}

// runAsKRMFunction runs secretize as a KRM function
func runAsKRMFunction() {
processor := SecretGeneratorProcessor{}
cmd := command.Build(processor, command.StandaloneDisabled, false)

// Add dockerfile generation support
command.AddGenerateDockerfile(cmd)

if err := cmd.Execute(); err != nil {
os.Exit(1)
}
}
Loading