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.
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()
Copy link
Preview

Copilot AI Jul 18, 2025

Choose a reason for hiding this comment

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

[nitpick] Error from os.Stdin.Stat() is ignored. While this might be intentional for detecting stdin availability, consider handling potential errors or adding a comment explaining why the error is safely ignored.

Suggested change
stat, _ := os.Stdin.Stat()
stat, err := os.Stdin.Stat()
if err != nil {
log.Warnf("Failed to stat stdin: %v", err)
return false
}

Copilot uses AI. Check for mistakes.

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