602 lines
15 KiB
Markdown
602 lines
15 KiB
Markdown
# Wild Cloud Configuration System
|
|
|
|
Wild Cloud uses a comprehensive configuration management system that handles both non-sensitive configuration data and sensitive secrets through separate files and commands. The system supports YAML path-based access, template processing, and environment-specific customization.
|
|
|
|
## Configuration Architecture
|
|
|
|
### Core Components
|
|
|
|
1. **`config.yaml`** - Main configuration file for non-sensitive settings
|
|
2. **`secrets.yaml`** - Encrypted/protected storage for sensitive data
|
|
3. **`.wildcloud/`** - Project marker and cache directory
|
|
4. **`env.sh`** - Environment setup and path configuration
|
|
5. **Template System** - gomplate-based dynamic configuration processing
|
|
|
|
### File Structure of a Wild Cloud Project
|
|
|
|
```
|
|
your-cloud-directory/
|
|
├── .wildcloud/ # Project marker and cache
|
|
│ ├── cache/ # Downloaded templates and temporary files
|
|
│ └── logs/ # Operation logs
|
|
├── config.yaml # Main configuration (tracked in git)
|
|
├── secrets.yaml # Sensitive data (NOT tracked in git, 600 perms)
|
|
├── env.sh # Environment setup (auto-generated)
|
|
├── apps/ # Deployed application configurations
|
|
├── setup/ # Infrastructure setup files
|
|
└── docs/ # Project documentation
|
|
```
|
|
|
|
## Configuration File (`config.yaml`)
|
|
|
|
### Structure and Organization
|
|
|
|
The configuration file uses a hierarchical YAML structure for organizing settings:
|
|
|
|
```yaml
|
|
# Cloud-wide settings
|
|
cloud:
|
|
domain: "example.com"
|
|
email: "admin@example.com"
|
|
timezone: "America/New_York"
|
|
|
|
# Cluster infrastructure settings
|
|
cluster:
|
|
name: "wild-cluster"
|
|
nodeCount: 3
|
|
network:
|
|
subnet: "192.168.1.0/24"
|
|
gateway: "192.168.1.1"
|
|
dnsServer: "192.168.1.50"
|
|
metallbPool: "192.168.1.80-89"
|
|
controlPlaneVip: "192.168.1.90"
|
|
nodes:
|
|
control-1:
|
|
ip: "192.168.1.91"
|
|
mac: "00:11:22:33:44:55"
|
|
interface: "eth0"
|
|
disk: "/dev/sda"
|
|
control-2:
|
|
ip: "192.168.1.92"
|
|
mac: "00:11:22:33:44:56"
|
|
interface: "eth0"
|
|
disk: "/dev/sda"
|
|
|
|
# Application-specific settings
|
|
apps:
|
|
ghost:
|
|
domain: "blog.example.com"
|
|
image: "ghost:5.0.0"
|
|
storage: "10Gi"
|
|
timezone: "UTC"
|
|
namespace: "ghost"
|
|
immich:
|
|
domain: "photos.example.com"
|
|
serverImage: "ghcr.io/immich-app/immich-server:release"
|
|
storage: "250Gi"
|
|
namespace: "immich"
|
|
|
|
# Service configurations
|
|
services:
|
|
traefik:
|
|
replicas: 2
|
|
dashboard: true
|
|
longhorn:
|
|
defaultReplicas: 3
|
|
storageClass: "longhorn"
|
|
```
|
|
|
|
### Configuration Commands
|
|
|
|
**Reading Configuration Values**:
|
|
```bash
|
|
# Read simple values
|
|
wild-config cloud.domain # "example.com"
|
|
wild-config cluster.name # "wild-cluster"
|
|
|
|
# Read nested values
|
|
wild-config apps.ghost.domain # "blog.example.com"
|
|
wild-config cluster.nodes.control-1.ip # "192.168.1.91"
|
|
|
|
# Check if key exists
|
|
wild-config --check apps.newapp.domain # Returns exit code 0/1
|
|
```
|
|
|
|
**Writing Configuration Values**:
|
|
```bash
|
|
# Set simple values
|
|
wild-config-set cloud.domain "newdomain.com"
|
|
wild-config-set cluster.nodeCount 5
|
|
|
|
# Set nested values
|
|
wild-config-set apps.ghost.storage "20Gi"
|
|
wild-config-set cluster.nodes.worker-1.ip "192.168.1.94"
|
|
|
|
# Set complex values (JSON format)
|
|
wild-config-set apps.ghost '{"domain":"blog.com","storage":"50Gi"}'
|
|
```
|
|
|
|
### Configuration Sections
|
|
|
|
#### Cloud Settings (`cloud.*`)
|
|
Global settings that affect the entire Wild Cloud deployment:
|
|
|
|
```yaml
|
|
cloud:
|
|
domain: "example.com" # Primary domain for services
|
|
email: "admin@example.com" # Contact email for certificates
|
|
timezone: "America/New_York" # Default timezone for services
|
|
backupLocation: "s3://backup" # Backup storage location
|
|
monitoring: true # Enable monitoring services
|
|
```
|
|
|
|
#### Cluster Settings (`cluster.*`)
|
|
Infrastructure and node configuration:
|
|
|
|
```yaml
|
|
cluster:
|
|
name: "production-cluster"
|
|
version: "v1.28.0"
|
|
network:
|
|
subnet: "10.0.0.0/16" # Cluster network range
|
|
serviceCIDR: "10.96.0.0/12" # Service network range
|
|
podCIDR: "10.244.0.0/16" # Pod network range
|
|
nodes:
|
|
control-1:
|
|
ip: "10.0.0.10"
|
|
role: "controlplane"
|
|
taints: []
|
|
worker-1:
|
|
ip: "10.0.0.20"
|
|
role: "worker"
|
|
labels:
|
|
node-type: "compute"
|
|
```
|
|
|
|
#### Application Settings (`apps.*`)
|
|
Per-application configuration that overrides defaults from app manifests:
|
|
|
|
```yaml
|
|
apps:
|
|
postgresql:
|
|
storage: "100Gi"
|
|
maxConnections: 200
|
|
sharedBuffers: "256MB"
|
|
redis:
|
|
memory: "1Gi"
|
|
persistence: true
|
|
ghost:
|
|
domain: "blog.example.com"
|
|
theme: "casper"
|
|
storage: "10Gi"
|
|
replicas: 2
|
|
```
|
|
|
|
## Secrets Management (`secrets.yaml`)
|
|
|
|
### Security Model
|
|
|
|
The `secrets.yaml` file stores all sensitive data with the following security measures:
|
|
|
|
- **File Permissions**: Automatically set to 600 (owner read/write only)
|
|
- **Git Exclusion**: Included in `.gitignore` by default
|
|
- **Encryption Support**: Can be encrypted at rest using tools like `age` or `gpg`
|
|
- **Access Control**: Only Wild Cloud commands can read/write secrets
|
|
|
|
### Secret Structure
|
|
|
|
```yaml
|
|
# Generated cluster secrets
|
|
cluster:
|
|
talos:
|
|
secrets: "base64-encoded-cluster-secrets"
|
|
adminKey: "talos-admin-private-key"
|
|
kubernetes:
|
|
adminToken: "k8s-admin-service-account-token"
|
|
|
|
# Application secrets
|
|
apps:
|
|
postgresql:
|
|
rootPassword: "randomly-generated-32-char-string"
|
|
replicationPassword: "randomly-generated-32-char-string"
|
|
ghost:
|
|
dbPassword: "randomly-generated-password"
|
|
adminPassword: "user-set-password"
|
|
jwtSecret: "randomly-generated-jwt-secret"
|
|
immich:
|
|
dbPassword: "randomly-generated-password"
|
|
dbUrl: "postgresql://immich:password@postgres:5432/immich"
|
|
jwtSecret: "jwt-signing-key"
|
|
|
|
# External service credentials
|
|
external:
|
|
cloudflare:
|
|
apiToken: "cloudflare-dns-api-token"
|
|
letsencrypt:
|
|
email: "admin@example.com"
|
|
backup:
|
|
s3AccessKey: "backup-s3-access-key"
|
|
s3SecretKey: "backup-s3-secret-key"
|
|
```
|
|
|
|
### Secret Commands
|
|
|
|
**Reading Secrets**:
|
|
```bash
|
|
# Read secret values
|
|
wild-secret apps.postgresql.rootPassword
|
|
wild-secret cluster.kubernetes.adminToken
|
|
|
|
# Check if secret exists
|
|
wild-secret --check apps.newapp.apiKey
|
|
```
|
|
|
|
**Writing Secrets**:
|
|
```bash
|
|
# Set specific secret value
|
|
wild-secret-set apps.ghost.adminPassword "my-secure-password"
|
|
|
|
# Generate random secret (if no value provided)
|
|
wild-secret-set apps.newapp.apiKey # Generates 32-char base64 string
|
|
|
|
# Set complex secret (JSON format)
|
|
wild-secret-set apps.database '{"user":"admin","password":"secret"}'
|
|
```
|
|
|
|
### Automatic Secret Generation
|
|
|
|
When you run `wild-app-add`, Wild Cloud automatically generates required secrets:
|
|
|
|
1. **Reads App Manifest**: Identifies `requiredSecrets` list
|
|
2. **Checks Existing Secrets**: Never overwrites existing values
|
|
3. **Generates Missing Secrets**: Creates secure random values
|
|
4. **Updates secrets.yaml**: Adds new secrets with proper structure
|
|
|
|
**Example App Manifest**:
|
|
```yaml
|
|
name: ghost
|
|
requiredSecrets:
|
|
- apps.ghost.dbPassword # Auto-generated if missing
|
|
- apps.ghost.jwtSecret # Auto-generated if missing
|
|
- apps.postgresql.password # Auto-generated if missing (dependency)
|
|
```
|
|
|
|
**Resulting secrets.yaml**:
|
|
```yaml
|
|
apps:
|
|
ghost:
|
|
dbPassword: "aB3kL9mN2pQ7rS8tU1vW4xY5zA6bC0dE"
|
|
jwtSecret: "jF2gH5iJ8kL1mN4oP7qR0sT3uV6wX9yZ"
|
|
postgresql:
|
|
password: "eE8fF1gG4hH7iI0jJ3kK6lL9mM2nN5oO"
|
|
```
|
|
|
|
## Template System
|
|
|
|
### gomplate Integration
|
|
|
|
Wild Cloud uses [gomplate](https://gomplate.ca/) for dynamic configuration processing, allowing templates to access both configuration and secrets:
|
|
|
|
```yaml
|
|
# Template example (before processing)
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: ghost-config
|
|
namespace: {{ .apps.ghost.namespace }}
|
|
data:
|
|
url: "https://{{ .apps.ghost.domain }}"
|
|
timezone: "{{ .apps.ghost.timezone | default .cloud.timezone }}"
|
|
database_host: "{{ .apps.postgresql.hostname }}"
|
|
# Conditionals
|
|
{{- if .apps.ghost.enableSSL }}
|
|
ssl_enabled: "true"
|
|
{{- end }}
|
|
# Loops
|
|
allowed_domains: |
|
|
{{- range .apps.ghost.allowedDomains }}
|
|
- {{ . }}
|
|
{{- end }}
|
|
```
|
|
|
|
### Template Processing Commands
|
|
|
|
**Process Single Template**:
|
|
```bash
|
|
# From stdin
|
|
cat template.yaml | wild-compile-template > output.yaml
|
|
|
|
# With custom context
|
|
echo "domain: {{ .cloud.domain }}" | wild-compile-template
|
|
```
|
|
|
|
**Process Template Directory**:
|
|
```bash
|
|
# Recursively process all templates
|
|
wild-compile-template-dir source-dir output-dir
|
|
|
|
# Clean destination first
|
|
wild-compile-template-dir --clean source-dir output-dir
|
|
```
|
|
|
|
### Template Context
|
|
|
|
Templates have access to the complete configuration and secrets context:
|
|
|
|
```go
|
|
// Available template variables
|
|
.cloud.* // All cloud configuration
|
|
.cluster.* // All cluster configuration
|
|
.apps.* // All application configuration
|
|
.services.* // All service configuration
|
|
|
|
// Special functions
|
|
.cloud.domain // Primary domain
|
|
default "fallback" // Default value if key missing
|
|
env "VAR_NAME" // Environment variable
|
|
file "path/to/file" // File contents
|
|
```
|
|
|
|
**Template Examples**:
|
|
```yaml
|
|
# Basic variable substitution
|
|
domain: {{ .apps.myapp.domain }}
|
|
|
|
# Default values
|
|
timezone: {{ .apps.myapp.timezone | default .cloud.timezone }}
|
|
|
|
# Conditionals
|
|
{{- if .apps.myapp.enableFeature }}
|
|
feature_enabled: true
|
|
{{- else }}
|
|
feature_enabled: false
|
|
{{- end }}
|
|
|
|
# Lists and iteration
|
|
allowed_hosts:
|
|
{{- range .apps.myapp.allowedHosts }}
|
|
- {{ . }}
|
|
{{- end }}
|
|
|
|
# Complex expressions
|
|
replicas: {{ if eq .cluster.environment "production" }}3{{ else }}1{{ end }}
|
|
```
|
|
|
|
## Environment Setup
|
|
|
|
### Environment Detection
|
|
|
|
Wild Cloud automatically detects and configures the environment through several mechanisms:
|
|
|
|
**Project Detection**:
|
|
- Searches for `.wildcloud` directory in current or parent directories
|
|
- Sets `WC_HOME` to the directory containing `.wildcloud`
|
|
- Fails if no Wild Cloud project found
|
|
|
|
**Repository Detection**:
|
|
- Locates Wild Cloud repository (source code)
|
|
- Sets `WC_ROOT` to repository location
|
|
- Used for accessing app templates and setup scripts
|
|
|
|
### Environment Variables
|
|
|
|
**Key Environment Variables**:
|
|
```bash
|
|
WC_HOME="/path/to/your-cloud" # Your cloud directory
|
|
WC_ROOT="/path/to/wild-cloud-repo" # Wild Cloud repository
|
|
PATH="$WC_ROOT/bin:$PATH" # Wild Cloud commands available
|
|
KUBECONFIG="$WC_HOME/.kube/config" # Kubernetes configuration
|
|
TALOSCONFIG="$WC_HOME/.talos/config" # Talos configuration
|
|
```
|
|
|
|
**Environment Setup Script** (`env.sh`):
|
|
```bash
|
|
#!/bin/bash
|
|
# Auto-generated environment setup
|
|
|
|
export WC_HOME="/home/user/my-cloud"
|
|
export WC_ROOT="/opt/wild-cloud"
|
|
export PATH="$WC_ROOT/bin:$PATH"
|
|
export KUBECONFIG="$WC_HOME/.kubeconfig"
|
|
export TALOSCONFIG="$WC_HOME/setup/cluster-nodes/generated/talosconfig"
|
|
|
|
# Source this file to set up Wild Cloud environment
|
|
# source env.sh
|
|
```
|
|
|
|
### Common Script Pattern
|
|
|
|
Most Wild Cloud scripts follow this initialization pattern:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
set -e
|
|
set -o pipefail
|
|
|
|
# Initialize Wild Cloud environment
|
|
if [ -z "${WC_ROOT}" ]; then
|
|
print "WC_ROOT is not set."
|
|
exit 1
|
|
else
|
|
source "${WC_ROOT}/scripts/common.sh"
|
|
init_wild_env
|
|
fi
|
|
|
|
# Script logic here...
|
|
```
|
|
|
|
## Configuration Validation
|
|
|
|
### Schema Validation
|
|
|
|
Wild Cloud validates configuration against expected schemas:
|
|
|
|
**Cluster Configuration Validation**:
|
|
- Node IP addresses are valid and unique
|
|
- Network ranges don't overlap
|
|
- Required fields are present
|
|
- Hardware specifications meet minimums
|
|
|
|
**Application Configuration Validation**:
|
|
- Domain names are valid DNS names
|
|
- Storage sizes use valid Kubernetes formats
|
|
- Image references are valid container images
|
|
- Dependencies are satisfied
|
|
|
|
### Validation Commands
|
|
|
|
```bash
|
|
# Validate current configuration
|
|
wild-config --validate
|
|
|
|
# Check specific configuration sections
|
|
wild-config --validate --section cluster
|
|
wild-config --validate --section apps.ghost
|
|
|
|
# Test template compilation
|
|
wild-compile-template --validate < template.yaml
|
|
```
|
|
|
|
## Configuration Best Practices
|
|
|
|
### Organization
|
|
|
|
**Hierarchical Structure**:
|
|
- Group related settings under common prefixes
|
|
- Use consistent naming conventions
|
|
- Keep application configs under `apps.*`
|
|
- Separate infrastructure from application settings
|
|
|
|
**Documentation**:
|
|
```yaml
|
|
# Document complex configurations
|
|
cluster:
|
|
# Node configuration - update IPs after hardware changes
|
|
nodes:
|
|
control-1:
|
|
ip: "192.168.1.91" # Main control plane node
|
|
interface: "eth0" # Primary network interface
|
|
```
|
|
|
|
### Security
|
|
|
|
**Configuration Security**:
|
|
- Never store secrets in `config.yaml`
|
|
- Use `wild-secret-set` for all sensitive data
|
|
- Regularly rotate generated secrets
|
|
- Backup `secrets.yaml` securely
|
|
|
|
**Access Control**:
|
|
```bash
|
|
# Ensure proper permissions
|
|
chmod 600 secrets.yaml
|
|
chmod 644 config.yaml
|
|
|
|
# Restrict directory access
|
|
chmod 755 your-cloud-directory
|
|
chmod 700 .wildcloud/
|
|
```
|
|
|
|
### Version Control
|
|
|
|
**Git Integration**:
|
|
```gitignore
|
|
# .gitignore for Wild Cloud projects
|
|
secrets.yaml # Never commit secrets
|
|
.wildcloud/cache/ # Temporary files
|
|
.wildcloud/logs/ # Operation logs
|
|
setup/cluster-nodes/generated/ # Generated cluster configs
|
|
.kube/ # Kubernetes configs
|
|
.talos/ # Talos configs
|
|
```
|
|
|
|
**Configuration Changes**:
|
|
- Commit `config.yaml` changes with descriptive messages
|
|
- Tag major configuration changes
|
|
- Use branches for experimental configurations
|
|
- Document configuration changes in commit messages
|
|
|
|
### Backup and Recovery
|
|
|
|
**Configuration Backup**:
|
|
```bash
|
|
# Backup configuration and secrets
|
|
wild-backup --home-only
|
|
|
|
# Export configuration for disaster recovery
|
|
cp config.yaml config-backup-$(date +%Y%m%d).yaml
|
|
cp secrets.yaml secrets-backup-$(date +%Y%m%d).yaml.gpg # Encrypt first
|
|
```
|
|
|
|
**Recovery Process**:
|
|
1. Restore `config.yaml` from backup
|
|
2. Decrypt and restore `secrets.yaml`
|
|
3. Re-run `wild-setup` if needed
|
|
4. Validate configuration with `wild-config --validate`
|
|
|
|
## Advanced Configuration
|
|
|
|
### Multi-Environment Setup
|
|
|
|
**Development Environment**:
|
|
```yaml
|
|
cloud:
|
|
domain: "dev.example.com"
|
|
cluster:
|
|
name: "dev-cluster"
|
|
nodeCount: 1
|
|
apps:
|
|
ghost:
|
|
domain: "blog.dev.example.com"
|
|
replicas: 1
|
|
```
|
|
|
|
**Production Environment**:
|
|
```yaml
|
|
cloud:
|
|
domain: "example.com"
|
|
cluster:
|
|
name: "prod-cluster"
|
|
nodeCount: 5
|
|
apps:
|
|
ghost:
|
|
domain: "blog.example.com"
|
|
replicas: 3
|
|
```
|
|
|
|
### Configuration Inheritance
|
|
|
|
**Base Configuration**:
|
|
```yaml
|
|
# config.base.yaml
|
|
cloud:
|
|
timezone: "UTC"
|
|
email: "admin@example.com"
|
|
apps:
|
|
postgresql:
|
|
storage: "10Gi"
|
|
```
|
|
|
|
**Environment-Specific Override**:
|
|
```yaml
|
|
# config.prod.yaml (merged with base)
|
|
apps:
|
|
postgresql:
|
|
storage: "100Gi" # Override for production
|
|
replicas: 3 # Additional production setting
|
|
```
|
|
|
|
### Dynamic Configuration
|
|
|
|
**Runtime Configuration Updates**:
|
|
```bash
|
|
# Update configuration without restart
|
|
wild-config-set apps.ghost.replicas 3
|
|
wild-app-deploy ghost # Apply changes
|
|
|
|
# Rolling updates
|
|
wild-config-set apps.ghost.image "ghost:5.1.0"
|
|
wild-app-deploy ghost --rolling-update
|
|
```
|
|
|
|
The Wild Cloud configuration system provides a powerful, secure, and flexible foundation for managing complex infrastructure deployments while maintaining simplicity for common use cases. |