From d1e536614f8f17c5cf1650235261dbab0ebe7cc0 Mon Sep 17 00:00:00 2001 From: Renku Bot Date: Wed, 15 Apr 2026 14:24:09 +0000 Subject: [PATCH 1/7] chore: create release 2.17.0 From 3f5b90a45c430e7fe5ca81f8567122948b057e95 Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Tue, 14 Apr 2026 13:21:28 +0200 Subject: [PATCH 2/7] feat: add configuration options for building image from private repositories --- helm-chart/renku/templates/data-service/deployment.yaml | 4 ++++ helm-chart/renku/values.yaml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/helm-chart/renku/templates/data-service/deployment.yaml b/helm-chart/renku/templates/data-service/deployment.yaml index cb2d99dfb1..0efd1934a5 100644 --- a/helm-chart/renku/templates/data-service/deployment.yaml +++ b/helm-chart/renku/templates/data-service/deployment.yaml @@ -130,6 +130,8 @@ spec: value: {{ .Values.dataService.imageBuilders.enabled | quote }} - name: BUILD_OUTPUT_IMAGE_PREFIX value: {{ .Values.dataService.imageBuilders.outputImagePrefix | default "" | quote }} + - name: BUILD_OUTPUT_PRIVATE_IMAGE_PREFIX + value: {{ .Values.dataService.imageBuilders.outputPrivateImagePrefix | default "" | quote }} - name: BUILD_BUILDER_IMAGE value: {{ .Values.dataService.imageBuilders.builderImage | default "" | quote }} - name: BUILD_RUN_IMAGE @@ -138,6 +140,8 @@ spec: value: {{ .Values.dataService.imageBuilders.strategyName | default "" | quote }} - name: BUILD_PUSH_SECRET_NAME value: {{ .Values.dataService.imageBuilders.pushSecretName | default "" | quote }} + - name: BUILD_PUSH_PRIVATE_SECRET_NAME + value: {{ .Values.dataService.imageBuilders.pushPrivateSecretName | default "" | quote }} - name: BUILD_RUN_RETENTION_AFTER_FAILED_SECONDS value: {{ .Values.dataService.imageBuilders.buildRunRetentionAfterFailedSeconds | default "" | quote }} - name: BUILD_RUN_RETENTION_AFTER_SUCCEEDED_SECONDS diff --git a/helm-chart/renku/values.yaml b/helm-chart/renku/values.yaml index e9f7a08ce5..1f638180af 100644 --- a/helm-chart/renku/values.yaml +++ b/helm-chart/renku/values.yaml @@ -1237,6 +1237,8 @@ dataService: enabled: false ## The container image prefix for images built from code outputImagePrefix: harbor.dev.renku.ch/renku-build/ + ## The container image prefix for images built from private code + outputPrivateImagePrefix: harbor.dev.renku.ch/renku-private-build/ ## The builder image (see https://buildpacks.io/docs/for-platform-operators/concepts/builder/) builderImage: "ghcr.io/swissdatasciencecenter/renku-frontend-buildpacks/selector:0.4.0" ## The run image (see https://buildpacks.io/docs/for-platform-operators/concepts/base-images/) @@ -1263,6 +1265,8 @@ dataService: # value: arm64 ## The name of the secret used to push images built from code. pushSecretName: renku-build-docker-secret + ## The name of the secret used to push images built from private code. + pushPrivateSecretName: renku-build-private-docker-secret ## The TTL for BuildRuns buildRunRetentionAfterFailedSeconds: 86400 buildRunRetentionAfterSucceededSeconds: 86400 From fa9146b45b7d7c5b7270ed85ba304800cdc6cd55 Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Tue, 21 Apr 2026 10:01:49 +0200 Subject: [PATCH 3/7] feat: add configuration for session private image pull secret This supplements the building part allowing to use independent secrets for pushing during build and pulling for session creation. This secret is not accessible by the user. --- helm-chart/renku/templates/data-service/deployment.yaml | 2 ++ helm-chart/renku/values.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/helm-chart/renku/templates/data-service/deployment.yaml b/helm-chart/renku/templates/data-service/deployment.yaml index 0efd1934a5..751d46c141 100644 --- a/helm-chart/renku/templates/data-service/deployment.yaml +++ b/helm-chart/renku/templates/data-service/deployment.yaml @@ -142,6 +142,8 @@ spec: value: {{ .Values.dataService.imageBuilders.pushSecretName | default "" | quote }} - name: BUILD_PUSH_PRIVATE_SECRET_NAME value: {{ .Values.dataService.imageBuilders.pushPrivateSecretName | default "" | quote }} + - name: BUILD_PULL_PRIVATE_SECRET_NAME + value: {{ .Values.dataService.imageBuilders.pullPrivateSecretName | default "" | quote }} - name: BUILD_RUN_RETENTION_AFTER_FAILED_SECONDS value: {{ .Values.dataService.imageBuilders.buildRunRetentionAfterFailedSeconds | default "" | quote }} - name: BUILD_RUN_RETENTION_AFTER_SUCCEEDED_SECONDS diff --git a/helm-chart/renku/values.yaml b/helm-chart/renku/values.yaml index 1f638180af..c603854ff9 100644 --- a/helm-chart/renku/values.yaml +++ b/helm-chart/renku/values.yaml @@ -1267,6 +1267,8 @@ dataService: pushSecretName: renku-build-docker-secret ## The name of the secret used to push images built from private code. pushPrivateSecretName: renku-build-private-docker-secret + ## The name of the secret used to pull images built from private code. + pullPrivateSecretName: renku-pull-private-docker-secret ## The TTL for BuildRuns buildRunRetentionAfterFailedSeconds: 86400 buildRunRetentionAfterSucceededSeconds: 86400 From 4c68e3432b1bb9339beaca4b22e2ab80f8eba8a6 Mon Sep 17 00:00:00 2001 From: Renku Bot Date: Wed, 15 Apr 2026 14:24:09 +0000 Subject: [PATCH 4/7] chore: create release 2.17.0 From 20017017700feea1d005f8bffedc3da0d88f670d Mon Sep 17 00:00:00 2001 From: Lorenzo Cavazzi <43481553+lorenzo-cavazzi@users.noreply.github.com> Date: Thu, 30 Apr 2026 09:30:29 +0200 Subject: [PATCH 5/7] feat: new data connector linking modal and tests update (#4431) --- .../cypress/e2e/v2/dataConnectors.cy.ts | 107 +++++++++--------- .../cypress/support/utils/dataConnectors.ts | 2 - helm-chart/renku/values.yaml | 10 +- 3 files changed, 58 insertions(+), 61 deletions(-) diff --git a/cypress-tests/cypress/e2e/v2/dataConnectors.cy.ts b/cypress-tests/cypress/e2e/v2/dataConnectors.cy.ts index 03709c75ae..d68e773c74 100644 --- a/cypress-tests/cypress/e2e/v2/dataConnectors.cy.ts +++ b/cypress-tests/cypress/e2e/v2/dataConnectors.cy.ts @@ -10,8 +10,11 @@ import { deleteDataConnector, } from "../../support/utils/dataConnectors"; import { login } from "../../support/utils/general"; +import { verifySearchIndexing } from "../../support/utils/search"; const sessionId = ["dataConnectors", getRandomString()]; +const searchDataConnectorType = "type:DataConnector"; +const searchDataConnectorSlug = "slug:"; describe("Data Connectors", () => { const randomString = getRandomString(); @@ -19,7 +22,7 @@ describe("Data Connectors", () => { const projectSlug = `project-for-data-connector-tests-${randomString}`; let userNamespace: string; let dataConnectorName: string; - let projectId: string; + let projectId: string | undefined; let groupName: string; let groupSlug: string; @@ -43,7 +46,7 @@ describe("Data Connectors", () => { }); after(() => { - deleteProject(projectId); + if (projectId) deleteProject(projectId); }); beforeEach(() => { @@ -121,8 +124,6 @@ describe("Data Connectors", () => { cy.getDataCy("data-connector-menu-dropdown").click(); cy.getDataCy("data-connector-delete").click(); }); - - // Confirm deletion by typing the slug cy.getDataCy("delete-confirmation-input").type(dataConnectorName); cy.getDataCy("delete-data-connector-modal-button").click(); @@ -248,7 +249,6 @@ describe("Data Connectors", () => { projectId, ); - // ? Currently, data connectors newly linked might not appear immediately visitCurrentProject(); cy.getDataCy("data-connector-box") .find(`[data-cy=data-connector-name]`) @@ -279,70 +279,54 @@ describe("Data Connectors", () => { cy.getDataCy("data-connector-edit-close-button").click(); // Verify the data connector is present with the edited name - visitCurrentProject(); cy.getDataCy("data-connector-box") .find(`[data-cy=data-connector-name]`) .contains(newName); }); - it("Link an existing data connector to a project", () => { - // Create a data connector not linked to a project + it("Link and unlink an existing data connector to a project", () => { + // Create a data connector not linked to a project and check it has been indexed const dataConnectorIdentifier = `${userNamespace}/${dataConnectorName}`; createDataConnector(dataConnectorIdentifier); + verifySearchIndexing( + `${searchDataConnectorType} ${searchDataConnectorSlug}${dataConnectorName}`, + 1, + ); - // Now link the data connector to the project + // Link the data connector to the project visitCurrentProject(); cy.getDataCy("add-data-connector").click(); - cy.getDataCy("project-data-controller-mode-link").click(); - - // Enter the data connector identifier - cy.get("#data-connector-identifier") + cy.getDataCy("data-connector-search-input") .should("be.empty") .type(dataConnectorIdentifier); - cy.getDataCy("link-data-connector-button").click(); + cy.getDataCy("data-connector-search-body") + .contains("[data-cy=link-data-connector-list-item]", dataConnectorName) + .find(`[data-cy=data-connector-link-button]`) + .click(); // Verify the data connector is linked to the project - visitCurrentProject(); + cy.getDataCy("project-data-connector-connect-header") + .find('button[data-bs-dismiss="modal"]') + .click(); cy.getDataCy("data-connector-box") .find(`[data-cy=data-connector-name]`) .contains(dataConnectorName); - }); - it("Unlink a data connector from a project", () => { - // Create a data connector not linked to a project - const dataConnectorIdentifier = `${userNamespace}/${dataConnectorName}`; - createDataConnector(dataConnectorIdentifier); - - // Now link the data connector to the project - visitCurrentProject(); - cy.getDataCy("add-data-connector").click(); - cy.getDataCy("project-data-controller-mode-link").click(); - - // Enter the data connector identifier - cy.get("#data-connector-identifier") - .should("be.empty") - .type(dataConnectorIdentifier); - cy.getDataCy("link-data-connector-button").click(); - - // ? Currently, data connectors newly linked might not appear immediately - visitCurrentProject(); + // Unlink the data connector from the project cy.getDataCy("data-connector-box") .find(`[data-cy=data-connector-name]`) .contains(dataConnectorName) .click(); - cy.getDataCy("data-connector-view").within(() => { cy.getDataCy("data-connector-title") .should("be.visible") .contains(dataConnectorName); cy.getDataCy("data-connector-menu-dropdown").click(); - cy.getDataCy("data-connector-unlink").should("be.visible").click(); + cy.getDataCy("data-connector-unlink").click(); }); - cy.getDataCy("delete-data-connector-modal-button").click(); // Verify the data connector is no longer linked to the project - visitCurrentProject(); cy.getDataCy("data-connector-box") .contains(`[data-cy=data-connector-name]`, dataConnectorName) .should("not.exist"); @@ -359,31 +343,38 @@ describe("Data Connectors", () => { slug: otherProjectName, visibility: "private", }).then((response) => { - const otherProjectId = response.body.id; + const otherProjectId = response.body.id ?? ""; // Defer-delete the other project (which will also delete the data connector) cy.defer(() => { - deleteProject(otherProjectId); + if (otherProjectId) deleteProject(otherProjectId); }); - // Create a data connector in the other project + // Create a data connector in the other project and check it has been indexed const dataConnectorIdentifier = `${userNamespace}/${otherProjectName}/${dataConnectorName}`; createDataConnector(dataConnectorIdentifier, otherProjectId); + verifySearchIndexing( + `${searchDataConnectorType} ${searchDataConnectorSlug}${dataConnectorName}`, + 1, + ); // Navigate to the main project visitCurrentProject(); // Link the data connector from the other project to the main project cy.getDataCy("add-data-connector").click(); - cy.getDataCy("project-data-controller-mode-link").click(); - - cy.get("#data-connector-identifier") + cy.getDataCy("data-connector-search-input") .should("be.empty") .type(dataConnectorIdentifier); - cy.getDataCy("link-data-connector-button").click(); + cy.getDataCy("data-connector-search-body") + .contains("[data-cy=link-data-connector-list-item]", dataConnectorName) + .find(`[data-cy=data-connector-link-button]`) + .click(); // Verify the data connector is linked to the main project - visitCurrentProject(); + cy.getDataCy("project-data-connector-connect-header") + .find('button[data-bs-dismiss="modal"]') + .click(); cy.getDataCy("data-connector-box") .find(`[data-cy=data-connector-name]`) .contains(dataConnectorName) @@ -395,12 +386,11 @@ describe("Data Connectors", () => { .should("be.visible") .contains(dataConnectorName); cy.getDataCy("data-connector-menu-dropdown").click(); - cy.getDataCy("data-connector-unlink").should("be.visible").click(); + cy.getDataCy("data-connector-unlink").click(); }); cy.getDataCy("delete-data-connector-modal-button").click(); // Verify the data connector is no longer linked to the main project - visitCurrentProject(); cy.getDataCy("data-connector-box") .contains(`[data-cy=data-connector-name]`, dataConnectorName) .should("not.exist"); @@ -468,21 +458,28 @@ describe("Data Connectors", () => { // Create a data connector owned by the group const dataConnectorIdentifier = `${groupSlug}/${dataConnectorName}`; createDataConnector(dataConnectorIdentifier); + verifySearchIndexing( + `${searchDataConnectorType} ${searchDataConnectorSlug}${dataConnectorName}`, + 1, + ); // Navigate to the user's project visitCurrentProject(); // Link the group data connector to the user's project cy.getDataCy("add-data-connector").click(); - cy.getDataCy("project-data-controller-mode-link").click(); - - // Enter the data connector identifier - cy.get("#data-connector-identifier") + cy.getDataCy("data-connector-search-input") .should("be.empty") .type(dataConnectorIdentifier); - cy.getDataCy("link-data-connector-button").click(); + cy.getDataCy("data-connector-search-body") + .contains("[data-cy=link-data-connector-list-item]", dataConnectorName) + .find(`[data-cy=data-connector-link-button]`) + .click(); // Verify the data connector is linked to the project + cy.getDataCy("project-data-connector-connect-header") + .find('button[data-bs-dismiss="modal"]') + .click(); cy.getDataCy("data-connector-box") .find(`[data-cy=data-connector-name]`) .contains(dataConnectorName) @@ -493,8 +490,8 @@ describe("Data Connectors", () => { cy.getDataCy("data-connector-title") .should("be.visible") .contains(dataConnectorName); - cy.getDataCy("data-connector-menu-dropdown").should("be.visible").click(); - cy.getDataCy("data-connector-unlink").should("be.visible").click(); + cy.getDataCy("data-connector-menu-dropdown").click(); + cy.getDataCy("data-connector-unlink").click(); }); cy.getDataCy("delete-data-connector-modal-button").click(); diff --git a/cypress-tests/cypress/support/utils/dataConnectors.ts b/cypress-tests/cypress/support/utils/dataConnectors.ts index 8173f4e612..aca69a98cb 100644 --- a/cypress-tests/cypress/support/utils/dataConnectors.ts +++ b/cypress-tests/cypress/support/utils/dataConnectors.ts @@ -46,11 +46,9 @@ export function createDataConnector( visibility: body.visibility || "private", description: body.description || "Test data connector description", storage: { - storage_type: "s3", configuration: { type: "s3", provider: "AWS", - region: "us-east-1", }, source_path: "giab", target_path: body.slug || "/", diff --git a/helm-chart/renku/values.yaml b/helm-chart/renku/values.yaml index a660a97c81..83c5aa15f4 100644 --- a/helm-chart/renku/values.yaml +++ b/helm-chart/renku/values.yaml @@ -548,7 +548,7 @@ ui: replicaCount: 1 image: repository: renku/renku-ui - tag: "4.21.0" + tag: "4.22.0" pullPolicy: IfNotPresent ## Optionally specify an array of imagePullSecrets. ## Secrets must be manually created in the namespace. @@ -740,7 +740,7 @@ ui: keepCookies: [] image: repository: renku/renku-ui-server - tag: "4.21.0" + tag: "4.22.0" pullPolicy: IfNotPresent imagePullSecrets: [] nameOverride: "" @@ -822,7 +822,8 @@ dlf-chart: enabled: false dataset-operator-chart: enabled: true -csi-rclone: {} +csi-rclone: + {} # This section is only relevant if you are installing csi-rclone as part of Renku ## Name of the csi storage class to use for RClone/Cloudstorage. Should be unique per cluster. # storageClassName: csi-rclone @@ -1244,7 +1245,8 @@ dataService: ## The name of the BuildStrategy to use for image builds. strategyName: renku-buildpacks-v3 ## Configuration overrides for specific target platforms - platformOverrides: {} + platformOverrides: + {} # linux/arm64: # builderImage: "ghcr.io/swissdatasciencecenter/renku-frontend-buildpacks/cuda-selector:0.5.1" # runImage: "ghcr.io/swissdatasciencecenter/renku-frontend-buildpacks/cuda-run-image:0.5.1" From 71113ce4a6ae9c571dc55dbfbee423cf2adc0143 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Thu, 30 Apr 2026 10:45:13 +0200 Subject: [PATCH 6/7] feat: remove enableInternalGitlab value from the chart (#4446) Remove the helm chart value `enableInternalGitlab`. Connecting to GitLab instances is now done exclusively through integrations. --- .../templates/data-service/deployment.yaml | 2 +- .../renku/templates/gateway/configmap.yaml | 12 ++---------- .../gateway/deployment-revproxy.yaml | 19 ------------------- helm-chart/renku/values.yaml | 12 ------------ helm-chart/values.yaml.changelog.md | 4 ++++ 5 files changed, 7 insertions(+), 42 deletions(-) diff --git a/helm-chart/renku/templates/data-service/deployment.yaml b/helm-chart/renku/templates/data-service/deployment.yaml index cb2d99dfb1..f80fa6b0c7 100644 --- a/helm-chart/renku/templates/data-service/deployment.yaml +++ b/helm-chart/renku/templates/data-service/deployment.yaml @@ -159,7 +159,7 @@ spec: - name: V1_SESSIONS_ENABLED value: {{ .Values.ui.client.supportLegacySessions | default false | quote }} - name: ENABLE_INTERNAL_GITLAB - value: {{ .Values.enableInternalGitlab | default false | quote }} + value: "false" - name: POSTHOG_ENABLED value: {{ .Values.posthog.enabled | quote }} - name: LOG_FORMAT_STYLE diff --git a/helm-chart/renku/templates/gateway/configmap.yaml b/helm-chart/renku/templates/gateway/configmap.yaml index 922168ffc8..d48880f9a2 100644 --- a/helm-chart/renku/templates/gateway/configmap.yaml +++ b/helm-chart/renku/templates/gateway/configmap.yaml @@ -36,7 +36,7 @@ data: audience: renku authorizedParty: renku-cli revproxy: - enableInternalGitlab: {{ .Values.enableInternalGitlab | default false }} + enableInternalGitlab: "false" renkuBaseUrl: {{ include "renku.baseUrl" . | quote }} externalGitlabUrl: {{ .Values.global.gitlab.url | default "" | quote }} k8sNamespace: {{ .Release.Namespace }} @@ -47,7 +47,7 @@ data: uiserver: {{ printf "http://%s" (include "ui-server.fullname" .) | quote }} search: {{ printf "http://%s-search-api" .Release.Name | quote }} login: - enableInternalGitlab: {{ .Values.enableInternalGitlab | default false }} + enableInternalGitlab: "false" renkuBaseUrl: {{ include "renku.baseUrl" . | quote }} loginRoutesBasePath: "/api/auth" defaultAppRedirectURL: {{ include "renku.baseUrl" . | quote }} @@ -60,14 +60,6 @@ data: scopes: ["profile", "email", "openid", "microprofile-jwt"] callbackURI: {{ printf "%s/api/auth/callback" (include "renku.baseUrl" .) }} usePKCE: false - {{- if .Values.enableInternalGitlab }} - gitlab: - issuer: {{ .Values.global.gitlab.url | quote }} - clientID: {{ .Values.gateway.gitlabClientId | default .Values.global.gateway.gitlabClientId | quote }} - scopes: ["openid", "api", "read_user", "read_repository"] - callbackURI: {{ printf "%s/api/auth/callback" (include "renku.baseUrl" .) }} - usePKCE: false - {{- end }} oldGitLabLogout: {{ .Values.gateway.oldGitLabLogout | default false }} logoutGitLabUponRenkuLogout: {{ .Values.gateway.logoutGitLabUponRenkuLogout | default true }} redis: diff --git a/helm-chart/renku/templates/gateway/deployment-revproxy.yaml b/helm-chart/renku/templates/gateway/deployment-revproxy.yaml index a9715de9e0..4b6a33f8b1 100644 --- a/helm-chart/renku/templates/gateway/deployment-revproxy.yaml +++ b/helm-chart/renku/templates/gateway/deployment-revproxy.yaml @@ -71,13 +71,6 @@ spec: secretKeyRef: name: {{ cat (include "renku.fullname" .) "-gateway" | nospace }} key: oidcClientSecret - {{- if .Values.enableInternalGitlab }} - - name: GATEWAY_LOGIN_PROVIDERS_GITLAB_CLIENTSECRET - valueFrom: - secretKeyRef: - name: {{ cat (include "renku.fullname" .) "-gateway" | nospace }} - key: gitlabClientSecret - {{- end }} - name: GATEWAY_LOGIN_TOKENENCRYPTION_SECRETKEY valueFrom: secretKeyRef: @@ -93,18 +86,6 @@ spec: secretKeyRef: name: {{ cat (include "renku.fullname" .) "-gateway" | nospace }} key: cookieHashKey - {{- if .Values.enableInternalGitlab }} - - name: GATEWAY_LOGIN_PROVIDERS_GITLAB_COOKIEENCODINGKEY - valueFrom: - secretKeyRef: - name: {{ cat (include "renku.fullname" .) "-gateway" | nospace }} - key: cookieEncodingKey - - name: GATEWAY_LOGIN_PROVIDERS_GITLAB_COOKIEHASHKEY - valueFrom: - secretKeyRef: - name: {{ cat (include "renku.fullname" .) "-gateway" | nospace }} - key: cookieHashKey - {{- end }} - name: GATEWAY_MONITORING_SENTRY_DSN value: {{ .Values.gateway.sentry.dsn }} - name: GATEWAY_POSTHOG_ENABLED diff --git a/helm-chart/renku/values.yaml b/helm-chart/renku/values.yaml index 83c5aa15f4..6871645aad 100644 --- a/helm-chart/renku/values.yaml +++ b/helm-chart/renku/values.yaml @@ -1349,18 +1349,6 @@ secretsStorage: tolerations: [] affinity: {} -# When this is set to false the gateway and data service will ignore the Gitlab -# that can be integrated with Renku and will not ask users to log into this Gitlab. -# NOTE: This flag has no effect on the core service and knowledge graph. Therefore, -# setting this to false should only be done if the enableV1Services flag is also false. -# When this is set to false the gateway will not inject the internal gitlab tokens and -# the data service will not require them and if tokens are passed it will just ignore them. -# Setting this to false in existing Renku deployment will result in code repositories -# that use the internal Gitlab not functioning properly. If you still want to set this -# to false and keep operating with an internal Gitlab you should create an Integration -# with the internal Gitlab and ask users to activate the connection. -enableInternalGitlab: false - podSecurityContext: {} securityContext: runAsUser: 1000 diff --git a/helm-chart/values.yaml.changelog.md b/helm-chart/values.yaml.changelog.md index 65003727a8..c84f8896e5 100644 --- a/helm-chart/values.yaml.changelog.md +++ b/helm-chart/values.yaml.changelog.md @@ -5,6 +5,10 @@ For changes that require manual steps other than changing values, please check o Please follow this convention when adding a new row * ` - **:
` +## Upgrading to Renku 2.8.0 + +* DELETE `enableInternalGitlab`, it is now not possible to configure Renku to use an "internal" GitLab instance. Admins can set up a GitLab integration instead. + ## Upgrading to Renku 2.15.0 * DELETE `global.gateway.cliClientSecret` the client is public and has no secret in Keycloak 25. From e540fa923fc21f567cb4cc27cd53c83eba4f90a0 Mon Sep 17 00:00:00 2001 From: Flora Thiebaut Date: Thu, 30 Apr 2026 11:01:18 +0200 Subject: [PATCH 7/7] feat: use self-minted tokens for sessions and migrate resource pool checks to authz (#4419) Add new internal features to Renku, including: * refactor access and refresh token handling in the backend, use internal tokens for sessions * migrate resource pool authorization checks to authz Note for @SwissDataScienceCenter/yat: the next release will need sessions to be paused during the upgrade. --------- Signed-off-by: dependabot[bot] Co-authored-by: Renku Bot Co-authored-by: Alessandro Degano Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Lorenzo Cavazzi <43481553+lorenzo-cavazzi@users.noreply.github.com> Co-authored-by: Tasko Olevski <16360283+olevski@users.noreply.github.com> Co-authored-by: Lorenzo Co-authored-by: Salim Kayal --- helm-chart/renku/requirements.yaml | 2 +- .../templates/data-service/deployment.yaml | 11 ++++ .../data-service/deployment_k8s_watcher.yaml | 9 ++++ helm-chart/renku/values.yaml | 13 ++--- scripts/platform-init/platform-init.py | 50 ++++++++++++++++++- 5 files changed, 77 insertions(+), 8 deletions(-) diff --git a/helm-chart/renku/requirements.yaml b/helm-chart/renku/requirements.yaml index 8ad7da5bf5..df4087a427 100644 --- a/helm-chart/renku/requirements.yaml +++ b/helm-chart/renku/requirements.yaml @@ -13,7 +13,7 @@ dependencies: condition: redis.install - name: amalthea-sessions repository: "https://swissdatasciencecenter.github.io/helm-charts/" - version: "0.27.2" + version: "0.28.0" - name: dlf-chart repository: "https://swissdatasciencecenter.github.io/datashim/" version: "0.3.9-renku-2" diff --git a/helm-chart/renku/templates/data-service/deployment.yaml b/helm-chart/renku/templates/data-service/deployment.yaml index f80fa6b0c7..75dbda719a 100644 --- a/helm-chart/renku/templates/data-service/deployment.yaml +++ b/helm-chart/renku/templates/data-service/deployment.yaml @@ -75,6 +75,8 @@ spec: value: /secrets/encryptionKey/encryptionKey - name: SECRETS_SERVICE_PUBLIC_KEY_PATH value: /secrets/publicKey/publicKey + - name: INTERNAL_AUTHN_SECRET_KEY_PATH + value: /secrets/internalSecretKey/secretKey - name: K8S_NAMESPACE value: {{ .Release.Namespace | quote }} - name: MAX_PINNED_PROJECTS @@ -215,6 +217,9 @@ spec: - mountPath: "/secrets/publicKey" name: secret-service-public-key readOnly: true + - mountPath: "/secrets/internalSecretKey" + name: internal-authn-key + readOnly: true {{- if .Values.dataService.remoteClustersKubeconfigSecretName }} - name: remote-cluster-kubeconfigs mountPath: "/secrets/kube_configs" @@ -271,6 +276,12 @@ spec: items: - key: publicKey path: publicKey + - name: internal-authn-key + secret: + secretName: {{ template "renku.fullname" . }}-internal-authn + items: + - key: secretKey + path: secretKey {{- if .Values.dataService.remoteClustersKubeconfigSecretName }} - name: remote-cluster-kubeconfigs secret: diff --git a/helm-chart/renku/templates/data-service/deployment_k8s_watcher.yaml b/helm-chart/renku/templates/data-service/deployment_k8s_watcher.yaml index 1b058de08c..584221c0ba 100644 --- a/helm-chart/renku/templates/data-service/deployment_k8s_watcher.yaml +++ b/helm-chart/renku/templates/data-service/deployment_k8s_watcher.yaml @@ -69,6 +69,15 @@ spec: value: {{ .Values.dataService.k8sWatcher.sentry.environment | quote }} - name: SENTRY_SAMPLE_RATE value: {{ .Values.dataService.k8sWatcher.sentry.sampleRate | quote }} + - name: AUTHZ_DB_HOST + value: {{ include "renku.fullname" . }}-authz + - name: AUTHZ_DB_KEY + valueFrom: + secretKeyRef: + name: {{ template "renku.fullname" . }}-authz + key: SPICEDB_GRPC_PRESHARED_KEY + - name: AUTHZ_DB_GRPC_PORT + value: "50051" {{- if .Values.dataService.remoteClustersKubeconfigSecretName }} - name: K8S_CONFIGS_ROOT value: "/secrets/kube_configs" diff --git a/helm-chart/renku/values.yaml b/helm-chart/renku/values.yaml index 6871645aad..5c101d336e 100644 --- a/helm-chart/renku/values.yaml +++ b/helm-chart/renku/values.yaml @@ -14,6 +14,7 @@ global: # secretServicePrivateKey: ... RSA Private Key in PKCS8 PEM format (`ssh-keygen -m PKCS8 -t rsa -b 4096`) # secretServicePreviousPrivateKey: ... Previous Private key in PEM format, only set this when rotating keys # dataServiceEncryptionKey: 32 byte random string + # dataServiceInternalAuthnKey: 64 byte random string gitlab: ## Name of the postgres database to be used by Gitlab postgresDatabase: gitlabhq_production @@ -992,7 +993,7 @@ notebooks: gitHttpsProxy: image: name: renku/sidecars - tag: 0.26.2 + tag: "0.28.0" args: ["gitproxy", "proxy"] port: 65480 healthPort: 65481 @@ -1091,7 +1092,7 @@ gateway: secretKey: image: repository: renku/renku-gateway - tag: "1.9.0" + tag: "1.10.0" pullPolicy: IfNotPresent service: type: ClusterIP @@ -1183,12 +1184,12 @@ dataService: existingPriorityClass: "" image: repository: renku/renku-data-service - tag: "0.72.2" + tag: "0.73.0" pullPolicy: IfNotPresent k8sWatcher: image: repository: renku/data-service-k8s-watcher - tag: "0.72.2" + tag: "0.73.0" pullPolicy: IfNotPresent resources: {} sentry: @@ -1199,7 +1200,7 @@ dataService: dataTasks: image: repository: renku/data-service-data-tasks - tag: "0.72.2" + tag: "0.73.0" pullPolicy: IfNotPresent resources: {} enableResourceRequestTracking: false @@ -1330,7 +1331,7 @@ authz: secretsStorage: image: repository: renku/secrets-storage - tag: "0.72.2" + tag: "0.73.0" pullPolicy: IfNotPresent service: type: ClusterIP diff --git a/scripts/platform-init/platform-init.py b/scripts/platform-init/platform-init.py index bfe2fecf60..5536ee7103 100644 --- a/scripts/platform-init/platform-init.py +++ b/scripts/platform-init/platform-init.py @@ -1,6 +1,7 @@ -from base64 import b64decode, b64encode +from base64 import b64decode, b64encode, urlsafe_b64encode import yaml import logging +import random from typing import cast from kubernetes import client as k8s_client, config as k8s_config from dataclasses import dataclass, field @@ -23,6 +24,7 @@ class Config: secret_service_private_key: str | None = field(repr=False) encryption_key: str | None = field(repr=False) previous_secret_service_private_key: str | None = field(repr=False) + internal_authn_secret_key : str | None = field(repr=False) @classmethod def from_env(cls): @@ -40,6 +42,7 @@ def from_env(cls): ), secret_service_private_key_secret_name=f"{renku_fullname}-secret-service-private-key", secret_service_public_key_secret_name=f"{renku_fullname}-secret-service-public-key", + internal_authn_secret_key=config_map.get("dataServiceInternalAuthnKey"), ) @@ -257,6 +260,50 @@ def init_secret_and_data_service_encryption(config: Config): ), ) +def init_data_service_internal_authentication_secret_key(config: Config): + """Initialize symmetric signing key for internal authentication in data service.""" + logging.info("Initializing data service internal secret key") + v1 = k8s_client.CoreV1Api() + + internal_secret_key = f"{config.renku_fullname}-internal-authn" + internal_secret_key_name = "secretKey" + existing_internal_secret_key = _get_k8s_secret( + config.k8s_namespace, internal_secret_key, internal_secret_key_name + ) + + if existing_internal_secret_key is None and config.internal_authn_secret_key is None: + # generate a random string + rand = random.SystemRandom() + key = urlsafe_b64encode (rand.randbytes(64)) + v1.create_namespaced_secret( + config.k8s_namespace, + k8s_client.V1Secret( + api_version="v1", + data={internal_secret_key_name: b64encode(key).decode()}, + kind="Secret", + metadata={ + "name": internal_secret_key, + "namespace": config.k8s_namespace, + }, + type="Opaque", + ), + ) + elif existing_internal_secret_key is None and config.internal_authn_secret_key is not None: + key = config.internal_authn_secret_key.encode() + v1.create_namespaced_secret( + config.k8s_namespace, + k8s_client.V1Secret( + api_version="v1", + data={internal_secret_key_name: b64encode(key).decode()}, + kind="Secret", + metadata={ + "name": internal_secret_key, + "namespace": config.k8s_namespace, + }, + type="Opaque", + ), + ) + def main(): config = Config.from_env() @@ -265,6 +312,7 @@ def main(): logging.info("Initializing Renku platform") set_secret_service_secrets(config) init_secret_and_data_service_encryption(config) + init_data_service_internal_authentication_secret_key(config) if __name__ == "__main__":