diff --git a/src/api/v1/cloudtty.ts b/src/api/v1/cloudtty.ts index 0b3f8c25a..9c2c147dd 100644 --- a/src/api/v1/cloudtty.ts +++ b/src/api/v1/cloudtty.ts @@ -23,6 +23,7 @@ export const connectCloudtty = async (req: OpenApiRequestExt, res: Response): Pr export const deleteCloudtty = async (req: OpenApiRequestExt, res: Response): Promise => { const sessionUser = req.user debug(`deleteCloudtty - ${sessionUser.email} - ${sessionUser.sub}`) - await req.otomi.deleteCloudtty(sessionUser) + const { teamId } = req.query as { teamId: string } + await req.otomi.deleteCloudtty(teamId, sessionUser) res.json({}) } diff --git a/src/api/v2/cloudtty.ts b/src/api/v2/cloudtty.ts index 1c0051cfc..7575dc48d 100644 --- a/src/api/v2/cloudtty.ts +++ b/src/api/v2/cloudtty.ts @@ -23,6 +23,7 @@ export const connectAplCloudtty = async (req: OpenApiRequestExt, res: Response): export const deleteAplCloudtty = async (req: OpenApiRequestExt, res: Response): Promise => { const sessionUser = req.user debug(`deleteCloudtty - ${sessionUser.email} - ${sessionUser.sub}`) - await req.otomi.deleteCloudtty(sessionUser) + const { teamId } = req.query as { teamId: string } + await req.otomi.deleteCloudtty(teamId, sessionUser) res.json({}) } diff --git a/src/k8s_operations.ts b/src/k8s_operations.ts index 67f2bc8f4..4ada7a91c 100644 --- a/src/k8s_operations.ts +++ b/src/k8s_operations.ts @@ -101,34 +101,24 @@ export async function checkPodExists(namespace: string, podName: string): Promis } } -export async function k8sdelete({ - sub, - isPlatformAdmin, - userTeams, -}: { - sub: string - isPlatformAdmin: boolean - userTeams: string[] -}): Promise { +export async function k8sdelete( + namespace: string, + sub: string, + isPlatformAdmin: boolean, + userTeams: string[], +): Promise { const kc = new KubeConfig() kc.loadFromDefault() const k8sApi = kc.makeApiClient(CoreV1Api) const customObjectsApi = kc.makeApiClient(CustomObjectsApi) const rbacAuthorizationV1Api = kc.makeApiClient(RbacAuthorizationV1Api) const resourceName = sub - const namespace = 'team-admin' try { - const apiVersion = 'v1beta1' - const apiGroupAuthz = 'security.istio.io' - const apiGroupVS = 'networking.istio.io' - const pluralAuth = 'authorizationpolicies' - const pluralVS = 'virtualservices' - await customObjectsApi.deleteNamespacedCustomObject({ - group: apiGroupAuthz, - version: apiVersion, + group: 'security.istio.io', + version: 'v1beta1', namespace, - plural: pluralAuth, + plural: 'authorizationpolicies', name: `tty-${resourceName}`, }) @@ -147,10 +137,10 @@ export async function k8sdelete({ await k8sApi.deleteNamespacedService({ name: `tty-${resourceName}`, namespace }) await customObjectsApi.deleteNamespacedCustomObject({ - group: apiGroupVS, - version: apiVersion, + group: 'gateway.networking.k8s.io', + version: 'v1', namespace, - plural: pluralVS, + plural: 'httproutes', name: `tty-${resourceName}`, }) } catch (error) { diff --git a/src/otomi-stack.ts b/src/otomi-stack.ts index 873c6bec6..514c87e89 100644 --- a/src/otomi-stack.ts +++ b/src/otomi-stack.ts @@ -1522,6 +1522,8 @@ export default class OtomiStack { } async connectCloudtty(teamId: string, sessionUser: SessionUser): Promise { + const isAdmin = sessionUser.isPlatformAdmin + const targetNamespace = isAdmin ? 'team-admin' : `team-${teamId}` if (!sessionUser.sub) { debug('No user sub found, cannot connect to shell.') throw new OtomiError(500, 'No user sub found, cannot connect to shell.') @@ -1543,14 +1545,14 @@ export default class OtomiStack { } // if cloudtty shell does not exists then check if the pod is running and return it - if (await checkPodExists('team-admin', `tty-${sessionUser.sub}`)) { + if (await checkPodExists(targetNamespace, `tty-${sessionUser.sub}`)) { return { iFrameUrl: `https://tty.${variables.FQDN}/${sessionUser.sub}` } } if (await pathExists('/tmp/ttyd.yaml')) await unlink('/tmp/ttyd.yaml') //if user is admin then read the manifests from ./dist/src/ttyManifests/adminTtyManifests - const files = sessionUser.isPlatformAdmin + const files = isAdmin ? await readdir('./dist/src/ttyManifests/adminTtyManifests', 'utf-8') : await readdir('./dist/src/ttyManifests', 'utf-8') const filteredFiles = files.filter((file) => file.startsWith('tty')) @@ -1590,15 +1592,15 @@ export default class OtomiStack { ) await writeFile('/tmp/ttyd.yaml', fileContents, 'utf-8') await apply('/tmp/ttyd.yaml') - await watchPodUntilRunning('team-admin', `tty-${sessionUser.sub}`) + await watchPodUntilRunning(targetNamespace, `tty-${sessionUser.sub}`) // check the pod every 30 minutes and terminate it after 2 hours of inactivity const ISACTIVE_INTERVAL = 30 * 60 * 1000 const TERMINATE_TIMEOUT = 2 * 60 * 60 * 1000 const intervalId = setInterval(() => { - getCloudttyActiveTime('team-admin', `tty-${sessionUser.sub}`).then((activeTime: number) => { + getCloudttyActiveTime(targetNamespace, `tty-${sessionUser.sub}`).then((activeTime: number) => { if (activeTime > TERMINATE_TIMEOUT) { - this.deleteCloudtty(sessionUser) + this.deleteCloudtty(teamId, sessionUser) clearInterval(intervalId) debug(`Cloudtty terminated after ${TERMINATE_TIMEOUT / (60 * 60 * 1000)} hours of inactivity`) } @@ -1608,12 +1610,13 @@ export default class OtomiStack { return { iFrameUrl: `https://tty.${variables.FQDN}/${sessionUser.sub}` } } - async deleteCloudtty(sessionUser: SessionUser): Promise { + async deleteCloudtty(teamId: string, sessionUser: SessionUser): Promise { const { sub, isPlatformAdmin, teams } = sessionUser as { sub: string; isPlatformAdmin: boolean; teams: string[] } + const namespace = isPlatformAdmin ? 'team-admin' : `team-${teamId}` const userTeams = teams.map((teamName) => `team-${teamName}`) try { - if (await checkPodExists('team-admin', `tty-${sessionUser.sub}`)) { - await k8sdelete({ sub, isPlatformAdmin, userTeams }) + if (await checkPodExists(namespace, `tty-${sessionUser.sub}`)) { + await k8sdelete(namespace, sub, isPlatformAdmin, userTeams) } } catch (error) { debug('Failed to delete cloudtty') diff --git a/src/ttyManifests/adminTtyManifests/tty_02_Pod.yaml b/src/ttyManifests/adminTtyManifests/tty_02_Pod.yaml index c2e2d2dfd..d84fe2a15 100644 --- a/src/ttyManifests/adminTtyManifests/tty_02_Pod.yaml +++ b/src/ttyManifests/adminTtyManifests/tty_02_Pod.yaml @@ -27,7 +27,7 @@ spec: cpu: '500m' env: - name: NAMESPACE - value: team-$TARGET_TEAM + value: $TARGET_TEAM securityContext: allowPrivilegeEscalation: false capabilities: diff --git a/src/ttyManifests/adminTtyManifests/tty_05_HttpRoute.yaml b/src/ttyManifests/adminTtyManifests/tty_05_HttpRoute.yaml new file mode 100644 index 000000000..68e55a8f1 --- /dev/null +++ b/src/ttyManifests/adminTtyManifests/tty_05_HttpRoute.yaml @@ -0,0 +1,32 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: tty-$SUB + namespace: team-admin +spec: + hostnames: + - tty.$FQDN + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: platform + namespace: istio-system + sectionName: https + rules: + - backendRefs: + - group: "" + kind: Service + name: tty-$SUB + port: 8080 + weight: 1 + matches: + - path: + type: PathPrefix + value: /$SUB + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: / +--- diff --git a/src/ttyManifests/adminTtyManifests/tty_05_Vs.yaml b/src/ttyManifests/adminTtyManifests/tty_05_Vs.yaml deleted file mode 100644 index 9dd644a58..000000000 --- a/src/ttyManifests/adminTtyManifests/tty_05_Vs.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: networking.istio.io/v1beta1 -kind: VirtualService -metadata: - name: tty-$SUB - namespace: team-admin -spec: - gateways: - - team-admin/team-admin-public-tlsterm - hosts: - - tty.$FQDN - http: - - match: - - uri: - prefix: /platform-logout - redirect: - authority: auth.$FQDN - uri: /oauth2/sign_out?rd=https%3A%2F%2Fkeycloak.$FQDN%2Frealms%2Fotomi%2Fprotocol%2Fopenid-connect%2Flogout%3Fpost_logout_redirect_uri%3Dhttps%3A%2F%2Fconsole.$FQDN%26client_id%3Dotomi - redirectCode: 302 - - match: - - uri: - prefix: /$SUB - rewrite: - uri: / - route: - - destination: - host: tty-$SUB.team-admin.svc.cluster.local - port: - number: 8080 - headers: - request: - set: - X-Forwarded-Proto: https ---- - diff --git a/src/ttyManifests/tty_00_Authz.yaml b/src/ttyManifests/tty_00_Authz.yaml index 1d64c4038..0e022c1ce 100644 --- a/src/ttyManifests/tty_00_Authz.yaml +++ b/src/ttyManifests/tty_00_Authz.yaml @@ -2,7 +2,7 @@ apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: tty-$SUB - namespace: team-admin + namespace: $TARGET_TEAM spec: selector: matchLabels: @@ -13,4 +13,3 @@ spec: - key: request.auth.claims[sub] values: ['$SUB'] --- - diff --git a/src/ttyManifests/tty_01_Sa.yaml b/src/ttyManifests/tty_01_Sa.yaml index 3c2993caa..1abeb0fd3 100644 --- a/src/ttyManifests/tty_01_Sa.yaml +++ b/src/ttyManifests/tty_01_Sa.yaml @@ -2,6 +2,5 @@ apiVersion: v1 kind: ServiceAccount metadata: name: tty-$SUB - namespace: team-admin + namespace: $TARGET_TEAM --- - diff --git a/src/ttyManifests/tty_02_Pod.yaml b/src/ttyManifests/tty_02_Pod.yaml index c2e2d2dfd..a1a17a19f 100644 --- a/src/ttyManifests/tty_02_Pod.yaml +++ b/src/ttyManifests/tty_02_Pod.yaml @@ -5,7 +5,7 @@ metadata: app: tty-$SUB otomi: tty name: tty-$SUB - namespace: team-admin + namespace: $TARGET_TEAM spec: serviceAccountName: tty-$SUB securityContext: @@ -27,7 +27,7 @@ spec: cpu: '500m' env: - name: NAMESPACE - value: team-$TARGET_TEAM + value: $TARGET_TEAM securityContext: allowPrivilegeEscalation: false capabilities: diff --git a/src/ttyManifests/tty_03_Rolebinding.yaml b/src/ttyManifests/tty_03_Rolebinding.yaml index 0f045b4f8..99d3a166f 100644 --- a/src/ttyManifests/tty_03_Rolebinding.yaml +++ b/src/ttyManifests/tty_03_Rolebinding.yaml @@ -10,6 +10,5 @@ roleRef: subjects: - kind: ServiceAccount name: tty-$SUB - namespace: team-admin + namespace: $TARGET_TEAM --- - diff --git a/src/ttyManifests/tty_04_Svc.yaml b/src/ttyManifests/tty_04_Svc.yaml index 7ad6ae4f5..5d5c9d1d2 100644 --- a/src/ttyManifests/tty_04_Svc.yaml +++ b/src/ttyManifests/tty_04_Svc.yaml @@ -4,7 +4,7 @@ metadata: labels: app: tty-$SUB name: tty-$SUB - namespace: team-admin + namespace: $TARGET_TEAM spec: ports: - name: 8080-8080 @@ -15,4 +15,3 @@ spec: app: tty-$SUB type: ClusterIP --- - diff --git a/src/ttyManifests/tty_05_HttpRoute.yaml b/src/ttyManifests/tty_05_HttpRoute.yaml new file mode 100644 index 000000000..2d9680855 --- /dev/null +++ b/src/ttyManifests/tty_05_HttpRoute.yaml @@ -0,0 +1,32 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: tty-$SUB + namespace: $TARGET_TEAM +spec: + hostnames: + - tty.$FQDN + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: platform + namespace: istio-system + sectionName: https + rules: + - backendRefs: + - group: "" + kind: Service + name: tty-$SUB + port: 8080 + weight: 1 + matches: + - path: + type: PathPrefix + value: /$SUB + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: / +--- diff --git a/src/ttyManifests/tty_05_Vs.yaml b/src/ttyManifests/tty_05_Vs.yaml deleted file mode 100644 index 9dd644a58..000000000 --- a/src/ttyManifests/tty_05_Vs.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: networking.istio.io/v1beta1 -kind: VirtualService -metadata: - name: tty-$SUB - namespace: team-admin -spec: - gateways: - - team-admin/team-admin-public-tlsterm - hosts: - - tty.$FQDN - http: - - match: - - uri: - prefix: /platform-logout - redirect: - authority: auth.$FQDN - uri: /oauth2/sign_out?rd=https%3A%2F%2Fkeycloak.$FQDN%2Frealms%2Fotomi%2Fprotocol%2Fopenid-connect%2Flogout%3Fpost_logout_redirect_uri%3Dhttps%3A%2F%2Fconsole.$FQDN%26client_id%3Dotomi - redirectCode: 302 - - match: - - uri: - prefix: /$SUB - rewrite: - uri: / - route: - - destination: - host: tty-$SUB.team-admin.svc.cluster.local - port: - number: 8080 - headers: - request: - set: - X-Forwarded-Proto: https ---- -