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:
Erich Blume 2026-01-18 10:12:58 -08:00
commit e032e27b66
4 changed files with 102 additions and 3 deletions

View file

@ -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"

View file

@ -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

View 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
}
}'

View file

@ -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/)
---