Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display OIDC observability URLs #1899

Merged
merged 11 commits into from
Jun 10, 2024
5 changes: 3 additions & 2 deletions backend/__fixtures__/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,11 @@ const mocks = {
const { id } = auth.getTokenPayload(headers)
const { resourceAttributes, nonResourceAttributes } = json.spec
if (resourceAttributes) {
const { resource } = resourceAttributes
const { resource, namespace } = resourceAttributes
switch (resource) {
case 'secrets':
allowed = id === '[email protected]'
allowed = id === '[email protected]' ||
(id === '[email protected]' && namespace === 'garden-foo')
break
case 'projects':
allowed = id === '[email protected]'
Expand Down
4 changes: 2 additions & 2 deletions backend/__fixtures__/seeds.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ function getSeed ({
type: kind,
region
},
dns: {
ingressDomain: `ingress.${region}.${kind}.example.org`
ingress: {
domain: `ingress.${region}.${kind}.example.org`
},
taints: [],
settings: {
Expand Down
12 changes: 12 additions & 0 deletions backend/lib/services/authorization.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,18 @@ exports.canListControllerRegistrations = function (user) {
})
}

exports.canGetSecret = function (user, namespace, name) {
return hasAuthorization(user, {
resourceAttributes: {
verb: 'get',
group: '',
resource: 'secrets',
namespace,
name
}
})
}

/*
SelfSubjectRulesReview should only be used to hide/show actions or views on the UI and not for authorization checks.
*/
Expand Down
4 changes: 3 additions & 1 deletion backend/lib/services/seeds.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ function fromResource (seed) {
const visible = _.get(seed, 'spec.settings.scheduling.visible')
const provider = _.get(seed, 'spec.provider')
const volume = _.get(seed, 'spec.volume')
const ingressDomain = _.get(seed, 'spec.ingress.domain')
const data = {
volume,
...provider,
visible,
unprotected
unprotected,
ingressDomain
}

return { metadata, data }
Expand Down
20 changes: 4 additions & 16 deletions backend/lib/services/shoots.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ const {
decodeBase64,
encodeBase64,
getSeedNameFromShoot,
getSeedIngressDomain,
projectFilter
} = utils
const { getSeed } = cache
Expand Down Expand Up @@ -334,13 +333,6 @@ exports.info = async function ({ user, namespace, name }) {

if (shoot.spec.seedName) {
const seed = getSeed(getSeedNameFromShoot(shoot))
const prefix = _.replace(shoot.status.technicalID, /^shoot-{1,2}/, '')
if (prefix) {
const ingressDomain = getSeedIngressDomain(seed)
if (ingressDomain) {
data.seedShootIngressDomain = `${prefix}.${ingressDomain}`
}
}
if (seed && namespace !== 'garden') {
try {
data.canLinkToSeed = !!(await client['core.gardener.cloud'].shoots.get('garden', seed.metadata.name))
Expand Down Expand Up @@ -372,14 +364,10 @@ exports.info = async function ({ user, namespace, name }) {
}
data.dashboardUrlPath = getDashboardUrlPath(shoot.spec.kubernetes.version)

/*
We explicitly use the (privileged) dashboardClient here for fetching the monitoring credentials instead of using the user's token
as we agreed that also project viewers should be able to see the monitoring credentials.
Usually project viewers do not have the permission to read the <shootName>.monitoring credential.
Our assumption: if the user can read the shoot resource, the user can be considered as project viewer.
This is only a temporary workaround until a Plutono SSO solution is implemented https://github.com/gardener/monitoring/issues/11.
*/
await assignMonitoringSecret(dashboardClient, data, namespace, name)
const oidcObservabilityUrlsEnabled = _.get(config, 'frontend.features.oidcObservabilityUrlsEnabled', false)
if (!oidcObservabilityUrlsEnabled && await authorization.canGetSecret(user, namespace, `${name}.monitoring`)) {
await assignMonitoringSecret(client, data, namespace, name)
}
holgerkoser marked this conversation as resolved.
Show resolved Hide resolved

petersutter marked this conversation as resolved.
Show resolved Hide resolved
return data
}
Expand Down
2 changes: 1 addition & 1 deletion backend/lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ function shootHasIssue (shoot) {
}

function getSeedIngressDomain (seed) {
return _.get(seed, 'spec.dns.ingressDomain') || _.get(seed, 'spec.ingress.domain')
return _.get(seed, 'spec.ingress.domain')
}

function isSeedUnreachable (seed) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ exports[`api seeds should return all seeds 2`] = `
Array [
Object {
"data": Object {
"ingressDomain": "ingress.foo-east.infra1.example.org",
"region": "foo-east",
"type": "infra1",
"unprotected": false,
Expand All @@ -42,6 +43,7 @@ Array [
},
Object {
"data": Object {
"ingressDomain": "ingress.foo-east.infra1.example.org",
"region": "foo-east",
"type": "infra1",
"unprotected": true,
Expand All @@ -54,6 +56,7 @@ Array [
},
Object {
"data": Object {
"ingressDomain": "ingress.foo-west.infra1.example.org",
"region": "foo-west",
"type": "infra1",
"unprotected": true,
Expand All @@ -66,6 +69,7 @@ Array [
},
Object {
"data": Object {
"ingressDomain": "ingress.foo-europe.infra3.example.org",
"region": "foo-europe",
"type": "infra3",
"unprotected": true,
Expand All @@ -78,6 +82,7 @@ Array [
},
Object {
"data": Object {
"ingressDomain": "ingress.foo-south.infra1.example.org",
"region": "foo-south",
"type": "infra1",
"unprotected": true,
Expand All @@ -90,6 +95,7 @@ Array [
},
Object {
"data": Object {
"ingressDomain": "ingress.foo-europe.infra3.example.org",
"region": "foo-europe",
"type": "infra3",
"unprotected": true,
Expand All @@ -102,6 +108,7 @@ Array [
},
Object {
"data": Object {
"ingressDomain": "ingress.foo-europe.infra3.example.org",
"region": "foo-europe",
"type": "infra3",
"unprotected": true,
Expand All @@ -114,6 +121,7 @@ Array [
},
Object {
"data": Object {
"ingressDomain": "ingress.foo-europe.infra3.example.org",
"region": "foo-europe",
"type": "infra3",
"unprotected": false,
Expand All @@ -126,6 +134,7 @@ Array [
},
Object {
"data": Object {
"ingressDomain": "ingress.foo-europe.infra3.example.org",
"region": "foo-europe",
"type": "infra3",
"unprotected": true,
Expand Down
52 changes: 48 additions & 4 deletions backend/test/acceptance/__snapshots__/api.shoots.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1702,13 +1702,36 @@ Array [
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImZvb0BleGFtcGxlLm9yZyIsImlhdCI6MTU3NzgzNjgwMCwiYXVkIjpbImdhcmRlbmVyIl0sImV4cCI6MzE1NTcxNjgwMCwianRpIjoianRpIn0.k3kGjF6AgugJLdwERXEWZPaibFAPFPOnmpT3YM9H0xU",
},
],
Array [
Object {
":authority": "kubernetes:6443",
":method": "post",
":path": "/apis/authorization.k8s.io/v1/selfsubjectaccessreviews",
":scheme": "https",
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImZvb0BleGFtcGxlLm9yZyIsImlhdCI6MTU3NzgzNjgwMCwiYXVkIjpbImdhcmRlbmVyIl0sImV4cCI6MzE1NTcxNjgwMCwianRpIjoianRpIn0.k3kGjF6AgugJLdwERXEWZPaibFAPFPOnmpT3YM9H0xU",
},
Object {
"apiVersion": "authorization.k8s.io/v1",
"kind": "SelfSubjectAccessReview",
"spec": Object {
"nonResourceAttributes": undefined,
"resourceAttributes": Object {
"group": "",
"name": "barShoot.monitoring",
"namespace": "garden-foo",
"resource": "secrets",
"verb": "get",
},
},
},
],
Array [
Object {
":authority": "kubernetes:6443",
":method": "get",
":path": "/api/v1/namespaces/garden-foo/secrets/barShoot.monitoring",
":scheme": "https",
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmdhcmRlbjpkZWZhdWx0In0.-4rSuvvj5BStN6DwnmLAaRVbgpl5iCn2hG0pcqx0NPw",
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImZvb0BleGFtcGxlLm9yZyIsImlhdCI6MTU3NzgzNjgwMCwiYXVkIjpbImdhcmRlbmVyIl0sImV4cCI6MzE1NTcxNjgwMCwianRpIjoianRpIn0.k3kGjF6AgugJLdwERXEWZPaibFAPFPOnmpT3YM9H0xU",
},
],
]
Expand Down Expand Up @@ -1743,13 +1766,36 @@ Array [
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImZvb0BleGFtcGxlLm9yZyIsImlhdCI6MTU3NzgzNjgwMCwiYXVkIjpbImdhcmRlbmVyIl0sImV4cCI6MzE1NTcxNjgwMCwianRpIjoianRpIn0.k3kGjF6AgugJLdwERXEWZPaibFAPFPOnmpT3YM9H0xU",
},
],
Array [
Object {
":authority": "kubernetes:6443",
":method": "post",
":path": "/apis/authorization.k8s.io/v1/selfsubjectaccessreviews",
":scheme": "https",
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImZvb0BleGFtcGxlLm9yZyIsImlhdCI6MTU3NzgzNjgwMCwiYXVkIjpbImdhcmRlbmVyIl0sImV4cCI6MzE1NTcxNjgwMCwianRpIjoianRpIn0.k3kGjF6AgugJLdwERXEWZPaibFAPFPOnmpT3YM9H0xU",
},
Object {
"apiVersion": "authorization.k8s.io/v1",
"kind": "SelfSubjectAccessReview",
"spec": Object {
"nonResourceAttributes": undefined,
"resourceAttributes": Object {
"group": "",
"name": "dummyShoot.monitoring",
"namespace": "garden-foo",
"resource": "secrets",
"verb": "get",
},
},
},
],
Array [
Object {
":authority": "kubernetes:6443",
":method": "get",
":path": "/api/v1/namespaces/garden-foo/secrets/dummyShoot.monitoring",
":scheme": "https",
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmdhcmRlbjpkZWZhdWx0In0.-4rSuvvj5BStN6DwnmLAaRVbgpl5iCn2hG0pcqx0NPw",
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImZvb0BleGFtcGxlLm9yZyIsImlhdCI6MTU3NzgzNjgwMCwiYXVkIjpbImdhcmRlbmVyIl0sImV4cCI6MzE1NTcxNjgwMCwianRpIjoianRpIn0.k3kGjF6AgugJLdwERXEWZPaibFAPFPOnmpT3YM9H0xU",
},
],
]
Expand All @@ -1762,7 +1808,6 @@ Object {
"kubeconfig": Any<String>,
"monitoringPassword": "pass-garden-foo-dummyShoot.monitoring",
"monitoringUsername": "user-garden-foo-dummyShoot.monitoring",
"seedShootIngressDomain": "foo--dummyShoot.ingress.foo-south.infra1.example.org",
}
`;

Expand Down Expand Up @@ -1809,7 +1854,6 @@ Object {
"kubeconfigGardenlogin": Any<String>,
"monitoringPassword": "pass-garden-foo-barShoot.monitoring",
"monitoringUsername": "user-garden-foo-barShoot.monitoring",
"seedShootIngressDomain": "foo--barShoot.ingress.foo-east.infra1.example.org",
}
`;

Expand Down
6 changes: 4 additions & 2 deletions backend/test/acceptance/api.shoots.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ describe('api', function () {
mockRequest.mockImplementationOnce(fixtures.configmaps.mocks.get())
mockRequest.mockImplementationOnce(fixtures.configmaps.mocks.get())
mockRequest.mockImplementationOnce(fixtures.shoots.mocks.get())
mockRequest.mockImplementationOnce(fixtures.auth.mocks.reviewSelfSubjectAccess())
mockRequest.mockImplementationOnce(fixtures.secrets.mocks.get())

const res = await agent
Expand All @@ -218,7 +219,7 @@ describe('api', function () {
.expect('content-type', /json/)
.expect(200)

expect(mockRequest).toBeCalledTimes(6)
expect(mockRequest).toBeCalledTimes(7)
expect(mockRequest.mock.calls).toMatchSnapshot()

expect(kubeconfig.cleanKubeconfig).toBeCalledTimes(1)
Expand All @@ -239,6 +240,7 @@ describe('api', function () {
mockRequest.mockImplementationOnce(fixtures.shoots.mocks.get())
mockRequest.mockImplementationOnce(fixtures.secrets.mocks.get())
mockRequest.mockImplementationOnce(fixtures.shoots.mocks.get())
mockRequest.mockImplementationOnce(fixtures.auth.mocks.reviewSelfSubjectAccess())
mockRequest.mockImplementationOnce(fixtures.secrets.mocks.get())

const res = await agent
Expand All @@ -247,7 +249,7 @@ describe('api', function () {
.expect('content-type', /json/)
.expect(200)

expect(mockRequest).toBeCalledTimes(4)
expect(mockRequest).toBeCalledTimes(5)
expect(mockRequest.mock.calls).toMatchSnapshot()

expect(kubeconfig.cleanKubeconfig).toBeCalledTimes(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,6 @@ Object {
"watch",
],
},
Object {
"apiGroups": Array [
"",
],
"resources": Array [
"secrets",
],
"verbs": Array [
"get",
],
},
],
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ Object {
},
],
"features": Object {
"oidcObservabilityUrlsEnabled": false,
"projectTerminalShortcutsEnabled": false,
"terminalEnabled": false,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,4 @@ rules:
verbs:
- list
- watch
# keep until gardener/monitoring#11 is resolved
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
{{- end }}
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ data:
features:
terminalEnabled: {{ .Values.global.dashboard.frontendConfig.features.terminalEnabled | default false }}
projectTerminalShortcutsEnabled: {{ .Values.global.dashboard.frontendConfig.features.projectTerminalShortcutsEnabled | default false }}
oidcObservabilityUrlsEnabled: {{ .Values.global.dashboard.frontendConfig.features.oidcObservabilityUrlsEnabled | default false }}
experimental:
throttleDelayPerCluster: {{ .Values.global.dashboard.frontendConfig.experimental.throttleDelayPerCluster | default 10 }}
{{- if .Values.global.dashboard.frontendConfig.terminal }}
Expand Down
4 changes: 4 additions & 0 deletions charts/gardener-dashboard/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ global:
features:
terminalEnabled: false
projectTerminalShortcutsEnabled: false
# The oidcObservabilityUrlsEnabled feature flag controls whether OIDC observability URLs are displayed in the dashboard.
# This requires the gardener/oidc-apps-controller extension to be deployed and assumes the hostPrefix (https://github.com/gardener/oidc-apps-controller/blob/653add2e304c382c1b8ba4e4b775477e288826a3/example/config.yaml#L64) is empty.
# When enabled, it shows the OIDC URLs; when disabled, it displays the current observability URLs for basic auth login along with the credentials.
oidcObservabilityUrlsEnabled: false

shootAdminKubeconfig:
enabled: false
Expand Down
Loading