Add helm chart

This commit is contained in:
2025-08-06 21:32:12 +10:00
parent 9efc8d5950
commit f33f8595aa
22 changed files with 1614 additions and 5 deletions
+10
View File
@@ -0,0 +1,10 @@
# Configuration for chart-releaser
# See https://github.com/helm/chart-releaser for more info
owner: wahyd4
git-repo: passkey-auth
charts-repo: https://wahyd4.github.io/passkey-auth
target-branch: gh-pages
package-path: .cr-release-packages
index-path: .cr-index
skip-existing: true
+95
View File
@@ -0,0 +1,95 @@
name: Release Helm Chart
on:
push:
branches:
- main
paths:
- 'helm/passkey-auth/**'
release:
types: [published]
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
lint-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Helm
uses: azure/setup-helm@v4
with:
version: '3.14.0'
- name: Set up chart-testing
uses: helm/chart-testing-action@v2.6.1
- name: Run chart-testing (list)
run: ct list --target-branch ${{ github.event.repository.default_branch }} --chart-dirs helm
- name: Run chart-testing (lint)
run: ct lint --target-branch ${{ github.event.repository.default_branch }} --chart-dirs helm
- name: Create kind cluster
uses: helm/kind-action@v1.9.0
- name: Run chart-testing (install)
run: |
# Install with test values
ct install --target-branch ${{ github.event.repository.default_branch }} --chart-dirs helm \
--helm-extra-set-args "--set config.webauthn.rpId=test.local --set config.auth.allowedEmails={test@example.com} --set secrets.sessionSecret=test-secret-for-ci-only --set ingress.enabled=false"
release:
needs: lint-test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' || github.event_name == 'release'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Configure Git
run: |
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Install Helm
uses: azure/setup-helm@v4
with:
version: '3.14.0'
- name: Add helm repos
run: |
helm repo add bitnami https://charts.bitnami.com/bitnami
- name: Run chart-releaser
uses: helm/chart-releaser-action@v1.6.0
with:
charts_dir: helm
config: .github/cr.yaml
env:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: .cr-release-packages
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
+1 -1
View File
@@ -5,7 +5,7 @@
*.so *.so
*.dylib *.dylib
bin/ bin/
passkey-auth ./passkey-auth
# Test binary, built with `go test -c` # Test binary, built with `go test -c`
*.test *.test
+313
View File
@@ -0,0 +1,313 @@
# Hosting Passkey Auth Helm Chart on GitHub
This guide explains how to host your Helm chart on GitHub Pages and make it available via a public Helm repository.
## Prerequisites
- GitHub repository with your Helm chart
- GitHub Actions enabled
- GitHub Pages enabled
## Setup Steps
### 1. Repository Structure
Ensure your repository has this structure:
```
passkey-auth/
├── .github/
│ ├── workflows/
│ │ └── helm-release.yml
│ └── cr.yaml
├── helm/
│ └── passkey-auth/
│ ├── Chart.yaml
│ ├── values.yaml
│ ├── README.md
│ ├── templates/
│ └── examples/
└── README.md
```
### 2. Enable GitHub Pages
1. Go to your GitHub repository
2. Navigate to **Settings** > **Pages**
3. Under **Source**, select **GitHub Actions**
4. Save the configuration
### 3. Configure Repository Settings
1. **Enable GitHub Actions**:
- Go to **Settings** > **Actions** > **General**
- Enable "Allow all actions and reusable workflows"
2. **Set up GitHub Pages permissions**:
- Go to **Settings** > **Actions** > **General**
- Under "Workflow permissions", select "Read and write permissions"
- Check "Allow GitHub Actions to create and approve pull requests"
### 4. Update Chart Configuration
Edit `.github/cr.yaml` to match your repository:
```yaml
owner: YOUR_GITHUB_USERNAME # Change this
git-repo: passkey-auth # Change if different
charts-repo: https://YOUR_GITHUB_USERNAME.github.io/passkey-auth
target-branch: gh-pages
package-path: .cr-release-packages
index-path: .cr-index
skip-existing: true
```
### 5. Create Your First Release
1. **Tag your first release**:
```bash
git tag v0.1.0
git push origin v0.1.0
```
2. **Or push changes to trigger workflow**:
```bash
git add .
git commit -m "Add Helm chart"
git push origin main
```
The GitHub Action will automatically:
- Lint and test your chart
- Package the chart
- Create a GitHub release
- Update the Helm repository index
- Deploy to GitHub Pages
### 6. Verify the Setup
1. **Check GitHub Actions**:
- Go to **Actions** tab in your repository
- Verify the "Release Helm Chart" workflow completes successfully
2. **Check GitHub Pages**:
- Go to **Settings** > **Pages**
- You should see "Your site is published at https://username.github.io/passkey-auth"
3. **Test the Helm repository**:
```bash
helm repo add passkey-auth https://YOUR_USERNAME.github.io/passkey-auth
helm repo update
helm search repo passkey-auth
```
## Using Your Hosted Chart
### Add the Repository
```bash
helm repo add passkey-auth https://YOUR_USERNAME.github.io/passkey-auth
helm repo update
```
### Install the Chart
```bash
# Basic installation
helm install my-passkey-auth passkey-auth/passkey-auth
# With custom values
helm install my-passkey-auth passkey-auth/passkey-auth \
--set config.webauthn.rpId=auth.example.com \
--set secrets.sessionSecret="your-secure-secret"
# With values file
helm install my-passkey-auth passkey-auth/passkey-auth \
-f values-production.yaml
```
### Search Available Versions
```bash
helm search repo passkey-auth --versions
```
## Maintenance and Updates
### Releasing New Versions
1. **Update Chart.yaml**:
```yaml
version: 0.2.0 # Increment version
appVersion: "v1.1.0" # Update app version if needed
```
2. **Commit and push**:
```bash
git add helm/passkey-auth/Chart.yaml
git commit -m "Bump chart version to 0.2.0"
git push origin main
```
3. **The workflow will automatically**:
- Package the new version
- Create a GitHub release
- Update the Helm repository
### Testing Charts Locally
```bash
# Lint the chart
helm lint helm/passkey-auth/
# Template the chart (dry run)
helm template my-passkey-auth helm/passkey-auth/ \
--set config.webauthn.rpId=test.local
# Install locally for testing
helm install test-release helm/passkey-auth/ \
--dry-run --debug
```
## Advanced Configuration
### Custom Domain for Helm Repository
If you want to use a custom domain instead of `username.github.io`:
1. **Set up custom domain in GitHub Pages**:
- Go to **Settings** > **Pages**
- Add your custom domain (e.g., `charts.example.com`)
2. **Update `.github/cr.yaml`**:
```yaml
charts-repo: https://charts.example.com
```
3. **Configure DNS**:
- Add CNAME record pointing to `username.github.io`
### Multiple Charts in One Repository
If you have multiple charts:
```
helm/
├── passkey-auth/
│ ├── Chart.yaml
│ └── ...
├── another-chart/
│ ├── Chart.yaml
│ └── ...
```
The workflow will automatically detect and release all charts.
### Private Repositories
For private repositories, users will need:
1. **GitHub Personal Access Token**:
```bash
helm repo add passkey-auth https://username:TOKEN@username.github.io/passkey-auth
```
2. **Or configure helm with auth**:
```bash
helm repo add passkey-auth https://username.github.io/passkey-auth \
--username YOUR_USERNAME \
--password YOUR_TOKEN
```
## Troubleshooting
### Common Issues
1. **GitHub Actions Fails**:
- Check workflow permissions in repository settings
- Verify GitHub Pages is enabled
- Check if there are syntax errors in the chart
2. **Chart Not Found**:
- Verify the repository URL is correct
- Check if GitHub Pages deployment completed
- Ensure chart name matches directory name
3. **Permission Denied**:
- Verify GitHub Actions has write permissions
- Check if GitHub Pages is enabled for the repository
### Debug Commands
```bash
# Check repository status
helm repo list
# Update repositories
helm repo update
# Debug template rendering
helm template my-release helm/passkey-auth/ --debug
# Validate chart
helm lint helm/passkey-auth/
# Check chart dependencies
helm dependency list helm/passkey-auth/
```
## Security Considerations
### Repository Security
1. **Secrets Management**:
- Never commit sensitive values to the repository
- Use GitHub Secrets for sensitive configuration
- Document security requirements in README
2. **Chart Signing** (Optional):
```bash
# Generate GPG key for chart signing
gpg --gen-key
# Export public key
gpg --armor --export your-email@example.com > public.key
# Add to chart-releaser config
echo "sign: true" >> .github/cr.yaml
```
3. **Dependency Security**:
- Regularly update chart dependencies
- Use dependency vulnerability scanning
- Pin specific versions in production
### Best Practices
1. **Version Management**:
- Follow semantic versioning
- Update `appVersion` when application changes
- Update `version` when chart changes
2. **Documentation**:
- Keep README.md updated
- Document breaking changes
- Provide migration guides
3. **Testing**:
- Test charts before releasing
- Use CI/CD for automated testing
- Validate on different Kubernetes versions
## Example Repository
You can see a complete example at: `https://github.com/YOUR_USERNAME/passkey-auth`
The hosted Helm repository will be available at: `https://YOUR_USERNAME.github.io/passkey-auth`
## Support
If you encounter issues:
1. Check the GitHub Actions logs
2. Verify chart syntax with `helm lint`
3. Review GitHub Pages deployment status
4. Open an issue in the repository for help
+29 -4
View File
@@ -20,13 +20,38 @@ A WebAuthn-based passkey authentication provider that integrates ingress control
## 🚀 Quick Start ## 🚀 Quick Start
### 1. Build and Test Locally ### Using Helm Chart (Recommended)
```bash ```bash
git clone <repository-url> # Add the Helm repository
cd passkey-auth helm repo add passkey-auth https://wahyd4.github.io/passkey-auth
helm repo update
docker-compose up # Install with your configuration
helm install my-passkey-auth passkey-auth/passkey-auth \
--set config.webauthn.rpId=auth.example.com \
--set config.webauthn.rpOrigins="{https://auth.example.com}" \
--set config.cors.allowedOrigins="{https://*.example.com}" \
--set config.auth.cookieDomain=".example.com" \
--set config.auth.allowedEmails="{admin@example.com}" \
--set ingress.hosts[0].host=auth.example.com \
--set secrets.sessionSecret="your-secure-random-secret-key"
# Or use a values file
helm install my-passkey-auth passkey-auth/passkey-auth -f values-production.yaml
```
See the [Helm Chart README](helm/passkey-auth/README.md) for detailed configuration options.
### Option 3: Local Development
```bash
# Install dependencies and run locally
go mod download
go run main.go
# Access at http://localhost:8080
``` ```
### 2. Configure ### 2. Configure
+21
View File
@@ -0,0 +1,21 @@
apiVersion: v2
name: passkey-auth
description: A WebAuthn-based passkey authentication provider for Kubernetes Nginx Ingress
type: application
version: 0.1.0
appVersion: "main"
home: https://github.com/wahyd4/passkey-auth
sources:
- https://github.com/wahyd4/passkey-auth
maintainers:
- name: Junwei Zhao
email: wahyd4@gmail.com
keywords:
- authentication
- webauthn
- passkey
- nginx-ingress
- security
annotations:
category: Security
licenses: Apache-2.0
+243
View File
@@ -0,0 +1,243 @@
# Passkey Auth Helm Chart
A Helm chart for deploying Passkey Auth, a WebAuthn-based passkey authentication provider that integrates with Kubernetes Nginx Ingress controller.
## Overview
This chart deploys a secure, passwordless authentication service using WebAuthn/FIDO2 passkeys. It's designed to work as an authentication backend for nginx ingress controllers, providing enterprise-grade security without the complexity of traditional password-based systems.
## TL;DR
```bash
helm repo add passkey-auth https://your-github-username.github.io/passkey-auth-helm
helm repo update
helm install my-passkey-auth passkey-auth/passkey-auth \
--set config.webauthn.rpId=auth.example.com \
--set config.webauthn.rpOrigins="{https://auth.example.com}" \
--set secrets.sessionSecret="your-very-long-random-secret-key-here"
```
## Prerequisites
- Kubernetes 1.19+
- Helm 3.0+
- Nginx Ingress Controller
- Cert-Manager (for TLS certificates)
- StorageClass for persistent volumes
## Installation
### Add Helm Repository
```bash
helm repo add passkey-auth https://your-github-username.github.io/passkey-auth-helm
helm repo update
```
### Install from Local Chart
```bash
git clone https://github.com/wahyd4/passkey-auth.git
cd passkey-auth
helm install my-passkey-auth ./helm/passkey-auth \
--values ./helm/passkey-auth/values.yaml
```
The command deploys Passkey Auth on the Kubernetes cluster with the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation.
> **Tip**: List all releases using `helm list`
## Configuration
### Core Configuration
The chart can be configured using the `values.yaml` file or by passing values via `--set` flags.
#### Required Configuration
```yaml
config:
webauthn:
rpId: "auth.example.com" # Your authentication domain
rpOrigins:
- "https://auth.example.com" # Allowed origins for WebAuthn
cors:
allowedOrigins:
- "https://*.example.com" # CORS allowed origins
auth:
cookieDomain: ".example.com" # Cookie domain for SSO
allowedEmails:
- "admin@example.com" # Allowed user emails
secrets:
sessionSecret: "your-secure-random-secret" # Session signing secret
ingress:
hosts:
- host: auth.example.com
paths:
- path: /
pathType: Prefix
```
## Setup Authentication for Your Services
Add these annotations to your ingress resources to protect them with passkey authentication:
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-protected-app
annotations:
nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com/auth"
nginx.ingress.kubernetes.io/auth-signin: "https://auth.example.com/login?rd=$scheme://$http_host$request_uri"
nginx.ingress.kubernetes.io/auth-response-headers: "X-Auth-User,X-Auth-Email"
spec:
# ... your ingress spec
```
## Advanced Configuration
### Custom Storage
```yaml
persistence:
enabled: true
existingClaim: "my-existing-pvc"
storageClass: "ssd-encrypted"
size: 10Gi
```
### External Secrets
```yaml
envFrom:
- secretRef:
name: external-secrets
secrets: {} # Don't create internal secret
```
## Parameters
### Common parameters
| Name | Description | Value |
| ------------------- | ---------------------------------------------------------------------------------------- | ----- |
| `nameOverride` | String to partially override passkey-auth.fullname template | `""` |
| `fullnameOverride` | String to fully override passkey-auth.fullname template | `""` |
| `commonLabels` | Add labels to all the deployed resources | `{}` |
| `commonAnnotations` | Add annotations to all the deployed resources | `{}` |
### Passkey Auth parameters
| Name | Description | Value |
| ----------------------------- | --------------------------------------------------------------- | ---------------------------------- |
| `replicaCount` | Number of Passkey Auth replicas to deploy | `1` |
| `image.repository` | Passkey Auth image repository | `ghcr.io/wahyd4/passkey-auth` |
| `image.tag` | Passkey Auth image tag (immutable tags are recommended) | `main` |
| `image.pullPolicy` | Passkey Auth image pull policy | `Always` |
| `image.pullSecrets` | Passkey Auth image pull secrets | `[]` |
### WebAuthn configuration
| Name | Description | Value |
| --------------------------------- | --------------------------------------------------------------- | ------------------------------ |
| `config.webauthn.rpDisplayName` | WebAuthn Relying Party display name | `Passkey Auth` |
| `config.webauthn.rpId` | WebAuthn Relying Party ID (must match your domain) | `pass.example.com` |
| `config.webauthn.rpOrigins` | Allowed origins for WebAuthn (array) | `["https://pass.example.com"]` |
### CORS configuration
| Name | Description | Value |
| --------------------------------- | --------------------------------------------------------------- | -------------------------------- |
| `config.cors.allowedOrigins` | CORS allowed origins (array) | `["https://*.example.com"]` |
### Authentication configuration
| Name | Description | Value |
| --------------------------------- | --------------------------------------------------------------- | ------------------------------ |
| `config.auth.requireApproval` | Require admin approval for new user registrations | `true` |
| `config.auth.cookieDomain` | Cookie domain for SSO (e.g., .example.com) | `.example.com` |
| `config.auth.allowedEmails` | List of allowed email addresses (array) | `["admin@example.com"]` |
| `config.auth.allowedDomains` | List of allowed email domains (array) | `[]` |
### Service configuration
| Name | Description | Value |
| -------------- | ----------------------------------- | ----------- |
| `service.type` | Kubernetes service type | `ClusterIP` |
| `service.port` | Kubernetes service port | `80` |
### Ingress configuration
| Name | Description | Value |
| -------------------------- | --------------------------------------------------------------- | ------------------------------ |
| `ingress.enabled` | Enable ingress controller resource | `true` |
| `ingress.className` | IngressClass that will be used to implement the Ingress | `nginx` |
| `ingress.annotations` | Additional annotations for the Ingress resource | `{}` |
| `ingress.hosts[0].host` | Hostname for the ingress | `pass.example.com` |
| `ingress.hosts[0].paths` | Paths for the ingress | `[{path: "/", pathType: "Prefix"}]` |
| `ingress.tls` | TLS configuration for ingress | `[]` |
### Persistence configuration
| Name | Description | Value |
| ----------------------------- | --------------------------------------------------------------- | ------------------ |
| `persistence.enabled` | Enable persistent volume for data storage | `true` |
| `persistence.storageClass` | Persistent Volume storage class | `""` |
| `persistence.accessMode` | Persistent Volume access mode | `ReadWriteOnce` |
| `persistence.size` | Persistent Volume size | `2Gi` |
| `persistence.existingClaim` | Use existing persistent volume claim | `""` |
### Security configuration
| Name | Description | Value |
| ------------------------------------------------- | --------------------------------------------------------------- | ---------------------------------------------- |
| `secrets.sessionSecret` | Session secret for signing cookies (change in production!) | `change-me-in-production-use-long-random-string` |
| `podSecurityContext.fsGroup` | Group ID for the pods | `1000` |
| `securityContext.allowPrivilegeEscalation` | Allow privilege escalation for containers | `false` |
| `securityContext.runAsNonRoot` | Run containers as non-root user | `true` |
| `securityContext.runAsUser` | User ID for the containers | `1000` |
| `securityContext.capabilities.drop` | Dropped capabilities | `["ALL"]` |
### Resource management
| Name | Description | Value |
| ----------------------------- | --------------------------------------------------------------- | -------- |
| `resources.limits.cpu` | CPU resource limits | `400m` |
| `resources.limits.memory` | Memory resource limits | `512Mi` |
| `resources.requests.cpu` | CPU resource requests | `100m` |
| `resources.requests.memory` | Memory resource requests | `128Mi` |
### Autoscaling configuration
| Name | Description | Value |
| -------------------------------------------------- | --------------------------------------------------------------- | ------- |
| `autoscaling.enabled` | Enable Horizontal Pod Autoscaler | `false` |
| `autoscaling.minReplicas` | Minimum number of replicas | `1` |
| `autoscaling.maxReplicas` | Maximum number of replicas | `3` |
| `autoscaling.targetCPUUtilizationPercentage` | Target CPU utilization percentage | `80` |
| `autoscaling.targetMemoryUtilizationPercentage` | Target memory utilization percentage | `""` |
### Environment variables
| Name | Description | Value |
| ---------------- | --------------------------------------------------------------- | ------- |
| `env.CONFIG_PATH` | Path to the configuration file | `/app/config.yaml` |
| `env.ADMIN_EMAIL` | Admin email for auto-approval (optional) | `""` |
| `env.DEFAULT_EMAIL` | Default email for initial setup (optional) | `""` |
### Other parameters
| Name | Description | Value |
| ------------- | --------------------------------------------------------------- | ------- |
| `nodeSelector` | Node labels for pod assignment | `{}` |
| `tolerations` | Tolerations for pod assignment | `[]` |
| `affinity` | Affinity for pod assignment | `{}` |
@@ -0,0 +1,62 @@
# Development values for passkey-auth
# Use this for local development and testing
replicaCount: 1
image:
repository: ghcr.io/wahyd4/passkey-auth
tag: "main"
pullPolicy: Always
# Application configuration
config:
webauthn:
rpDisplayName: "Dev Passkey Auth"
rpId: "localhost"
rpOrigins:
- "http://localhost:8080"
- "https://auth.dev.local"
cors:
allowedOrigins:
- "*" # Allow all origins in dev
auth:
requireApproval: false # Auto-approve in dev
cookieDomain: ".dev.local"
allowedEmails: [] # Allow any email in dev
# Security configuration (dev only)
secrets:
sessionSecret: "dev-secret-not-for-production"
# Disable ingress for local development
ingress:
enabled: false
# Minimal persistence for dev
persistence:
enabled: true
size: 1Gi
# Minimal resources for dev
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
# Disable autoscaling
autoscaling:
enabled: false
# Environment variables
env:
- name: DEFAULT_EMAIL
value: "dev@example.com"
- name: CONFIG_PATH
value: "/app/config.yaml"
- name: ADMIN_EMAIL
value: "dev@example.com"
@@ -0,0 +1,117 @@
# Production values for passkey-auth
# Use this as a template for production deployments
replicaCount: 2
image:
repository: ghcr.io/wahyd4/passkey-auth
tag: "v1.0.0" # Pin to specific version in production
pullPolicy: IfNotPresent
# Application configuration
config:
webauthn:
rpDisplayName: "My Company Passkey Auth"
rpId: "auth.company.com" # CHANGE THIS
rpOrigins:
- "https://auth.company.com" # CHANGE THIS
cors:
allowedOrigins:
- "https://*.company.com" # CHANGE THIS
- "https://app.company.com" # Add specific origins
auth:
requireApproval: true
cookieDomain: ".company.com" # CHANGE THIS
allowedEmails:
- "admin@company.com" # CHANGE THIS
- "user@company.com" # Add allowed users
# Security configuration
secrets:
sessionSecret: "" # REQUIRED: Set this to a secure random string (32+ chars)
# Ingress configuration
ingress:
enabled: true
className: "nginx"
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
hosts:
- host: auth.company.com # CHANGE THIS
paths:
- path: /
pathType: Prefix
tls:
- secretName: auth-company-com-tls
hosts:
- auth.company.com # CHANGE THIS
# Persistence configuration
persistence:
enabled: true
storageClass: "fast-ssd" # Use fast storage for production
size: 5Gi
accessMode: ReadWriteOnce
# Resource configuration
resources:
limits:
cpu: 500m
memory: 1Gi
requests:
cpu: 200m
memory: 256Mi
# Autoscaling configuration
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 5
targetCPUUtilizationPercentage: 70
# Health checks
healthCheck:
enabled: true
livenessProbe:
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
readinessProbe:
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
# Security context
podSecurityContext:
fsGroup: 1000
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false # SQLite needs write access
runAsNonRoot: true
runAsUser: 1000
# Node selection
nodeSelector:
kubernetes.io/os: linux
# Environment variables
env:
- name: DEFAULT_EMAIL
value: "admin@company.com" # CHANGE THIS
- name: CONFIG_PATH
value: "/app/config.yaml"
- name: ADMIN_EMAIL
value: "admin@company.com" # CHANGE THIS
+40
View File
@@ -0,0 +1,40 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "passkey-auth.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "passkey-auth.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "passkey-auth.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "passkey-auth.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}
2. Configure your ingress for authentication by adding these annotations to your protected services:
annotations:
nginx.ingress.kubernetes.io/auth-url: "http{{ if .Values.ingress.tls }}s{{ end }}://{{ (index .Values.ingress.hosts 0).host }}/auth"
nginx.ingress.kubernetes.io/auth-signin: "http{{ if .Values.ingress.tls }}s{{ end }}://{{ (index .Values.ingress.hosts 0).host }}/login?rd=$scheme://$http_host$request_uri"
nginx.ingress.kubernetes.io/auth-response-headers: "X-Auth-User,X-Auth-Email"
3. Visit the admin panel to manage users:
{{- if .Values.ingress.enabled }}
http{{ if .Values.ingress.tls }}s{{ end }}://{{ (index .Values.ingress.hosts 0).host }}/admin
{{- end }}
Important Security Notes:
- Change the default session secret in values.yaml before deploying to production
- Configure proper CORS origins for your domain
- Set up proper TLS certificates
- Review and configure allowed email addresses
+87
View File
@@ -0,0 +1,87 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "passkey-auth.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "passkey-auth.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "passkey-auth.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "passkey-auth.labels" -}}
helm.sh/chart: {{ include "passkey-auth.chart" . }}
{{ include "passkey-auth.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "passkey-auth.selectorLabels" -}}
app.kubernetes.io/name: {{ include "passkey-auth.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "passkey-auth.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "passkey-auth.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Create the name of the configmap
*/}}
{{- define "passkey-auth.configmapName" -}}
{{- printf "%s-config" (include "passkey-auth.fullname" .) }}
{{- end }}
{{/*
Create the name of the secret
*/}}
{{- define "passkey-auth.secretName" -}}
{{- printf "%s-secrets" (include "passkey-auth.fullname" .) }}
{{- end }}
{{/*
Create the name of the PVC
*/}}
{{- define "passkey-auth.pvcName" -}}
{{- if .Values.persistence.existingClaim }}
{{- .Values.persistence.existingClaim }}
{{- else }}
{{- printf "%s-pvc" (include "passkey-auth.fullname" .) }}
{{- end }}
{{- end }}
@@ -0,0 +1,38 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "passkey-auth.configmapName" . }}
labels:
{{- include "passkey-auth.labels" . | nindent 4 }}
data:
config.yaml: |
server:
port: {{ .Values.config.server.port | quote }}
host: {{ .Values.config.server.host | quote }}
webauthn:
rp_display_name: {{ .Values.config.webauthn.rpDisplayName | quote }}
rp_id: {{ .Values.config.webauthn.rpId | quote }}
rp_origins:
{{- range .Values.config.webauthn.rpOrigins }}
- {{ . | quote }}
{{- end }}
database:
path: {{ .Values.config.database.path | quote }}
cors:
allowed_origins:
{{- range .Values.config.cors.allowedOrigins }}
- {{ . | quote }}
{{- end }}
auth:
require_approval: {{ .Values.config.auth.requireApproval }}
cookie_domain: {{ .Values.config.auth.cookieDomain | quote }}
{{- if .Values.config.auth.allowedEmails }}
allowed_emails:
{{- range .Values.config.auth.allowedEmails }}
- {{ . | quote }}
{{- end }}
{{- end }}
+121
View File
@@ -0,0 +1,121 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "passkey-auth.fullname" . }}
labels:
{{- include "passkey-auth.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
revisionHistoryLimit: 3
strategy:
type: RollingUpdate
selector:
matchLabels:
{{- include "passkey-auth.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "passkey-auth.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "passkey-auth.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
env:
{{- range .Values.env }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end }}
- name: SESSION_SECRET
valueFrom:
secretKeyRef:
name: {{ include "passkey-auth.secretName" . }}
key: session-secret
{{- with .Values.envFrom }}
envFrom:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
- name: config
mountPath: "/app/config.yaml"
subPath: config.yaml
readOnly: true
{{- if .Values.persistence.enabled }}
- name: data
mountPath: /data
{{- end }}
{{- with .Values.volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if .Values.healthCheck.enabled }}
livenessProbe:
httpGet:
path: {{ .Values.healthCheck.path }}
port: http
initialDelaySeconds: {{ .Values.healthCheck.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.healthCheck.livenessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.healthCheck.livenessProbe.timeoutSeconds }}
failureThreshold: {{ .Values.healthCheck.livenessProbe.failureThreshold }}
readinessProbe:
httpGet:
path: {{ .Values.healthCheck.path }}
port: http
initialDelaySeconds: {{ .Values.healthCheck.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.healthCheck.readinessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.healthCheck.readinessProbe.timeoutSeconds }}
failureThreshold: {{ .Values.healthCheck.readinessProbe.failureThreshold }}
startupProbe:
httpGet:
path: {{ .Values.healthCheck.path }}
port: http
initialDelaySeconds: {{ .Values.healthCheck.startupProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.healthCheck.startupProbe.periodSeconds }}
timeoutSeconds: {{ .Values.healthCheck.startupProbe.timeoutSeconds }}
failureThreshold: {{ .Values.healthCheck.startupProbe.failureThreshold }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumes:
- name: config
configMap:
name: {{ include "passkey-auth.configmapName" . }}
{{- if .Values.persistence.enabled }}
- name: data
persistentVolumeClaim:
claimName: {{ include "passkey-auth.pvcName" . }}
{{- end }}
{{- with .Values.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
restartPolicy: Always
+32
View File
@@ -0,0 +1,32 @@
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "passkey-auth.fullname" . }}
labels:
{{- include "passkey-auth.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "passkey-auth.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}
+56
View File
@@ -0,0 +1,56 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "passkey-auth.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- if and .Values.ingress.className (not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class")) }}
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
{{- end }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "passkey-auth.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- if not (hasKey . "nginx.ingress.kubernetes.io/upstream-vhost") }}
nginx.ingress.kubernetes.io/upstream-vhost: {{ $.Values.config.webauthn.rpId | quote }}
{{- end }}
{{- end }}
spec:
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
pathType: {{ .pathType }}
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}
port:
number: {{ $svcPort }}
{{- else }}
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
+20
View File
@@ -0,0 +1,20 @@
{{- if .Values.persistence.enabled }}
{{- if not .Values.persistence.existingClaim }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "passkey-auth.pvcName" . }}
labels:
{{- include "passkey-auth.labels" . | nindent 4 }}
spec:
accessModes:
- {{ .Values.persistence.accessMode }}
resources:
requests:
storage: {{ .Values.persistence.size }}
{{- if .Values.persistence.storageClass }}
storageClassName: {{ .Values.persistence.storageClass }}
{{- end }}
volumeMode: Filesystem
{{- end }}
{{- end }}
+9
View File
@@ -0,0 +1,9 @@
apiVersion: v1
kind: Secret
metadata:
name: {{ include "passkey-auth.secretName" . }}
labels:
{{- include "passkey-auth.labels" . | nindent 4 }}
type: Opaque
data:
session-secret: {{ .Values.secrets.sessionSecret | b64enc | quote }}
+15
View File
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "passkey-auth.fullname" . }}
labels:
{{- include "passkey-auth.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "passkey-auth.selectorLabels" . | nindent 4 }}
@@ -0,0 +1,12 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "passkey-auth.serviceAccountName" . }}
labels:
{{- include "passkey-auth.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
+165
View File
@@ -0,0 +1,165 @@
# Default values for passkey-auth.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: ghcr.io/wahyd4/passkey-auth
pullPolicy: Always
# Overrides the image tag whose default is the chart appVersion.
tag: "main"
imagePullSecrets:
- name: github-image-pull-secret
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
create: false
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
podAnnotations: {}
podSecurityContext:
fsGroup: 1000
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
service:
type: ClusterIP
port: 80
targetPort: 8080
ingress:
enabled: true
className: "nginx"
annotations:
kubernetes.io/tls-acme: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/proxy-set-headers: "passkey-auth-headers"
nginx.ingress.kubernetes.io/upstream-vhost: "" # Will be set to config.webauthn.rpId
nginx.ingress.kubernetes.io/proxy-redirect-from: "http://"
nginx.ingress.kubernetes.io/proxy-redirect-to: "https://"
hosts:
- host: pass.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: passkey-auth-tls
hosts:
- pass.example.com
resources:
limits:
cpu: 400m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 3
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
# Persistent storage for SQLite database
persistence:
enabled: true
storageClass: "" # Use default storage class
accessMode: ReadWriteOnce
size: 2Gi
# existingClaim: ""
# Health check configuration
healthCheck:
enabled: true
path: /health
livenessProbe:
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
startupProbe:
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 6
# Application configuration
config:
server:
port: "8080"
host: "0.0.0.0"
webauthn:
rpDisplayName: "Passkey Auth"
rpId: "pass.example.com"
rpOrigins:
- "https://pass.example.com"
database:
path: "/data/passkey-auth.db"
cors:
allowedOrigins:
- "https://*.example.com"
auth:
requireApproval: true
cookieDomain: ".example.com"
allowedEmails:
- "admin@example.com"
# adminEmail will be set via environment variable
# Environment variables
env:
- name: DEFAULT_EMAIL
value: "admin@example.com"
- name: CONFIG_PATH
value: "/app/config.yaml"
- name: ADMIN_EMAIL
value: "admin@example.com"
# Secret environment variables
secrets:
# Session secret for cookie signing
sessionSecret: "change-me-in-production-use-long-random-string"
# Additional environment variables from existing secrets
envFrom: []
# - secretRef:
# name: my-secret
# Additional volume mounts
volumeMounts: []
# Additional volumes
volumes: []
Executable
BIN
View File
Binary file not shown.
+128
View File
@@ -0,0 +1,128 @@
#!/bin/bash
# Helm Chart Validation Script
# This script validates the Helm chart before deployment
set -e
CHART_DIR="helm/passkey-auth"
NAMESPACE="passkey-auth-test"
echo "🔍 Validating Passkey Auth Helm Chart..."
# Check if helm is installed
if ! command -v helm &> /dev/null; then
echo "❌ Helm is not installed. Please install Helm first."
exit 1
fi
# Check if kubectl is installed
if ! command -v kubectl &> /dev/null; then
echo "❌ kubectl is not installed. Please install kubectl first."
exit 1
fi
echo "✅ Prerequisites check passed"
# Lint the chart
echo "🔧 Linting Helm chart..."
if helm lint $CHART_DIR; then
echo "✅ Chart linting passed"
else
echo "❌ Chart linting failed"
exit 1
fi
# Template the chart with test values
echo "📝 Templating chart with test values..."
helm template test-release $CHART_DIR \
--set config.webauthn.rpId=test.example.com \
--set config.webauthn.rpOrigins="{https://test.example.com}" \
--set config.cors.allowedOrigins="{https://test.example.com}" \
--set config.auth.cookieDomain=".example.com" \
--set config.auth.allowedEmails="{admin@example.com}" \
--set secrets.sessionSecret="test-secret-for-validation-only" \
--set ingress.hosts[0].host=test.example.com \
--set ingress.tls[0].hosts="{test.example.com}" \
> /tmp/passkey-auth-template.yaml
if [ $? -eq 0 ]; then
echo "✅ Chart templating passed"
else
echo "❌ Chart templating failed"
exit 1
fi
# Validate Kubernetes manifests
echo "🔍 Validating Kubernetes manifests..."
if kubectl apply --dry-run=client -f /tmp/passkey-auth-template.yaml; then
echo "✅ Kubernetes manifest validation passed"
else
echo "❌ Kubernetes manifest validation failed"
exit 1
fi
# Check for required values
echo "🔧 Checking for required configuration..."
REQUIRED_VALUES=(
"config.webauthn.rpId"
"config.webauthn.rpOrigins"
"secrets.sessionSecret"
)
for value in "${REQUIRED_VALUES[@]}"; do
if helm template test-release $CHART_DIR --show-only templates/configmap.yaml | grep -q "REQUIRED"; then
echo "❌ Required value not set: $value"
exit 1
fi
done
echo "✅ Required values check passed"
# Test with production values
if [ -f "$CHART_DIR/examples/values-production.yaml" ]; then
echo "🏭 Testing with production values..."
helm template test-release $CHART_DIR \
-f $CHART_DIR/examples/values-production.yaml \
--set secrets.sessionSecret="test-secret" \
> /tmp/passkey-auth-production.yaml
if kubectl apply --dry-run=client -f /tmp/passkey-auth-production.yaml; then
echo "✅ Production values validation passed"
else
echo "❌ Production values validation failed"
exit 1
fi
fi
# Test with development values
if [ -f "$CHART_DIR/examples/values-development.yaml" ]; then
echo "🚀 Testing with development values..."
helm template test-release $CHART_DIR \
-f $CHART_DIR/examples/values-development.yaml \
> /tmp/passkey-auth-development.yaml
if kubectl apply --dry-run=client -f /tmp/passkey-auth-development.yaml; then
echo "✅ Development values validation passed"
else
echo "❌ Development values validation failed"
exit 1
fi
fi
# Cleanup
rm -f /tmp/passkey-auth-*.yaml
echo "🎉 All validations passed! The Helm chart is ready for deployment."
echo ""
echo "Next steps:"
echo "1. Update values in values.yaml or use --set flags"
echo "2. Install the chart: helm install my-passkey-auth $CHART_DIR"
echo "3. Configure your ingress controllers to use the auth backend"
echo ""
echo "For production deployment, make sure to:"
echo "• Set a secure session secret"
echo "• Configure proper domains and origins"
echo "• Set up TLS certificates"
echo "• Configure allowed email addresses"