Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ bin/*
node_modules
openstack.rc
.qodo
.DS_STORE
pkg/vpwned/vendor/*
pkg/vpwned/bin/vpwctl
proto_dependencies
proto_dependencies
47 changes: 47 additions & 0 deletions KNOWN_ISSUES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Known Issues

## API Endpoints Return 401 Through Ingress

**Status**: Under Investigation
**Severity**: High
**Date**: 2025-10-16

### Symptom
Browser requests to `/api/v1/*` and `/apis/vjailbreak.k8s.pf9.io/*` return 401 when accessed through ingress (https://10.9.0.61/apis/...).

### What Works
- Direct K8s API access with SA token: ✅ Returns 200
- UI login and dashboard: ✅ Works correctly
- OAuth2/Dex authentication: ✅ Functional
- SDK endpoints: ✅ Some working

### What Doesn't Work
- `/api/v1/namespaces` → 401 from nginx
- `/apis/vjailbreak.k8s.pf9.io/...` → 401 from nginx

### Attempted Fixes
1. ✗ Ingress priority annotations (`priority: 100`)
2. ✗ Explicit `auth-url: ""` to disable OAuth2
3. ✗ Backend protocol `HTTPS` with `proxy-ssl-verify: false`
4. ✗ Narrowing UI ingress paths (not using `/ Prefix`)
5. ✗ Custom header ConfigMap for Authorization
6. ✗ Removing UI ingress entirely

### Root Cause Hypothesis
The architecture likely requires the **UI backend to proxy API requests** rather than browsers sending service account tokens directly through ingress. This is a more secure pattern where:
- Browser → UI (with OAuth2 session)
- UI backend → K8s API (with SA token)
- Browser never handles SA tokens

### Next Steps
1. Check how cluster 10.9.2.145 handles `/apis` requests
2. Verify if UI code proxies API calls server-side
3. Consider updating UI to proxy K8s API requests through its backend
4. Review nginx ingress controller global settings

### Workaround
For now, the UI can make API calls from its backend pod where SA tokens work correctly.

---

**Last Updated**: 2025-10-16 23:51 IST
186 changes: 186 additions & 0 deletions deploy/04ui-with-dex.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: vjailbreak-ui
namespace: migration-system
spec:
replicas: 1
selector:
matchLabels:
app: vjailbreak-ui
template:
metadata:
labels:
app: vjailbreak-ui
spec:
serviceAccountName: vjailbreak-ui-sa
containers:
- name: vjailbreak-ui-container
image: quay.io/platform9/vjailbreak-ui:2458-038b041
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
env:
# Pass service account token location
- name: SERVICE_ACCOUNT_TOKEN_PATH
value: /var/run/secrets/kubernetes.io/serviceaccount/token
# Dex configuration
- name: DEX_ISSUER
value: "https://HOST_IP/dex"
- name: OAUTH2_PROXY_URL
value: "http://oauth2-proxy.oauth2-proxy.svc.cluster.local:4180"
# API server configuration
- name: KUBERNETES_SERVICE_HOST
value: kubernetes.default.svc.cluster.local
- name: KUBERNETES_SERVICE_PORT
value: "443"
volumeMounts:
- name: sa-token
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
readOnly: true
volumes:
- name: sa-token
projected:
sources:
- serviceAccountToken:
path: token
expirationSeconds: 7200
- configMap:
name: kube-root-ca.crt
items:
- key: ca.crt
path: ca.crt
- downwardAPI:
items:
- path: namespace
fieldRef:
fieldPath: metadata.namespace

---
apiVersion: v1
kind: Service
metadata:
name: vjailbreak-ui-service
namespace: migration-system
spec:
selector:
app: vjailbreak-ui
type: ClusterIP
ports:
- protocol: TCP
port: 80
targetPort: 80

---
# API Ingress WITHOUT OAuth2 auth - for Kubernetes API calls and vpwned SDK
# These use service account tokens and should not be intercepted
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: vjailbreak-api-ingress
namespace: migration-system
annotations:
# Backend protocol for Kubernetes API
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
nginx.ingress.kubernetes.io/proxy-ssl-verify: "false"
# Pass Authorization header to backend
nginx.ingress.kubernetes.io/proxy-set-headers: "nginx-ingress/custom-headers"
# Explicitly disable OAuth2 auth for API paths
nginx.ingress.kubernetes.io/auth-url: ""
# Higher priority to match before UI ingress catches everything
nginx.ingress.kubernetes.io/priority: "100"
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: kubernetes
port:
number: 443
- path: /apis
pathType: Prefix
backend:
service:
name: kubernetes
port:
number: 443

---
# UI Ingress WITH OAuth2 Proxy authentication
# For dashboard and static assets only
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: vjailbreak-ui-ingress
namespace: migration-system
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
# Force HTTPS redirect
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
# Lower priority than API ingress (default is 0)
nginx.ingress.kubernetes.io/priority: "10"
# OAuth2 Proxy annotations for UI authentication
nginx.ingress.kubernetes.io/auth-url: "http://oauth2-proxy.oauth2-proxy.svc.cluster.local:4180/oauth2/auth"
nginx.ingress.kubernetes.io/auth-signin: "https://$host/oauth2/start?rd=$scheme://$host/dashboard"
nginx.ingress.kubernetes.io/auth-response-headers: "X-Auth-Request-User,X-Auth-Request-Email,X-Auth-Request-Access-Token,Authorization"
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /dashboard
pathType: Prefix
backend:
service:
name: vjailbreak-ui-service
port:
number: 80
- path: /onboarding
pathType: Prefix
backend:
service:
name: vjailbreak-ui-service
port:
number: 80
- path: /assets
pathType: Prefix
backend:
service:
name: vjailbreak-ui-service
port:
number: 80
- path: /
pathType: Exact
backend:
service:
name: vjailbreak-ui-service
port:
number: 80

---
# OAuth2 Proxy Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: oauth2-proxy-ingress
namespace: oauth2-proxy
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /oauth2
pathType: Prefix
backend:
service:
name: oauth2-proxy
port:
number: 4180

35 changes: 35 additions & 0 deletions k8s/cert-manager/00-selfsigned-issuer.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
# Self-signed ClusterIssuer for IP-based deployments
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-issuer
spec:
selfSigned: {}
---
# CA Certificate for signing
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: vjailbreak-ca
namespace: cert-manager
spec:
isCA: true
commonName: vjailbreak-ca
secretName: vjailbreak-ca-secret
privateKey:
algorithm: ECDSA
size: 256
issuerRef:
name: selfsigned-issuer
kind: ClusterIssuer
group: cert-manager.io
---
# CA Issuer using the CA certificate
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: vjailbreak-ca-issuer
spec:
ca:
secretName: vjailbreak-ca-secret
7 changes: 7 additions & 0 deletions k8s/dex/00-namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: Namespace
metadata:
name: dex
labels:
app.kubernetes.io/name: dex
app.kubernetes.io/component: identity-provider
61 changes: 61 additions & 0 deletions k8s/dex/01-configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: dex-config
namespace: dex
data:
config.yaml: |
# Issuer URL - Dex is exposed via Ingress with HTTPS (SSL termination at ingress)
issuer: https://HOST_IP/dex

storage:
type: kubernetes
config:
inCluster: true

web:
http: 0.0.0.0:5556
allowedOrigins: ['*']

# Branding customization
frontend:
issuer: "vJailbreak - VM Migration Platform"
logoURL: ""
dir: ""
theme: light

telemetry:
http: 0.0.0.0:5558

# OAuth2 configuration
oauth2:
skipApprovalScreen: true
responseTypes: ["code", "token", "id_token"]

# Enable password grant for local authentication
enablePasswordDB: true

# Static passwords for local authentication
staticPasswords:
- email: "admin@vjailbreak.local"
# bcrypt hash of "admin" - MUST be changed on first login
# Generated with: echo -n "admin" | htpasswd -BinC 10 admin | cut -d: -f2
hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
username: "admin"
userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"

# Static clients configuration
staticClients:
- id: vjailbreak-ui
redirectURIs:
- 'https://HOST_IP/oauth2/callback'
name: 'vJailbreak UI'
secret: vjailbreak-ui-secret-change-me

- id: vjailbreak-cli
redirectURIs:
- 'urn:ietf:wg:oauth:2.0:oob'
- 'http://localhost:8000'
- 'http://localhost:5555/callback'
name: 'vJailbreak CLI'
public: true
31 changes: 31 additions & 0 deletions k8s/dex/02-rbac.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: dex
namespace: dex
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: dex
rules:
- apiGroups: ["dex.coreos.com"]
resources: ["*"]
verbs: ["*"]
- apiGroups: ["apiextensions.k8s.io"]
resources: ["customresourcedefinitions"]
verbs: ["create", "get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: dex
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: dex
subjects:
- kind: ServiceAccount
name: dex
namespace: dex
Loading
Loading