The Flux Extension Controller provides automatic GitHub App token management for accessing private repositories in Flux CD GitRepository resources. This feature ensures that authentication tokens are automatically created, refreshed, and maintained without manual intervention.
This feature enables:
- Automatic token generation for GitHub App authentication
- Token refresh management to prevent expiration
- Seamless integration with Flux CD GitRepository resources
- Organization-scoped access for enterprise GitHub setups
The controller monitors GitRepository resources and:
- Detects private GitHub repositories that need authentication
- Validates repository URLs against the configured GitHub organization
- Generates GitHub App tokens using JWT authentication
- Creates/updates Kubernetes secrets with valid tokens
- Schedules automatic refresh before tokens expire
- Handles token lifecycle including cleanup when resources are deleted
-
Create a GitHub App in your organization:
- Go to GitHub → Settings → Developer settings → GitHub Apps
- Click "New GitHub App"
- Configure permissions:
- Repository permissions:
Contents: Read,Metadata: Read - Organization permissions:
Members: Read(if needed)
- Repository permissions:
-
Generate a private key:
- In your GitHub App settings, scroll to "Private keys"
- Click "Generate a private key"
- Download and securely store the
.pemfile
-
Install the GitHub App:
- Go to your GitHub App settings
- Click "Install App"
- Select your organization and repositories
-
Note the App ID and Installation ID:
- App ID: Found in the GitHub App settings page
- Installation ID: Found in the URL after installing the app
Configure the GitHub App details in your controller configuration:
# config.yaml
github:
# GitHub App ID (required)
appId: ""
privateKeySecret:
name: "github-app-credentials"
key: "private-key"
controller:
organization: "your-org" # GitHub organization name
tokenRefresh:
refreshInterval: "50m" # How often to check for tokens needing refresh
tokenLifetime: "1h" # Expected GitHub App token lifetimeMount the GitHub App private key into the controller pod:
apiVersion: apps/v1
kind: Deployment
metadata:
name: flux-extension-controller
spec:
template:
spec:
containers:
- name: manager
volumeMounts:
- name: github-private-key
mountPath: /etc/github
readOnly: true
volumes:
- name: github-private-key
secret:
secretName: github-app-private-key
items:
- key: private-key.pem
path: private-key.pem
mode: 0400Create the secret with your private key:
kubectl create secret generic github-app-private-key \
--from-file=private-key.pem=/path/to/your/private-key.pem \
-n flux-systemapiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: private-repo
namespace: flux-system
spec:
url: https://github.com/your-org/private-repository
interval: 5m
secretRef:
name: github-token-private-repo # Will be automatically managedThe controller will:
- Detect this is a private repository in your organization
- Generate a GitHub App token
- Create the
github-token-private-reposecret - Schedule automatic token refresh
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: app-config
namespace: flux-system
spec:
url: https://github.com/your-org/app-config
interval: 10m
secretRef:
name: github-token-app-config
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: infrastructure
namespace: flux-system
spec:
url: https://github.com/your-org/k8s-infrastructure
interval: 15m
secretRef:
name: github-token-infrastructureEach GitRepository will get its own managed secret with a fresh token.
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: team-a-config
namespace: team-a
spec:
url: https://github.com/your-org/team-a-config
interval: 5m
secretRef:
name: github-tokenThe controller manages tokens across all namespaces where it has permissions.
tokenRefresh:
refreshInterval: "30m" # Check every 30 minutes
tokenLifetime: "55m" # Assume tokens expire after 55 minutesNote: GitHub App tokens typically have a 1-hour lifetime. Configure refresh to happen with sufficient buffer time.
The controller validates that repository URLs belong to the configured organization:
github:
organization: "acme-corp"Only repositories under https://github.com/acme-corp/* will be processed.
If you omit the installationId, the controller will attempt to auto-detect it:
github:
appId: 123456
# installationId: omitted - will be auto-detected
privateKeyPath: "/etc/github/private-key.pem"
organization: "your-org"This is useful when the same configuration is used across multiple environments.
Generated secrets follow this format:
apiVersion: v1
kind: Secret
metadata:
name: github-token-private-repo
namespace: flux-system
annotations:
flux-extension.nrfcloud.com/managed-by: "flux-extension-controller"
flux-extension.nrfcloud.com/repository-url: "https://github.com/your-org/private-repository"
flux-extension.nrfcloud.com/token-expires-at: "2025-09-19T10:30:00Z"
type: Opaque
data:
username: Z2l0aHVi # base64: "github"
password: Z2hwX3h4eHh4eA== # base64: GitHub App tokenCheck token expiration times:
kubectl get secrets -A -o json | \
jq '.items[] | select(.metadata.annotations["flux-extension.nrfcloud.com/managed-by"] == "flux-extension-controller") |
{name: .metadata.name, namespace: .metadata.namespace, expires: .metadata.annotations["flux-extension.nrfcloud.com/token-expires-at"]}'Monitor token refresh operations in controller logs:
kubectl logs -n flux-system -l app.kubernetes.io/name=flux-extension-controller | grep "token refresh"Check if GitRepositories can access their repositories:
kubectl get gitrepositories -A -o wideLook for Ready=True status and recent successful reconciliations.
GitRepository stuck in "Authentication failed" state:
-
Check GitHub App configuration:
# Verify the secret exists kubectl get secret github-token-<name> -n <namespace> # Check token expiration kubectl get secret github-token-<name> -n <namespace> -o json | \ jq '.metadata.annotations["flux-extension.nrfcloud.com/token-expires-at"]'
-
Verify repository URL format:
- Must be
https://github.com/organization/repository - Must match the configured organization
- Repository must exist and be accessible by the GitHub App
- Must be
-
Check GitHub App permissions:
- Contents: Read
- Metadata: Read
- App must be installed on the target repository
Tokens not refreshing automatically:
-
Check refresh configuration:
kubectl logs -n flux-system -l app.kubernetes.io/name=flux-extension-controller | grep "refresh"
-
Verify token lifetime settings:
refreshIntervalshould be less thantokenLifetimetokenLifetimeshould be less than actual GitHub token expiration (usually 1 hour)
Permission errors:
-
Verify RBAC permissions:
kubectl auth can-i create secrets --as=system:serviceaccount:flux-system:flux-extension-controller kubectl auth can-i update secrets --as=system:serviceaccount:flux-system:flux-extension-controller kubectl auth can-i get gitrepositories --as=system:serviceaccount:flux-system:flux-extension-controller
-
Check GitHub App installation:
- Verify the app is installed on the target repositories
- Check organization-level permissions if using org-owned repositories
# List all managed secrets
kubectl get secrets -A -l flux-extension.nrfcloud.com/managed-by=flux-extension-controller
# Check specific secret details
kubectl describe secret github-token-<name> -n <namespace>
# View controller events
kubectl get events -n flux-system --field-selector involvedObject.kind=Secret
# Test GitHub API access (manual verification)
# Replace <token> with actual token from secret
curl -H "Authorization: Bearer <token>" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/your-org/your-repoLook for these log patterns:
# Successful token generation
kubectl logs -n flux-system -l app.kubernetes.io/name=flux-extension-controller | grep "token generated"
# Token refresh operations
kubectl logs -n flux-system -l app.kubernetes.io/name=flux-extension-controller | grep "refreshing token"
# Authentication errors
kubectl logs -n flux-system -l app.kubernetes.io/name=flux-extension-controller | grep "authentication failed"
# Repository validation
kubectl logs -n flux-system -l app.kubernetes.io/name=flux-extension-controller | grep "repository validation"- Store private keys in Kubernetes secrets with restricted access
- Use
mode: 0400when mounting private key files - Rotate private keys periodically as per your security policy
- Monitor access to secrets containing private keys
- Tokens are stored in Kubernetes secrets with base64 encoding
- Limit secret access using RBAC
- Monitor token usage through GitHub audit logs
- Implement network policies to restrict controller access
- Use minimal required permissions:
- Contents: Read (for repository access)
- Metadata: Read (for repository information)
- Avoid organization-level permissions unless necessary
- Regularly audit GitHub App installations and permissions
- Use unique secret names for each GitRepository to avoid conflicts
- Monitor token expiration and refresh operations
- Set appropriate refresh intervals with sufficient buffer time
- Implement monitoring for authentication failures
- Document GitHub App setup for your team
- Test token refresh in non-production environments first
- Keep private keys secure and rotate them regularly
- Use organization-scoped GitHub Apps for better security
- Only supports GitHub.com repositories (not GitHub Enterprise Server)
- Requires GitHub App setup and private key management
- Token lifetime is limited by GitHub (typically 1 hour)
- Repository URLs must exactly match the configured organization
- One GitHub organization per controller instance