Skip to content
Merged
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
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
configs
/configs
gitopsctl
.tmp_gitopsctl*
dist/
coverage.out
coverage.html
# Ignore runtime configs in test packages
internal/**/configs
# But don't ignore examples
!examples/configs
346 changes: 78 additions & 268 deletions README.md

Large diffs are not rendered by default.

6,993 changes: 0 additions & 6,993 deletions coverage.html

This file was deleted.

126 changes: 100 additions & 26 deletions docs/SOPS.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,117 @@
# SOPS Secret Management in GitOpsCTL
# SOPS Secret Management

GitOpsCTL provides native support for [SOPS (Secrets Operations)](https://github.com/getsops/sops), allowing you to store encrypted secrets in your Git repository.
GitOpsCTL can decrypt SOPS-encrypted manifests before applying them to Kubernetes. This lets you keep encrypted Secrets in Git while applying plaintext only inside the controller's temporary working directory.

## How it Works
## How Decryption Works

The GitOpsCTL controller automatically detects SOPS-encrypted files in your application's manifest directory. During each synchronization:
During each application sync:

1. The repository is cloned/pulled to a temporary directory.
2. The controller walks through the directory and identifies encrypted files (`.yaml`, `.yml`, `.json`).
3. Files containing SOPS metadata are decrypted in-place using the SOPS library.
4. The decrypted manifests are then applied to the Kubernetes cluster.
5. The temporary directory is cleaned up immediately after synchronization.
1. GitOpsCTL clones or pulls the application repository into a temporary directory.
2. It walks the configured manifest path.
3. It attempts SOPS decryption for `.yaml`, `.yml`, and `.json` files.
4. Files that are encrypted are written back decrypted inside the temporary checkout.
5. The manifest engine renders Helm, Kustomize, or raw YAML.
6. Kubernetes resources are applied.
7. The temporary checkout is removed after reconciliation.

Unencrypted files are left unchanged.

## Supported Providers

Since GitOpsCTL uses the SOPS library directly, it supports all providers that SOPS supports, provided the environment is configured correctly on the host running the controller:
GitOpsCTL uses the SOPS library, so it can use the providers supported by SOPS when the controller environment is configured correctly:

- Age
- PGP
- AWS KMS
- GCP KMS
- Azure Key Vault
- HashiCorp Vault, when configured through SOPS

## Age Example

Create a key:

```bash
age-keygen -o age.key
export SOPS_AGE_KEY_FILE="$PWD/age.key"
```

Create `.sops.yaml`:

```yaml
creation_rules:
- path_regex: .*\.sops\.ya?ml$
age: age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

Encrypt a Kubernetes Secret:

```bash
kubectl create secret generic demo-secret \
--namespace demo \
--from-literal=password=change-me \
--dry-run=client \
-o yaml > secret.yaml

sops --encrypt secret.yaml > secret.sops.yaml
rm secret.yaml
```

Commit `secret.sops.yaml` and `.sops.yaml`. Do not commit `age.key`.

Run GitOpsCTL with access to the key:

```bash
export SOPS_AGE_KEY_FILE=/secure/path/age.key
gitopsctl start
```

## PGP Example

```bash
sops --encrypt --pgp <PGP_FINGERPRINT> secret.yaml > secret.sops.yaml
```

The controller host must have the matching private key available to GPG.

## Cloud KMS Examples

AWS:

- **PGP**: Ensure GPG is installed and the private key is in the keyring.
- **AWS KMS**: Ensure the controller has AWS credentials with `kms:Decrypt` permissions.
- **GCP KMS**: Ensure the controller has GCP credentials with `cloudkms.cryptoKeyVersions.useToDecrypt` permissions.
- **Azure Key Vault**: Ensure the controller is authenticated with Azure.
- **Age**: Ensure the `SOPS_AGE_KEY_FILE` or `SOPS_AGE_KEY` environment variables are set.
```bash
sops --encrypt --kms arn:aws:kms:us-east-1:123456789012:key/<key-id> secret.yaml > secret.sops.yaml
```

## Configuration
GCP:

No special configuration is needed in GitOpsCTL. As long as your files are encrypted with SOPS and the environment where the controller runs has access to the decryption keys, it will work automatically.
```bash
sops --encrypt --gcp-kms projects/<project>/locations/<location>/keyRings/<ring>/cryptoKeys/<key> secret.yaml > secret.sops.yaml
```

### Example: Encrypting a Secret
Azure:

```bash
# Encrypt a secret using a PGP key
sops --encrypt --pgp <YOUR_PGP_FINGERPRINT> secret.yaml > secret.enc.yaml
sops --encrypt --azure-kv https://<vault>.vault.azure.net/keys/<key>/<version> secret.yaml > secret.sops.yaml
```

The controller process must have decrypt permissions through its runtime identity or mounted credentials.

## File Naming

GitOpsCTL does not require a specific encrypted filename suffix. Any `.yaml`, `.yml`, or `.json` file containing SOPS metadata can be decrypted.

Recommended convention:

# Encrypt using AWS KMS
sops --encrypt --kms <YOUR_KMS_ARN> secret.yaml > secret.enc.yaml
```text
secret.sops.yaml
config.sops.json
```

## Security Best Practices
## Safety Checklist

1. **Least Privilege**: Ensure the controller's identity (e.g., IAM Role, ServiceAccount) only has the minimum permissions required to decrypt the specific keys used for your GitOps repo.
2. **Key Rotation**: Regularly rotate your encryption keys. SOPS makes it easy to re-encrypt files with new keys.
3. **No Plaintext in Git**: Never commit decrypted secrets to your Git repository. Always use SOPS to encrypt them before pushing.
- Commit only encrypted secret files.
- Keep decryption keys and cloud credentials outside the repo.
- Run the controller with least-privilege decrypt permissions.
- Use Kubernetes RBAC and `allowedNamespaces` to limit blast radius.
- Rotate keys and re-encrypt secrets when access changes.
- Confirm decrypted files are not produced in your working tree before committing.
86 changes: 0 additions & 86 deletions docs/adr/0001-events-delivery-and-compatibility.md

This file was deleted.

150 changes: 150 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Architecture

GitOpsCTL is an external GitOps controller. It can run outside the Kubernetes clusters it manages and uses kubeconfig files to connect to those clusters.

## System Diagram

```mermaid
flowchart LR
Git["Git repositories"] --> Controller["GitOpsCTL controller"]
Controller --> Renderer["YAML / Kustomize / Helm renderer"]
Renderer --> K8s["Kubernetes API servers"]
Controller --> Store["configs/*.json"]
Controller --> EventBus["Event bus"]
EventBus --> SSE["SSE stream"]
EventBus --> JSONL["JSONL event file"]
EventBus --> Webhook["Webhook sink"]
API["REST API"] --> Controller
TUI["Terminal dashboard"] --> API
CLI["API-backed CLI commands"] --> API
Prom["Prometheus"] --> Metrics["/metrics"]
Metrics --> API
```

## Runtime Components

### CLI

The CLI is built with Cobra. It handles:

- Cluster registration and removal.
- Application registration and removal.
- Status commands.
- API-backed sync, approval, health-check, and dashboard commands.
- Controller startup.

### Controller

The controller owns reconciliation. On `gitopsctl start`, it:

1. Loads `configs/applications.json`.
2. Loads `configs/clusters.json`.
3. Starts the API server.
4. Starts cluster health checking.
5. Starts a reconciliation worker for each registered app.
6. Watches the app config file for changes and reloads application definitions.

### Git Engine

For each app, GitOpsCTL clones or pulls the configured repository and records the latest commit hash. Sync decisions compare:

- Latest discovered hash.
- Last successfully synced hash.
- Approved hash for manual apps.

### Manifest Engine

GitOpsCTL detects the application manifest mode from the configured `path`:

- Helm chart when `Chart.yaml` or `Chart.yml` exists.
- Kustomize overlay when `kustomization.yaml`, `kustomization.yml`, or `Kustomization` exists.
- Raw YAML for recursive `.yaml` and `.yml` files otherwise.

SOPS decryption runs before render/apply.

### Kubernetes Client

The Kubernetes client wraps `client-go` dynamic clients and REST mapping. It:

- Maps YAML resources to Kubernetes API resources.
- Defaults missing namespaces on namespaced resources to `default`.
- Enforces `allowedNamespaces` when configured on the target cluster.
- Creates missing resources and updates existing resources.
- Tracks applied resource metadata for later health checks.

### API Server

The API server exposes:

- Application and cluster management endpoints.
- Sync, approval, and cluster check endpoints.
- Health endpoint.
- SSE event stream.
- Prometheus metrics endpoint.

The dashboard and API-backed CLI commands use this server.

### Event Bus

The event bus fans out integration events to configured sinks:

- In-memory history for API consumers.
- SSE stream for the dashboard.
- JSONL file for audit trails.
- HTTP webhook for external systems.

## Storage Model

GitOpsCTL intentionally uses simple JSON files as its local store:

- `configs/applications.json`
- `configs/clusters.json`

The controller updates status fields in these files. Back up the directory or keep generated config under infrastructure management if you run GitOpsCTL on a server.

## Reconciliation Flow

```mermaid
sequenceDiagram
participant C as Controller
participant G as Git
participant R as Renderer
participant K as Kubernetes
participant S as Store
participant E as Event Bus

C->>G: clone or pull repo
G-->>C: latest commit hash
C->>C: evaluate sync policy
alt manual policy not approved
C->>S: save OutOfSync status
C->>E: emit sync required event
else apply allowed
C->>R: decrypt and render manifests
R-->>C: Kubernetes objects
C->>K: create or update resources
K-->>C: apply results
C->>S: save status, hash, resources
C->>E: emit success or failure
end
```

## Codebase Layout

```text
main.go Entry point
cmd/ Cobra commands
internal/api/ REST API, validation, SSE stream
internal/controller/ Reconciliation loop and command dispatch
internal/core/app/ Application model and persistence
internal/core/cluster/ Cluster model and persistence
internal/core/git/ Git operations
internal/core/k8s/ Kubernetes render, apply, health logic
internal/core/sops/ SOPS decryption helpers
internal/events/ Event envelope, bus, sinks
internal/metrics/ Prometheus metrics
internal/tui/ Bubble Tea dashboard
internal/utils/ CLI rendering helpers
docs/ Documentation
examples/ Example configs and manifests
```
Loading
Loading