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

Implemented Disable Gardener provided static admin kubeconfig #1249

Merged
merged 10 commits into from
Aug 16, 2022
13 changes: 13 additions & 0 deletions backend/lib/routes/shoots.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,19 @@ router.route('/:name')
}
})

router.route('/:name/spec/kubernetes/enableStaticTokenKubeconfig')
.put(async (req, res, next) => {
try {
const user = req.user
const namespace = req.params.namespace
const name = req.params.name
const body = req.body
res.send(await shoots.replaceEnableStaticTokenKubeconfig({ user, namespace, name, body }))
} catch (err) {
next(err)
}
})

router.route('/:name/spec/kubernetes/version')
.put(async (req, res, next) => {
try {
Expand Down
11 changes: 11 additions & 0 deletions backend/lib/services/shoots.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,17 @@ exports.replaceVersion = async function ({ user, namespace, name, body }) {
return client['core.gardener.cloud'].shoots.jsonPatch(namespace, name, patchOperations)
}

exports.replaceEnableStaticTokenKubeconfig = async function ({ user, namespace, name, body }) {
const client = user.client
const enableStaticTokenKubeconfig = body.enableStaticTokenKubeconfig
grolu marked this conversation as resolved.
Show resolved Hide resolved
const patchOperations = [{
op: 'replace',
path: '/spec/kubernetes/enableStaticTokenKubeconfig',
value: enableStaticTokenKubeconfig
}]
return client['core.gardener.cloud'].shoots.jsonPatch(namespace, name, patchOperations)
}

exports.replaceHibernationEnabled = async function ({ user, namespace, name, body }) {
const client = user.client
const enabled = !!body.enabled
Expand Down
88 changes: 72 additions & 16 deletions backend/test/acceptance/__snapshots__/api.shoots.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,62 @@ Object {
}
`;

exports[`api shoots should replace shoot kubernetes enableStaticTokenKubeconfig 1`] = `
Array [
Array [
Object {
":authority": "kubernetes:6443",
":method": "patch",
":path": "/apis/core.gardener.cloud/v1beta1/namespaces/garden-foo/shoots/barShoot",
":scheme": "https",
"authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImZvb0BleGFtcGxlLm9yZyIsImlhdCI6MTU3NzgzNjgwMCwiYXVkIjpbImdhcmRlbmVyIl0sImV4cCI6MzE1NTcxNjgwMCwianRpIjoianRpIn0.k3kGjF6AgugJLdwERXEWZPaibFAPFPOnmpT3YM9H0xU",
"content-type": "application/json-patch+json",
},
Array [
Object {
"op": "replace",
"path": "/spec/kubernetes/enableStaticTokenKubeconfig",
"value": true,
},
],
],
]
`;

exports[`api shoots should replace shoot kubernetes enableStaticTokenKubeconfig 2`] = `
Object {
"metadata": Object {
"annotations": Object {
"gardener.cloud/created-by": "[email protected]",
},
"name": "barShoot",
"namespace": "garden-foo",
"resourceVersion": "43",
"uid": 2,
},
"spec": Object {
"cloudProfileName": "infra1-profileName",
"hibernation": Object {
"enabled": false,
},
"kubernetes": Object {
"enableStaticTokenKubeconfig": true,
"version": "1.16.0",
},
"provider": Object {
"type": "fooInfra",
},
"purpose": "barPurpose",
"region": "foo-west",
"secretBindingName": "foo-infra1",
"seedName": "infra1-seed",
},
"status": Object {
"technicalID": "shoot--foo--barShoot",
},
}
`;

exports[`api shoots should replace shoot kubernetes version 1`] = `
Array [
Array [
Expand Down Expand Up @@ -981,7 +1037,7 @@ users:
}
`;

exports[`api shoots should return shoot seed info when no fallback is needed 1`] = `
exports[`api shoots should return shoot seed info when need to fallback to old monitoring secret 1`] = `
grolu marked this conversation as resolved.
Show resolved Hide resolved
Array [
Array [
Object {
Expand Down Expand Up @@ -1030,17 +1086,25 @@ Array [
":scheme": "https",
},
],
Array [
Object {
":authority": "api.foo-east.infra1.seed.cluster",
":method": "get",
":path": "/api/v1/namespaces/shoot--foo--barShoot/secrets/monitoring-ingress-credentials",
":scheme": "https",
},
],
]
`;

exports[`api shoots should return shoot seed info when no fallback is needed 2`] = `
exports[`api shoots should return shoot seed info when need to fallback to old monitoring secret 2`] = `
Object {
"monitoringPassword": "pass-shoot--foo--barShoot-bar.monitoring",
"monitoringUsername": "user-shoot--foo--barShoot-bar.monitoring",
"monitoringPassword": "pass-shoot--foo--barShoot-monitoring-ingress-credentials",
"monitoringUsername": "user-shoot--foo--barShoot-monitoring-ingress-credentials",
}
`;

exports[`api shoots should return shoot seed info when need to fallback to old monitoring secret 1`] = `
exports[`api shoots should return shoot seed info when no fallback is needed 1`] = `
Array [
Array [
Object {
Expand Down Expand Up @@ -1089,21 +1153,13 @@ Array [
":scheme": "https",
},
],
Array [
Object {
":authority": "api.foo-east.infra1.seed.cluster",
":method": "get",
":path": "/api/v1/namespaces/shoot--foo--barShoot/secrets/monitoring-ingress-credentials",
":scheme": "https",
},
],
]
`;

exports[`api shoots should return shoot seed info when need to fallback to old monitoring secret 2`] = `
exports[`api shoots should return shoot seed info when no fallback is needed 2`] = `
Object {
"monitoringPassword": "pass-shoot--foo--barShoot-monitoring-ingress-credentials",
"monitoringUsername": "user-shoot--foo--barShoot-monitoring-ingress-credentials",
"monitoringPassword": "pass-shoot--foo--barShoot-bar.monitoring",
"monitoringUsername": "user-shoot--foo--barShoot-bar.monitoring",
}
`;

Expand Down
18 changes: 18 additions & 0 deletions backend/test/acceptance/api.shoots.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,24 @@ describe('api', function () {
expect(res.body).toMatchSnapshot()
})

it('should replace shoot kubernetes enableStaticTokenKubeconfig', async function () {
mockRequest.mockImplementationOnce(fixtures.shoots.mocks.patch())

const res = await agent
.put(`/api/namespaces/${namespace}/shoots/${name}/spec/kubernetes/enableStaticTokenKubeconfig`)
.set('cookie', await user.cookie)
.send({
enableStaticTokenKubeconfig: true
})
.expect('content-type', /json/)
.expect(200)

expect(mockRequest).toBeCalledTimes(1)
expect(mockRequest.mock.calls).toMatchSnapshot()

expect(res.body).toMatchSnapshot()
})

it('should replace shoot maintenance data', async function () {
mockRequest.mockImplementationOnce(fixtures.shoots.mocks.patch())

Expand Down
52 changes: 52 additions & 0 deletions frontend/src/components/AdminKubeconfig.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!--
SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors

SPDX-License-Identifier: Apache-2.0
-->

<template>
<div>
<v-checkbox
v-model="enableStaticTokenKubeconfig"
label="Enable static token kubeconfig"
petersutter marked this conversation as resolved.
Show resolved Hide resolved
color="primary"
@change="onChange"
grolu marked this conversation as resolved.
Show resolved Hide resolved
hide-details
></v-checkbox>
<span class="text-caption">It is <span class="font-weight-bold">not</span> the recommended method to access the shoot cluster as the static token <code>kubeconfig</code> has some security flaws associated with it.</span>
grolu marked this conversation as resolved.
Show resolved Hide resolved
grolu marked this conversation as resolved.
Show resolved Hide resolved
<p>
<external-link class="text-caption" url="https://github.com/gardener/gardener/blob/master/docs/usage/shoot_access.md">More information</external-link>
</p>
grolu marked this conversation as resolved.
Show resolved Hide resolved
</div>
</template>
<script>

import ExternalLink from '@/components/ExternalLink.vue'

export default {
name: 'AdminKubeconfig',
grolu marked this conversation as resolved.
Show resolved Hide resolved
components: {
ExternalLink
},
props: {
value: {
type: Boolean
}
},
data () {
return {
enableStaticTokenKubeconfig: undefined
}
},
methods: {
onChange (value) {
this.$emit('input', value)
}
},
watch: {
value: function (value) {
this.enableStaticTokenKubeconfig = value
}
}
grolu marked this conversation as resolved.
Show resolved Hide resolved
}
</script>
73 changes: 73 additions & 0 deletions frontend/src/components/AdminKubeconfigConfiguration.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<!--
SPDX-FileCopyrightText: 2021 SAP SE or an SAP affiliate company and Gardener contributors

SPDX-License-Identifier: Apache-2.0
-->

<template>
<action-button-dialog
grolu marked this conversation as resolved.
Show resolved Hide resolved
:shoot-item="shootItem"
@dialog-opened="onConfigurationDialogOpened"
ref="actionDialog"
width="450"
caption="Configure Admin Kubeconfig">
<template v-slot:actionComponent>
<admin-kubeconfig
v-model="enableStaticTokenKubeconfig"
></admin-kubeconfig>
</template>
</action-button-dialog>
</template>

<script>
import ActionButtonDialog from '@/components/dialogs/ActionButtonDialog'
import AdminKubeconfig from '@/components/AdminKubeconfig'
import { updateShootEnableStaticTokenKubeconfig } from '@/utils/api'
import { errorDetailsFromError } from '@/utils/error'
import shootItem from '@/mixins/shootItem'

export default {
name: 'admin-kubeconfig-configuration',
components: {
ActionButtonDialog,
AdminKubeconfig
},
mixins: [
shootItem
],
data () {
return {
enableStaticTokenKubeconfig: undefined
}
},
methods: {
async onConfigurationDialogOpened () {
await this.reset()
const confirmed = await this.$refs.actionDialog.waitForDialogClosed()
if (confirmed) {
await this.updateConfiguration()
}
},
async updateConfiguration () {
try {
await updateShootEnableStaticTokenKubeconfig({
namespace: this.shootNamespace,
name: this.shootName,
data: {
enableStaticTokenKubeconfig: this.enableStaticTokenKubeconfig
}
})
} catch (err) {
const errorMessage = 'Could not update static kubeconfig flag'
const errorDetails = errorDetailsFromError(err)
const detailedErrorMessage = errorDetails.detailedMessage
this.$refs.actionDialog.setError({ errorMessage, detailedErrorMessage })
console.error(this.errorMessage, errorDetails.errorCode, errorDetails.detailedMessage, err)
}
},
async reset () {
this.enableStaticTokenKubeconfig = this.shootEnableStaticTokenKubeconfig
}
}
}
</script>
18 changes: 14 additions & 4 deletions frontend/src/components/NewShoot/NewShootDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ SPDX-License-Identifier: Apache-2.0
></purpose>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<admin-kubeconfig v-model="enableStaticTokenKubeconfig"></admin-kubeconfig>
</v-col>
</v-row>
<v-row v-if="slaDescriptionHtml">
<v-col cols="12">
<label>{{slaTitle}}</label>
Expand All @@ -80,6 +85,7 @@ import asyncRef from '@/mixins/asyncRef'

import { getValidationErrors, transformHtml, setDelayedInputFocus } from '@/utils'
import { resourceName, noStartEndHyphen, noConsecutiveHyphen } from '@/utils/validators'
import AdminKubeconfig from '@/components/AdminKubeconfig'

const Purpose = () => import('@/components/Purpose')

Expand All @@ -101,7 +107,8 @@ export default {
name: 'new-shoot-details',
components: {
HintColorizer,
Purpose
Purpose,
AdminKubeconfig
},
mixins: [
asyncRef('purpose')
Expand All @@ -122,7 +129,8 @@ export default {
purposeValid: false,
cloudProfileName: undefined,
secret: undefined,
updateK8sMaintenance: undefined
updateK8sMaintenance: undefined,
enableStaticTokenKubeconfig: undefined
}
},
validations () {
Expand Down Expand Up @@ -236,15 +244,17 @@ export default {
return {
name: this.name,
kubernetesVersion: this.kubernetesVersion,
purpose: this.purpose
purpose: this.purpose,
enableStaticTokenKubeconfig: this.enableStaticTokenKubeconfig
}
},
async setDetailsData ({ name, kubernetesVersion, purpose, cloudProfileName, secret, updateK8sMaintenance }) {
async setDetailsData ({ name, kubernetesVersion, purpose, cloudProfileName, secret, updateK8sMaintenance, enableStaticTokenKubeconfig }) {
this.name = name
this.cloudProfileName = cloudProfileName
this.secret = secret
this.kubernetesVersion = kubernetesVersion
this.updateK8sMaintenance = updateK8sMaintenance
this.enableStaticTokenKubeconfig = enableStaticTokenKubeconfig

await this.$purpose.dispatch('setPurpose', purpose)

Expand Down
Loading