A kubectl plugin for authenticating to Kubernetes clusters using Keycloak via OAuth 2.0 Device Authorization Grant flow.
- Device Flow Authentication - Authenticate on headless systems by entering a code in a browser on another device
- Dual Authentication Modes - Support both OIDC and direct token injection
- Automatic Token Refresh - Seamlessly refresh expired tokens
- Keycloak Setup Automation - Tools and scripts to configure Keycloak programmatically
- Secure Token Storage - Tokens stored with 0600 permissions
- Debug Support - Comprehensive debug logging for troubleshooting
- Installation
- Quick Start
- Configuration
- Usage
- Keycloak Setup
- Troubleshooting
- Documentation
- Building from Source
# Download the latest release
curl -LO https://github.com/ohauer/kubectl-oidc-login/releases/latest/kubectl-oidc-login
# Make it executable
chmod +x kubectl-oidc-login
# Move to PATH
sudo mv kubectl-oidc-login /usr/local/bin/kubectl-oidc-login --versionUse the automated setup tool:
# Create admin password file
echo "your-admin-password" > ~/keycloak-admin-pass.txt
chmod 600 ~/keycloak-admin-pass.txt
# Initialize Keycloak realm and client
setup-keycloak init \
-u https://keycloak.example.com \
-P ~/keycloak-admin-pass.txt
# Create test user
echo "test-user-password" > ~/user-pass.txt
chmod 600 ~/user-pass.txt
setup-keycloak user \
-u https://keycloak.example.com \
-P ~/keycloak-admin-pass.txt \
-n testuser \
-p ~/user-pass.txt
# Clean up password files
rm -f ~/keycloak-admin-pass.txt ~/user-pass.txtOr follow the manual setup guide.
Create ~/.kube/oidc-login-config.yaml:
keycloak:
issuer_url: https://keycloak.example.com/realms/kubernetes
client_id: kubectl-oidc
auth:
mode: oidc # or "direct" for clusters without OIDC
auto_refresh: true
cache_path: ~/.kube/keycloak-tokenskubectl-oidc-login loginFollow the instructions to complete authentication in your browser.
kubectl get podsLocation: ~/.kube/oidc-login-config.yaml
keycloak:
# Keycloak issuer URL (realm URL)
issuer_url: https://keycloak.example.com/realms/kubernetes
# OAuth2 client ID
client_id: kubectl-oidc
auth:
# Authentication mode: "oidc" or "direct"
# - oidc: Uses OIDC auth provider (requires cluster OIDC configuration)
# - direct: Injects bearer token directly (works without cluster OIDC)
mode: oidc
# Enable automatic token refresh
auto_refresh: true
# Token cache file path
cache_path: ~/.kube/keycloak-tokenskubectl-oidc-login [command] [flags]
Global Flags:
--config string Config file path (default: ~/.kube/oidc-login-config.yaml)
--kubeconfig string Kubeconfig file path (default: ~/.kube/config)
--user string Username in kubeconfig (default: keycloak-user)
--debug Enable debug outputRequires Kubernetes API server configured with OIDC flags:
kube-apiserver \
--oidc-issuer-url=https://keycloak.example.com/realms/kubernetes \
--oidc-client-id=kubectl-oidc \
--oidc-username-claim=preferred_username \
--oidc-groups-claim=groupsAdvantages:
- Tokens validated by Kubernetes API server
- More secure
- Standard approach
Works without cluster OIDC configuration.
Advantages:
- No cluster configuration needed
- Easier for testing
- Works with any cluster
Disadvantages:
- Plugin must handle token refresh
- Less secure than OIDC mode
Authenticate via device flow:
kubectl-oidc-login loginOutput:
Requesting device code...
Authentication Required
=======================
Please visit the following URL in your browser:
https://keycloak.example.com/realms/kubernetes/device
And enter this code:
ABCD-EFGH
Waiting for authentication... (expires in 10 minutes)
Polling for token...
✓ Authentication successful!
Tokens saved to: ~/.kube/keycloak-tokens
✓ Kubeconfig updated (OIDC mode)
You can now use kubectl with user: keycloak-user
View authentication status and token expiry:
kubectl-oidc-login statusOutput:
Authentication Status
====================
User: keycloak-user
Mode: oidc
Issuer: https://keycloak.example.com/realms/kubernetes
Client ID: kubectl-oidc
Token Type: Bearer
Expires At: 2026-01-30T01:00:00+01:00
Valid: true
Time Left: 45m30s
Manually refresh tokens:
kubectl-oidc-login refreshClear cached tokens and remove user from kubeconfig:
kubectl-oidc-login logoutEnable debug output for troubleshooting:
kubectl-oidc-login login --debug# Initialize
setup-keycloak init \
-u https://keycloak.example.com \
-r kubernetes \
-P /path/to/admin-password.txt \
-c kubectl-oidc
# Create user
setup-keycloak user \
-u https://keycloak.example.com \
-r kubernetes \
-P /path/to/admin-password.txt \
--username testuser \
--password-file /path/to/user-password.txt
# Validate
setup-keycloak validate \
-u https://keycloak.example.com \
-r kubernetes# Initialize
./scripts/setup-keycloak.sh init \
-u https://keycloak.example.com \
-P /path/to/admin-password.txt
# Create user
./scripts/setup-keycloak.sh user \
-u https://keycloak.example.com \
-P /path/to/admin-password.txt \
-n testuser \
-p /path/to/user-password.txt
# Validate
./scripts/setup-keycloak.sh validate \
-u https://keycloak.example.comSee Keycloak Setup Guide for detailed manual configuration instructions.
Problem: failed to request device code or authentication failed
Solutions:
- Verify Keycloak URL is correct and accessible
- Check that realm and client ID match configuration
- Ensure device flow is enabled on the client
- Use
--debugflag to see detailed error messages
kubectl-oidc-login login --debugProblem: token expired and auto-refresh disabled
Solutions:
- Enable auto-refresh in config:
auto_refresh: true - Manually refresh:
kubectl-oidc-login refresh - Re-authenticate:
kubectl-oidc-login login
Problem: error: You must be logged in to the server (Unauthorized)
Solutions:
- Check token status:
kubectl-oidc-login status - Verify kubeconfig user is set correctly
- For OIDC mode, ensure cluster is configured with OIDC flags
- Try direct token mode if OIDC is not configured
Problem: failed to load config: failed to read config file
Solutions:
- Create config file:
~/.kube/oidc-login-config.yaml - Use example:
cp oidc-login-config.yaml.example ~/.kube/oidc-login-config.yaml - Specify custom path:
--config /path/to/config.yaml
Problem: error: expired_token
Solutions:
- Device codes expire after 10 minutes
- Request a new code:
kubectl-oidc-login login - Complete authentication faster
Problem: x509: certificate signed by unknown authority
Solutions:
- Ensure Keycloak uses valid TLS certificate
- For private domains with valid certs, add to /etc/hosts
- Verify certificate chain is complete
Problem: failed to save token cache: permission denied
Solutions:
- Check directory permissions:
ls -la ~/.kube/ - Create directory:
mkdir -p ~/.kube && chmod 700 ~/.kube - Verify file permissions:
chmod 600 ~/.kube/keycloak-tokens
Enable debug mode to see detailed diagnostic information:
# For kubectl-oidc-login
kubectl-oidc-login login --debug
# For setup-keycloak
setup-keycloak init -u https://keycloak.example.com -P pass.txt --debug
# For shell script
./scripts/setup-keycloak.sh init -u https://keycloak.example.com -P pass.txt -d- Architecture - System architecture and component design
- Project Plan - Implementation plan and task breakdown
- Device Flow Details - OAuth 2.0 Device Authorization Grant flow
- Keycloak Setup Guide - Manual Keycloak configuration
- Shell Scripts README - POSIX shell script documentation
- Go 1.25.6 or higher
- make
- git
# Clone the repository
git clone https://github.com/ohauer/kubectl-oidc-login.git
cd kubectl-oidc-login
# Build binaries
make build
# Run tests
make test
# Run linter
make lint
# Install locally
make installmake build # Build both binaries
make test # Run all tests with coverage
make lint # Run golangci-lint
make clean # Remove build artifacts
make install # Install to /usr/local/bin# Release build
make build
./bin/kubectl-oidc-login --version
# Output: kubectl-oidc-login version 0.1-alpha0
# Development build
VERSION=dev make build
./bin/kubectl-oidc-login --version
# Output: kubectl-oidc-login version dev (built: 2026-01-30_00:30:00, commit: abc1234)- Go 1.25.6 or higher (for building)
- Keycloak 26.x
- Kubernetes cluster (for OIDC mode, cluster must be configured with OIDC flags)
- Valid HTTPS connection to Keycloak
- Tokens stored with 0600 file permissions
- File-based credential input prevents exposure in process lists
- All communication over HTTPS
- TLS certificate validation enabled by default
- No credentials in logs or command history
Apache License 2.0
Contributions are welcome! Please ensure:
- Code follows Go best practices
- All tests pass:
make test - Linter passes:
make lint - Documentation is updated
For issues, questions, or contributions:
- GitHub Issues: https://github.com/ohauer/kubectl-oidc-login/issues
- Documentation: See docs/ directory