# Garage UI Helm Chart A Helm chart for deploying [Garage UI](https://github.com/Noooste/garage-ui), a modern web interface for managing [Garage](https://garagehq.deuxfleurs.fr/) distributed object storage systems. [![Version](https://img.shields.io/badge/version-0.1.1-blue.svg)](Chart.yaml) [![App Version](https://img.shields.io/badge/app%20version-v0.0.6-green.svg)](Chart.yaml) ## Table of Contents - [Overview](#overview) - [Prerequisites](#prerequisites) - [Quick Start](#quick-start) - [Installation](#installation) - [Configuration](#configuration) - [Examples](#examples) - [Upgrading](#upgrading) - [Accessing the Application](#accessing-the-application) - [Monitoring](#monitoring) - [Troubleshooting](#troubleshooting) - [Uninstalling](#uninstalling) ## Overview Garage UI provides an intuitive web interface for managing your Garage S3 storage cluster, featuring: - **Bucket Management** - Create, delete, and configure S3 buckets - **Object Operations** - Upload, download, and delete objects through the UI - **User Access Control** - Manage users, access keys, and permissions - **Cluster Monitoring** - View cluster health, status, and statistics - **Authentication** - Support for no auth, basic auth, and OIDC/SSO ## Prerequisites Before installing this chart, ensure you have: - **Kubernetes** `1.19+` or later - **Helm** `3.0+` or later - **Garage S3** instance running and accessible - **Garage Admin Token** - Required for administrative operations ### Required Information You will need the following information from your Garage installation: 1. **Garage S3 Endpoint** - The Garage S3 API endpoint (default port: `3900`) 2. **Garage Admin Endpoint** - The Garage Admin API endpoint (default port: `3903`) 3. **Admin Token** - Bearer token for authenticating with the Admin API To find your admin token, check your Garage server's configuration file. ## Quick Start The fastest way to get started with Garage UI: ### Step 1: Create a values file Create a file named `my-values.yaml` with your Garage configuration: ```yaml config: garage: endpoint: "http://garage:3900" # Your Garage S3 endpoint admin_endpoint: "http://garage:3903" # Your Garage Admin endpoint admin_token: "YOUR_ADMIN_TOKEN_HERE" # Your admin token region: "garage" # S3 region (can be any value) ``` ### Step 2: Install the chart ```bash helm install garage-ui ./helm/garage-ui -f my-values.yaml ``` ### Step 3: Access the UI ```bash # Forward port to access locally kubectl port-forward svc/garage-ui 8080:80 # Open in your browser open http://localhost:8080 ``` That's it! You should now have Garage UI running and accessible. ## Installation ### Installing from local chart If you've cloned the repository: ```bash helm install garage-ui ./helm/garage-ui -f my-values.yaml ``` ### Installing with inline values You can also set values directly on the command line: ```bash helm install garage-ui ./helm/garage-ui \ --set config.garage.endpoint=http://garage:3900 \ --set config.garage.admin_endpoint=http://garage:3903 \ --set config.garage.admin_token=your-token-here ``` ### Verify installation Check that the pod is running: ```bash kubectl get pods -l app.kubernetes.io/name=garage-ui ``` View the logs: ```bash kubectl logs -l app.kubernetes.io/name=garage-ui ``` ## Configuration ### Configuration Structure The chart uses a structured `config` section that maps directly to the application's configuration file. You can override any value using Helm's standard methods. ### Minimal Configuration The absolute minimum configuration requires only the Garage endpoints and admin token: ```yaml config: garage: endpoint: "http://garage:3900" admin_endpoint: "http://garage:3903" admin_token: "your-admin-token" ``` ### Common Configuration Options #### Server Settings ```yaml config: server: port: 8080 # Application port environment: production # Environment (production/development/staging) ``` #### Authentication Configuration **No Authentication** (default, suitable for private networks): ```yaml config: auth: admin: enabled: false oidc: enabled: false ``` **Admin Authentication** (username/password with JWT): ```yaml config: auth: admin: enabled: true username: "admin" password: "your-secure-password" oidc: enabled: false ``` **OIDC/SSO** (recommended for production): ```yaml config: auth: admin: enabled: false oidc: enabled: true provider_name: "Keycloak" client_id: "garage-ui" client_secret: "your-oidc-secret" issuer_url: "https://auth.example.com/realms/master" # ... additional OIDC settings ``` **Both Authentication Methods** (admin and OIDC simultaneously): ```yaml config: auth: admin: enabled: true username: "admin" password: "your-secure-password" oidc: enabled: true provider_name: "Keycloak" client_id: "garage-ui" client_secret: "your-oidc-secret" issuer_url: "https://auth.example.com/realms/master" # ... additional OIDC settings ``` #### CORS Configuration ```yaml config: cors: enabled: true allowed_origins: - "https://garage-ui.example.com" # Recommended: specific origins # - "*" # Or: allow all origins (less secure) ``` #### Logging ```yaml config: logging: level: info # debug, info, warn, error format: json # json (recommended for production), text ``` ### Kubernetes Resource Configuration #### Ingress Enable external access with Ingress: ```yaml ingress: enabled: true className: nginx hosts: - host: garage-ui.example.com paths: - path: / pathType: Prefix ``` With TLS: ```yaml ingress: enabled: true className: nginx annotations: cert-manager.io/cluster-issuer: letsencrypt-prod hosts: - host: garage-ui.example.com paths: - path: / pathType: Prefix tls: - secretName: garage-ui-tls hosts: - garage-ui.example.com ``` #### Resource Limits ```yaml resources: limits: cpu: 500m memory: 512Mi requests: cpu: 100m memory: 128Mi ``` #### Replicas and High Availability ```yaml replicaCount: 3 affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app.kubernetes.io/name operator: In values: - garage-ui topologyKey: kubernetes.io/hostname ``` ### Complete Parameters Reference For a complete list of all available parameters, see the [values.yaml](values.yaml) file which includes detailed comments for every configuration option. ## Examples ### Example 1: Basic Installation (In-Cluster Garage) For Garage running in the same Kubernetes cluster: ```yaml # values-basic.yaml config: garage: endpoint: "http://garage.garage-system.svc.cluster.local:3900" admin_endpoint: "http://garage.garage-system.svc.cluster.local:3903" admin_token: "GgJLd9J...your-token-here" region: "garage" ``` Install: ```bash helm install garage-ui ./helm/garage-ui -f values-basic.yaml ``` ### Example 2: External Access with Ingress ```yaml # values-ingress.yaml config: garage: endpoint: "http://garage:3900" admin_endpoint: "http://garage:3903" admin_token: "your-admin-token" ingress: enabled: true className: nginx annotations: cert-manager.io/cluster-issuer: letsencrypt-prod nginx.ingress.kubernetes.io/force-ssl-redirect: "true" hosts: - host: garage-ui.example.com paths: - path: / pathType: Prefix tls: - secretName: garage-ui-tls hosts: - garage-ui.example.com ``` Install: ```bash helm install garage-ui ./helm/garage-ui -f values-ingress.yaml ``` ### Example 3: With Admin Authentication ```yaml # values-admin-auth.yaml config: garage: endpoint: "http://garage:3900" admin_endpoint: "http://garage:3903" admin_token: "your-admin-token" auth: admin: enabled: true username: "admin" password: "super-secret-password-change-me" oidc: enabled: false ingress: enabled: true className: nginx hosts: - host: garage.local paths: - path: / pathType: Prefix ``` Install: ```bash helm install garage-ui ./helm/garage-ui -f values-admin-auth.yaml ``` ### Example 4: Production Setup with OIDC (Keycloak) ```yaml # values-production.yaml replicaCount: 3 config: server: environment: production garage: endpoint: "https://s3.garage.example.com" admin_endpoint: "https://admin.garage.example.com" admin_token: "your-admin-token" region: "us-east-1" auth: admin: enabled: false oidc: enabled: true provider_name: "Keycloak" client_id: "garage-ui" client_secret: "your-oidc-client-secret" scopes: - openid - email - profile issuer_url: "https://auth.example.com/realms/production" auth_url: "https://auth.example.com/realms/production/protocol/openid-connect/auth" token_url: "https://auth.example.com/realms/production/protocol/openid-connect/token" userinfo_url: "https://auth.example.com/realms/production/protocol/openid-connect/userinfo" cookie_secure: true cookie_http_only: true cookie_same_site: "lax" cors: enabled: true allowed_origins: - "https://garage-ui.example.com" logging: level: info format: json ingress: enabled: true className: nginx annotations: cert-manager.io/cluster-issuer: letsencrypt-prod nginx.ingress.kubernetes.io/force-ssl-redirect: "true" nginx.ingress.kubernetes.io/proxy-body-size: "100m" hosts: - host: garage-ui.example.com paths: - path: / pathType: Prefix tls: - secretName: garage-ui-tls hosts: - garage-ui.example.com resources: limits: cpu: 1000m memory: 1Gi requests: cpu: 200m memory: 256Mi affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app.kubernetes.io/name operator: In values: - garage-ui topologyKey: kubernetes.io/hostname ``` Install: ```bash helm install garage-ui ./helm/garage-ui -f values-production.yaml ``` ### Example 5: With Prometheus Monitoring ```yaml # values-monitoring.yaml config: garage: endpoint: "http://garage:3900" admin_endpoint: "http://garage:3903" admin_token: "your-admin-token" serviceMonitor: enabled: true interval: 30s labels: prometheus: kube-prometheus ``` Install: ```bash helm install garage-ui ./helm/garage-ui -f values-monitoring.yaml ``` ### Example 6: Using Kubernetes Secrets for Admin Token (Recommended) For improved security, store the admin token in a Kubernetes secret instead of in values files: **Option A: Use an existing secret** First, create a Kubernetes secret: ```bash kubectl create secret generic garage-admin-token \ --from-literal=admin-token='your-admin-token-here' ``` Then configure the chart to use it: ```yaml # values-with-secret.yaml config: garage: endpoint: "http://garage:3900" admin_endpoint: "http://garage:3903" # Leave admin_token empty when using existingSecret admin_token: "" # Reference the existing secret existingSecret: name: "garage-admin-token" key: "admin-token" ``` Install: ```bash helm install garage-ui ./helm/garage-ui -f values-with-secret.yaml ``` **Option B: Let the chart create the secret** If you provide `admin_token` in values but don't configure `existingSecret`, the chart will automatically create a secret for you: ```yaml # values-auto-secret.yaml config: garage: endpoint: "http://garage:3900" admin_endpoint: "http://garage:3903" # Chart will create a secret with this value admin_token: "your-admin-token" ``` This approach keeps the token out of the ConfigMap while still allowing you to manage it through Helm values. ## Upgrading ### Upgrade to a new version ```bash helm upgrade garage-ui ./helm/garage-ui -f my-values.yaml ``` ### Upgrade with new values ```bash helm upgrade garage-ui ./helm/garage-ui \ --set config.logging.level=debug \ --reuse-values ``` ### Check upgrade history ```bash helm history garage-ui ``` ### Rollback to previous version ```bash helm rollback garage-ui ``` ## Accessing the Application ### Method 1: Port Forward (Development) Quick access for testing: ```bash kubectl port-forward svc/garage-ui 8080:80 ``` Then open: http://localhost:8080 ### Method 2: Ingress (Production) If you've enabled Ingress with a domain: ```bash # Access via your configured domain open https://garage-ui.example.com ``` ### Method 3: NodePort (Alternative) Change service type to NodePort: ```yaml service: type: NodePort port: 80 ``` Find the assigned port: ```bash kubectl get svc garage-ui ``` ### Method 4: LoadBalancer (Cloud Environments) For cloud providers with LoadBalancer support: ```yaml service: type: LoadBalancer port: 80 ``` Get the external IP: ```bash kubectl get svc garage-ui ``` ## Monitoring ### Prometheus ServiceMonitor Enable Prometheus metrics scraping (requires Prometheus Operator): ```yaml serviceMonitor: enabled: true interval: 30s path: /api/v1/monitoring/metrics labels: prometheus: kube-prometheus ``` ### Metrics Endpoint The application exposes metrics at: - Path: `/api/v1/monitoring/metrics` - Format: Prometheus format (proxies Garage Admin API metrics) ### Health Checks Health endpoint available at: - Path: `/health` - Response: `{"status": "ok", "version": "0.1.0"}` Used by Kubernetes liveness and readiness probes. ## Troubleshooting ### Pods Not Starting Check pod status and logs: ```bash kubectl get pods -l app.kubernetes.io/name=garage-ui kubectl describe pod -l app.kubernetes.io/name=garage-ui kubectl logs -l app.kubernetes.io/name=garage-ui ``` Common issues: - **Missing admin token**: Ensure `config.garage.admin_token` is set or `config.garage.existingSecret.name` points to a valid secret - **Secret not found**: If using `existingSecret`, verify the secret exists: `kubectl get secret ` - **Unreachable Garage**: Verify endpoints are accessible from within the cluster - **Invalid OIDC config**: Check all OIDC URLs and credentials when using `auth.oidc.enabled: true` ### Cannot Access the UI For Ingress issues: ```bash kubectl get ingress kubectl describe ingress garage-ui ``` Check Ingress controller logs: ```bash kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx ``` Common issues: - **DNS not configured**: Ensure your domain points to the Ingress controller - **Certificate issues**: Check cert-manager logs if using automatic TLS - **Ingress class mismatch**: Verify `ingress.className` matches your controller ### Configuration Not Updating The deployment includes a ConfigMap checksum annotation that automatically triggers pod restarts when configuration changes. If it's not working: ```bash kubectl rollout restart deployment/garage-ui ``` ### Connection to Garage Fails Test connectivity from within the pod: ```bash kubectl exec -it deployment/garage-ui -- sh # Inside the pod: curl http://garage:3900 curl http://garage:3903/health ``` ### Authentication Issues **Admin Auth:** - Verify username/password in `config.auth.admin` - Ensure `config.auth.admin.enabled` is set to `true` - Check browser developer tools for 401 errors - Verify JWT token is being sent in Authorization header **OIDC:** - Ensure `config.auth.oidc.enabled` is set to `true` - Verify all OIDC URLs are accessible - Check OIDC provider logs - Ensure redirect URI is registered: `https://your-domain/auth/oidc/callback` - Verify client ID and secret ### View Application Logs ```bash # Follow logs kubectl logs -f -l app.kubernetes.io/name=garage-ui # View recent logs kubectl logs --tail=100 -l app.kubernetes.io/name=garage-ui # Export logs kubectl logs -l app.kubernetes.io/name=garage-ui > garage-ui.log ``` ### Debug Mode Enable debug logging: ```yaml config: logging: level: debug ``` ## Uninstalling ### Remove the release ```bash helm uninstall garage-ui ``` This removes all Kubernetes components associated with the chart. ### Clean up completely If you want to remove all associated resources: ```bash # Uninstall the release helm uninstall garage-ui # Remove any remaining ConfigMaps kubectl delete configmap -l app.kubernetes.io/name=garage-ui # Remove any remaining Secrets kubectl delete secret -l app.kubernetes.io/name=garage-ui ``` ## Advanced Configuration ### Using Secrets for Sensitive Data The chart supports using Kubernetes secrets for sensitive data to avoid storing credentials in values files. #### Admin Token from Secret The chart has built-in support for storing the admin token in a Kubernetes secret: **Method 1: Use an existing secret** (recommended) ```bash # Create the secret kubectl create secret generic garage-admin-token \ --from-literal=admin-token='your-admin-token-here' ``` Configure in values: ```yaml config: garage: existingSecret: name: "garage-admin-token" key: "admin-token" ``` **Method 2: Let the chart create the secret** Simply provide the `admin_token` in values, and the chart will automatically create a secret: ```yaml config: garage: admin_token: "your-admin-token" ``` The chart will: 1. Create a secret named `-admin-token` with the token 2. Remove the token from the ConfigMap 3. Inject it into the pod via environment variable This keeps sensitive data out of ConfigMaps while maintaining easy Helm-based management. #### OIDC Client Secret For OIDC credentials, you can similarly use secrets (requires manual template modification): ```bash kubectl create secret generic garage-oidc \ --from-literal=client-secret='your-oidc-secret' ``` ### Network Policies Enable network policies for enhanced security: ```yaml networkPolicy: enabled: true policyTypes: - Ingress - Egress ``` This restricts network traffic to/from the pods. Requires a CNI that supports NetworkPolicy (e.g., Calico, Cilium). ### Custom Container Images Use a custom or private registry: ```yaml image: repository: myregistry.example.com/garage-ui tag: "v1.0.0" pullPolicy: Always imagePullSecrets: - name: myregistrykey ``` ## Version Compatibility | Chart Version | App Version | Kubernetes | Garage | |--------------|-------------|------------|--------| | 0.1.1 | v0.0.6 | 1.19+ | 0.8+ | ## Additional Resources - **GitHub Repository**: https://github.com/Noooste/garage-ui - **Garage Documentation**: https://garagehq.deuxfleurs.fr/ - **Issue Tracker**: https://github.com/Noooste/garage-ui/issues - **Helm Documentation**: https://helm.sh/docs/ ## Support For help and support: 1. **Documentation**: Check this README and the [values.yaml](values.yaml) file 2. **GitHub Issues**: Report bugs or request features at https://github.com/Noooste/garage-ui/issues 3. **Garage Community**: Visit https://garagehq.deuxfleurs.fr/ for Garage-specific questions ## Contributing Contributions are welcome! Please: 1. Fork the repository 2. Create a feature branch 3. Make your changes 4. Submit a pull request ## License This Helm chart is open source and distributed under the same license as Garage UI.