Configure remote kubectl access with 1Password credentials
Step 0.10 implementation: - Recreate minikube with --apiserver-names=indri --listen-address=0.0.0.0 - Add kubectl-credential-1password exec plugin for 1Password integration - Client certs fetched from 1Password on-demand (no private keys on disk) - CA cert stored locally (not secret - public key for server verification) Minikube role updates: - Add minikube_apiserver_names and minikube_listen_address variables - Update tasks to include remote access flags This mirrors the 1Password SSH agent pattern - biometric auth required for each kubectl command that needs credentials. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
9950c8207f
commit
e032e27b66
4 changed files with 102 additions and 3 deletions
|
|
@ -6,3 +6,9 @@ minikube_memory: 7800
|
|||
minikube_disk_size: "200g"
|
||||
minikube_driver: podman
|
||||
minikube_container_runtime: cri-o
|
||||
|
||||
# Remote access configuration
|
||||
# These allow kubectl from other machines (e.g., gilbert) to connect
|
||||
minikube_apiserver_names:
|
||||
- indri
|
||||
minikube_listen_address: "0.0.0.0"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
# NOTE: Similar to podman, minikube start may have issues when run via SSH.
|
||||
# If cluster fails to start, manually run on indri:
|
||||
# minikube start --driver=podman --container-runtime=cri-o \
|
||||
# --cpus=4 --memory=8192 --disk-size=200g
|
||||
# --cpus=4 --memory=7800 --disk-size=200g \
|
||||
# --apiserver-names=indri --listen-address=0.0.0.0
|
||||
|
||||
- name: Install minikube via homebrew
|
||||
community.general.homebrew:
|
||||
|
|
@ -33,6 +34,10 @@
|
|||
--cpus={{ minikube_cpus }}
|
||||
--memory={{ minikube_memory }}
|
||||
--disk-size={{ minikube_disk_size }}
|
||||
{% for name in minikube_apiserver_names %}
|
||||
--apiserver-names={{ name }}
|
||||
{% endfor %}
|
||||
--listen-address={{ minikube_listen_address }}
|
||||
register: minikube_start
|
||||
changed_when: minikube_start.rc == 0
|
||||
failed_when: false # Don't fail - may need manual intervention like podman
|
||||
|
|
|
|||
31
bin/kubectl-credential-1password
Executable file
31
bin/kubectl-credential-1password
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
#!/bin/bash
|
||||
# kubectl exec credential plugin for 1Password
|
||||
# Usage: kubectl-credential-1password <vault-id> <item-id> <cert-field> <key-field>
|
||||
#
|
||||
# Fetches client certificate and key from 1Password and outputs
|
||||
# ExecCredential JSON for kubectl authentication.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
VAULT_ID="$1"
|
||||
ITEM_ID="$2"
|
||||
CERT_FIELD="$3"
|
||||
KEY_FIELD="$4"
|
||||
|
||||
# Fetch credentials from 1Password (strips surrounding quotes from text fields)
|
||||
CLIENT_CERT=$(op --vault "$VAULT_ID" item get "$ITEM_ID" --fields "$CERT_FIELD" | sed 's/^"//; s/"$//')
|
||||
CLIENT_KEY=$(op --vault "$VAULT_ID" item get "$ITEM_ID" --fields "$KEY_FIELD" | sed 's/^"//; s/"$//')
|
||||
|
||||
# Output ExecCredential JSON
|
||||
# Note: jq is used to properly escape the PEM data for JSON
|
||||
jq -n \
|
||||
--arg cert "$CLIENT_CERT" \
|
||||
--arg key "$CLIENT_KEY" \
|
||||
'{
|
||||
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||
"kind": "ExecCredential",
|
||||
"status": {
|
||||
"clientCertificateData": $cert,
|
||||
"clientKeyData": $key
|
||||
}
|
||||
}'
|
||||
|
|
@ -637,7 +637,7 @@ Chose **Option 3: Recreate cluster with `--apiserver-names`** after researching
|
|||
2. **SOCKS5 proxy with kubeconfig `proxy-url`** - Kubeconfig supports `proxy-url: socks5://localhost:1080` per-context, but still requires managing the proxy
|
||||
3. **`--apiserver-names` + `--listen-address`** - Native minikube support, cleanest solution
|
||||
|
||||
**Approach:** Recreate the minikube cluster with additional flags:
|
||||
**Cluster Setup:** Recreated the minikube cluster with additional flags:
|
||||
```bash
|
||||
minikube delete
|
||||
minikube start \
|
||||
|
|
@ -650,14 +650,71 @@ minikube start \
|
|||
|
||||
- `--apiserver-names=indri` adds "indri" to the API server certificate SAN
|
||||
- `--listen-address=0.0.0.0` tells podman to expose the API port on all interfaces
|
||||
- API server port is dynamic (check with `kubectl config view --minify -o jsonpath="{.clusters[0].cluster.server}"` on indri)
|
||||
|
||||
Then configure kubeconfig on gilbert pointing to `https://indri:<port>` with certs copied from indri.
|
||||
**Credential Management with 1Password:**
|
||||
|
||||
Rather than copying private keys between machines, credentials are stored in 1Password and fetched on-demand using kubectl's exec credential plugin. This mirrors the 1Password SSH agent pattern for biometric-protected key access.
|
||||
|
||||
1. **Store credentials in 1Password** (vault: `vg6xf6vvfmoh5hqjjhlhbeoaie`, item: `3jo4f2hnzvwfmamudfsbbbec7e`):
|
||||
- `client-cert` - Contents of `~/.minikube/profiles/minikube/client.crt` (text field)
|
||||
- `client-key` - Contents of `~/.minikube/profiles/minikube/client.key` (text field)
|
||||
- `ca-cert` - Contents of `~/.minikube/ca.crt` (text field, not secret but stored for convenience)
|
||||
|
||||
2. **Created credential helper script** at `bin/kubectl-credential-1password`:
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Fetches client cert/key from 1Password, outputs ExecCredential JSON
|
||||
# Usage: kubectl-credential-1password <vault-id> <item-id> <cert-field> <key-field>
|
||||
```
|
||||
Symlinked to `~/.local/bin/kubectl-credential-1password`
|
||||
|
||||
3. **Kubeconfig setup on gilbert:**
|
||||
```bash
|
||||
# Store CA cert locally (not secret - public key for server verification)
|
||||
mkdir -p ~/.kube/minikube-indri
|
||||
op --vault <vault> item get <item> --fields ca-cert | sed 's/^"//; s/"$//' > ~/.kube/minikube-indri/ca.crt
|
||||
|
||||
# Configure cluster
|
||||
kubectl config set-cluster minikube-indri \
|
||||
--server=https://indri:<port> \
|
||||
--certificate-authority=/Users/eblume/.kube/minikube-indri/ca.crt
|
||||
|
||||
# Configure credentials with exec plugin
|
||||
kubectl config set-credentials minikube-indri \
|
||||
--exec-api-version=client.authentication.k8s.io/v1beta1 \
|
||||
--exec-command=kubectl-credential-1password \
|
||||
--exec-arg=<vault-id> \
|
||||
--exec-arg=<item-id> \
|
||||
--exec-arg=client-cert \
|
||||
--exec-arg=client-key
|
||||
|
||||
# Create context
|
||||
kubectl config set-context minikube-indri \
|
||||
--cluster=minikube-indri \
|
||||
--user=minikube-indri
|
||||
```
|
||||
|
||||
4. **Usage:**
|
||||
```bash
|
||||
kubectl --context=minikube-indri get nodes
|
||||
# or
|
||||
kubectl config use-context minikube-indri
|
||||
kubectl get nodes
|
||||
```
|
||||
|
||||
**Security Notes:**
|
||||
- Client private key never stored on disk - fetched from 1Password on each kubectl command
|
||||
- CA cert stored on disk (not secret - it's a public key for server verification)
|
||||
- 1Password biometric/password prompt required for credential access
|
||||
- `op` command strips quotes from text fields with `sed 's/^"//; s/"$//'`
|
||||
|
||||
**References:**
|
||||
- [minikube start options](https://minikube.sigs.k8s.io/docs/commands/start/)
|
||||
- [Using kubectl via SSH Tunnel](https://blog.scottlowe.org/2020/06/16/using-kubectl-via-an-ssh-tunnel/)
|
||||
- [SOCKS5 Proxy Access to K8s API](https://kubernetes.ltd/docs/tasks/extend-kubernetes/socks5-proxy-access-api/)
|
||||
- [kubectl-tokensshtunnel](https://github.com/jordiprats/kubectl-tokensshtunnel)
|
||||
- [Securing kubectl config with 1Password](https://blog.mikael.green/post/1password-kubeconfig/)
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue