Skip to content
Draft
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
74 changes: 74 additions & 0 deletions test/library/encryption/kms/k8s_mock_kms_plugin_deployer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package kms

import (
"context"
"testing"
"time"

"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)

// getKubeClient returns a Kubernetes client for testing.
func getKubeClient(t *testing.T) kubernetes.Interface {
t.Helper()

loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
configOverrides := &clientcmd.ConfigOverrides{}
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides)

config, err := kubeConfig.ClientConfig()
if err != nil {
t.Fatalf("Failed to get kubeconfig: %v", err)
}

client, err := kubernetes.NewForConfig(config)
if err != nil {
t.Fatalf("Failed to create kubernetes client: %v", err)
}

return client
}

// TestDeployUpstreamMockKMSPlugin tests deploying the KMS plugin.
func TestDeployUpstreamMockKMSPlugin(t *testing.T) {
if testing.Short() {
t.Skip("Skipping KMS deploy test in short mode")
}

kubeClient := getKubeClient(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()

namespace := WellKnownUpstreamMockKMSPluginNamespace
image := WellKnownUpstreamMockKMSPluginImage

t.Logf("Deploying KMS plugin with namespace=%s, image=%s", namespace, image)

// Deploy and get cleanup function
cleanup := DeployUpstreamMockKMSPlugin(ctx, t, kubeClient, namespace, image)
defer cleanup()

t.Log("KMS plugin deployed successfully!")
}

// TestDeployUpstreamMockKMSPluginNoCleanup deploys the KMS plugin without cleanup.
func TestDeployUpstreamMockKMSPluginNoCleanup(t *testing.T) {
if testing.Short() {
t.Skip("Skipping KMS deploy test in short mode")
}

kubeClient := getKubeClient(t)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()

namespace := WellKnownUpstreamMockKMSPluginNamespace
image := WellKnownUpstreamMockKMSPluginImage

t.Logf("Deploying KMS plugin with namespace=%s, image=%s (no cleanup)", namespace, image)

// Deploy without calling cleanup
_ = DeployUpstreamMockKMSPlugin(ctx, t, kubeClient, namespace, image)

t.Log("KMS plugin deployed successfully! Resources left in cluster.")
}
31 changes: 31 additions & 0 deletions test/library/encryption/kms/vault-plugin/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Build stage
FROM golang:1.22-alpine AS builder

WORKDIR /app

# Install git for go mod download
RUN apk add --no-cache git ca-certificates

# Copy source files
COPY go.mod main.go ./

# Download dependencies and generate go.sum
RUN go mod tidy

# Build the binary
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
-ldflags="-w -s" \
-o /vault-kms-plugin .

# Runtime stage
FROM alpine:3.19

RUN apk add --no-cache ca-certificates

COPY --from=builder /vault-kms-plugin /usr/local/bin/vault-kms-plugin

# Create socket directory
RUN mkdir -p /var/run/kmsplugin

ENTRYPOINT ["/usr/local/bin/vault-kms-plugin"]
CMD ["-listen-addr=unix:///var/run/kmsplugin/kms.sock"]
139 changes: 139 additions & 0 deletions test/library/encryption/kms/vault-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Vault KMS v2 Plugin Deployer

This directory contains scripts for deploying a Vault-based KMS v2 plugin on OpenShift.

## Architecture

```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ API Server │────▶│ Vault KMS │────▶│ HashiCorp │
│ (kube-api) │ │ Plugin │ │ Vault │
│ │ │ (DaemonSet) │ │ (Transit) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ │ │
Encryption Unix Socket AppRole Auth
Config /var/run/kmsplugin Transit Engine
```

## Prerequisites

1. **HashiCorp Vault** deployed with Transit engine enabled
2. **AppRole credentials** (Role ID and Secret ID)
3. **oc CLI** logged into OpenShift cluster

## Quick Start

### Step 1: Setup Vault (if not already done)

Use the Vault setup script from [gangwgr/kms-setup](https://github.com/gangwgr/kms-setup):

```bash
curl -sL https://raw.githubusercontent.com/gangwgr/kms-setup/main/vault-kms-setup/setup-vault-transit-kms.sh | bash
```

This will output the AppRole credentials needed for the KMS plugin.

### Step 2: Deploy KMS Plugin

```bash
./deploy-vault-kms-plugin.sh \
--vault-addr http://vault.vault.svc.cluster.local:8200 \
--role-id <ROLE_ID> \
--secret-id <SECRET_ID>
```

Or using environment variables:

```bash
export VAULT_ADDR="http://vault.vault.svc.cluster.local:8200"
export VAULT_ROLE_ID="abc123..."
export VAULT_SECRET_ID="xyz789..."
./deploy-vault-kms-plugin.sh
```

### Step 3: Check Status

```bash
./deploy-vault-kms-plugin.sh --status
```

### Step 4: Cleanup

```bash
./deploy-vault-kms-plugin.sh --cleanup
```

## Configuration Options

| Option | Environment Variable | Default | Description |
|--------|---------------------|---------|-------------|
| `--vault-addr` | `VAULT_ADDR` | Auto-detect | Vault server address |
| `--vault-key` | `VAULT_TRANSIT_KEY` | `kubernetes-encryption` | Transit key name |
| `--role-id` | `VAULT_ROLE_ID` | Required | AppRole Role ID |
| `--secret-id` | `VAULT_SECRET_ID` | Required | AppRole Secret ID |
| `--namespace` | `KMS_NAMESPACE` | `openshift-kms-plugin` | Plugin namespace |
| `--image` | `KMS_PLUGIN_IMAGE` | `quay.io/openshifttest/vault-kms-plugin:latest` | Plugin image |

## What Gets Deployed

1. **Namespace**: `openshift-kms-plugin` (with privileged pod security)
2. **ServiceAccount**: `vault-kms-plugin`
3. **RoleBinding**: Grants privileged SCC access
4. **Secret**: Vault AppRole credentials
5. **ConfigMap**: Plugin and Vault configuration
6. **DaemonSet**: KMS plugin running on control-plane nodes

## Socket Path

The KMS plugin listens on: `unix:///var/run/kmsplugin/kms.sock`

This path is mounted as a hostPath volume so the API server can access it.

## Differences from Mock KMS Plugin

| Feature | Mock KMS Plugin | Vault KMS Plugin |
|---------|-----------------|------------------|
| Backend | SoftHSM (local) | HashiCorp Vault |
| External Dependencies | None | Vault server |
| Key Management | Local PKCS11 | Vault Transit |
| Use Case | Testing | Production-like testing |
| Authentication | None | AppRole |

## Troubleshooting

### Check plugin logs

```bash
oc logs -n openshift-kms-plugin -l app=vault-kms-plugin
```

### Verify socket exists

```bash
oc exec -n openshift-kms-plugin <pod-name> -- ls -la /var/run/kmsplugin/
```

### Test Vault connectivity from plugin pod

```bash
oc exec -n openshift-kms-plugin <pod-name> -- curl -s $VAULT_ADDR/v1/sys/health
```

### Check AppRole authentication

```bash
oc exec -n openshift-kms-plugin <pod-name> -- \
curl -s -X POST $VAULT_ADDR/v1/auth/approle/login \
-d '{"role_id":"<ROLE_ID>","secret_id":"<SECRET_ID>"}'
```

## Files

- `deploy-vault-kms-plugin.sh` - Main deployment script
- `README.md` - This documentation

## Related

- [Mock KMS Plugin](../k8s-mock-plugin/) - Simple mock plugin using SoftHSM
- [Vault Setup Script](https://github.com/gangwgr/kms-setup) - Sets up Vault Transit engine
134 changes: 134 additions & 0 deletions test/library/encryption/kms/vault-plugin/build-image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#!/bin/bash

# Build Vault KMS Plugin Image
# This script builds the Vault KMS v2 plugin container image

set -euo pipefail

# Configuration
IMAGE_NAME="${IMAGE_NAME:-vault-kms-plugin}"
IMAGE_TAG="${IMAGE_TAG:-quay.io/openshifttest/vault-kms-plugin:latest}"
CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-podman}"

# Colors
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'

log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}

log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}

log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}

usage() {
cat <<EOF
Usage: $0 [OPTIONS]

Build the Vault KMS v2 plugin container image.

Options:
--tag TAG Image tag (default: quay.io/openshifttest/vault-kms-plugin:latest)
--runtime RUNTIME Container runtime: docker or podman (default: podman)
--push Push image after building
--help Show this help message

Examples:
# Build image
$0

# Build and push
$0 --push

# Build with custom tag
$0 --tag myregistry/vault-kms-plugin:v1.0
EOF
}

# Parse arguments
PUSH=false

while [[ $# -gt 0 ]]; do
case $1 in
--tag)
IMAGE_TAG="$2"
shift 2
;;
--runtime)
CONTAINER_RUNTIME="$2"
shift 2
;;
--push)
PUSH=true
shift
;;
--help|-h)
usage
exit 0
;;
*)
log_error "Unknown option: $1"
usage
exit 1
;;
esac
done

# Check container runtime
if ! command -v ${CONTAINER_RUNTIME} &> /dev/null; then
log_error "${CONTAINER_RUNTIME} is not installed"
exit 1
fi

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "${SCRIPT_DIR}"

log_info "Building Vault KMS plugin image..."
log_info "Image tag: ${IMAGE_TAG}"
log_info "Container runtime: ${CONTAINER_RUNTIME}"

# Download dependencies
log_info "Downloading Go dependencies..."
go mod download 2>/dev/null || go mod tidy

# Build image
log_info "Building container image..."
${CONTAINER_RUNTIME} build \
--platform linux/amd64 \
-t "${IMAGE_TAG}" \
-f Dockerfile \
.

log_info "Image built successfully: ${IMAGE_TAG}"

# Push if requested
if [ "$PUSH" = true ]; then
log_info "Pushing image to registry..."
${CONTAINER_RUNTIME} push "${IMAGE_TAG}"
log_info "Image pushed successfully!"
fi

echo ""
log_info "=========================================="
log_info "Build Complete!"
log_info "=========================================="
echo ""
echo "Image: ${IMAGE_TAG}"
echo ""
echo "To push manually:"
echo " ${CONTAINER_RUNTIME} push ${IMAGE_TAG}"
echo ""
echo "To run locally (for testing):"
echo " ${CONTAINER_RUNTIME} run -it --rm ${IMAGE_TAG} --help"
echo ""
echo "To deploy on OpenShift:"
echo " ./deploy-vault-kms-plugin.sh --image ${IMAGE_TAG} \\"
echo " --vault-addr http://vault.vault.svc:8200 \\"
echo " --role-id <ROLE_ID> --secret-id <SECRET_ID>"
Loading