added permission check for secrets (#14)

* added permission check for secrets

* typo in subresource

* moved auth check to the command receiver
This commit is contained in:
Nikola Jokic
2022-06-15 14:54:50 +02:00
committed by GitHub
parent 8ea57170d8
commit de4553f25a
4 changed files with 23 additions and 53 deletions

View File

@@ -1,6 +1,5 @@
import { pruneSecrets, prunePods } from '../k8s' import { prunePods, pruneSecrets } from '../k8s'
export async function cleanupJob(): Promise<void> { export async function cleanupJob(): Promise<void> {
await prunePods() await Promise.all([prunePods(), pruneSecrets()])
await pruneSecrets()
} }

View File

@@ -6,11 +6,8 @@ import path from 'path'
import { import {
containerPorts, containerPorts,
createPod, createPod,
isAuthPermissionsOK,
isPodContainerAlpine, isPodContainerAlpine,
namespace,
prunePods, prunePods,
requiredPermissions,
waitForPodPhases waitForPodPhases
} from '../k8s' } from '../k8s'
import { import {
@@ -30,13 +27,6 @@ export async function prepareJob(
} }
await prunePods() await prunePods()
if (!(await isAuthPermissionsOK())) {
throw new Error(
`The Service account needs the following permissions ${JSON.stringify(
requiredPermissions
)} on the pod resource in the '${namespace}' namespace. Please contact your self hosted runner administrator.`
)
}
await copyExternalsToRoot() await copyExternalsToRoot()
let container: k8s.V1Container | undefined = undefined let container: k8s.V1Container | undefined = undefined
if (args.container?.image) { if (args.container?.image) {

View File

@@ -1,11 +1,12 @@
import { Command, getInputFromStdin, prepareJobArgs } from 'hooklib'
import * as core from '@actions/core' import * as core from '@actions/core'
import { Command, getInputFromStdin, prepareJobArgs } from 'hooklib'
import { import {
cleanupJob, cleanupJob,
prepareJob, prepareJob,
runContainerStep, runContainerStep,
runScriptStep runScriptStep
} from './hooks' } from './hooks'
import { isAuthPermissionsOK, namespace, requiredPermissions } from './k8s'
async function run(): Promise<void> { async function run(): Promise<void> {
const input = await getInputFromStdin() const input = await getInputFromStdin()
@@ -17,6 +18,13 @@ async function run(): Promise<void> {
let exitCode = 0 let exitCode = 0
try { try {
if (!(await isAuthPermissionsOK())) {
throw new Error(
`The Service account needs the following permissions ${JSON.stringify(
requiredPermissions
)} on the pod resource in the '${namespace}' namespace. Please contact your self hosted runner administrator.`
)
}
switch (command) { switch (command) {
case Command.PrepareJob: case Command.PrepareJob:
await prepareJob(args as prepareJobArgs, responseFile) await prepareJob(args as prepareJobArgs, responseFile)

View File

@@ -45,15 +45,14 @@ export const requiredPermissions = [
verbs: ['get', 'list', 'create', 'delete'], verbs: ['get', 'list', 'create', 'delete'],
resource: 'jobs', resource: 'jobs',
subresource: '' subresource: ''
} },
] {
const secretPermission = {
group: '', group: '',
verbs: ['get', 'list', 'create', 'delete'], verbs: ['create', 'delete', 'get', 'list'],
resource: 'secrets', resource: 'secrets',
subresource: '' subresource: ''
} }
]
export async function createPod( export async function createPod(
jobContainer?: k8s.V1Container, jobContainer?: k8s.V1Container,
@@ -94,7 +93,6 @@ export async function createPod(
] ]
if (registry) { if (registry) {
if (await isSecretsAuthOK()) {
const secret = await createDockerSecret(registry) const secret = await createDockerSecret(registry)
if (!secret?.metadata?.name) { if (!secret?.metadata?.name) {
throw new Error(`created secret does not have secret.metadata.name`) throw new Error(`created secret does not have secret.metadata.name`)
@@ -102,11 +100,6 @@ export async function createPod(
const secretReference = new k8s.V1LocalObjectReference() const secretReference = new k8s.V1LocalObjectReference()
secretReference.name = secret.metadata.name secretReference.name = secret.metadata.name
appPod.spec.imagePullSecrets = [secretReference] appPod.spec.imagePullSecrets = [secretReference]
} else {
throw new Error(
`Pulls from private registry is not allowed. Please contact your self hosted runner administrator. Service account needs permissions for ${secretPermission.verbs} in resource ${secretPermission.resource}`
)
}
} }
const { body } = await k8sApi.createNamespacedPod(namespace(), appPod) const { body } = await k8sApi.createNamespacedPod(namespace(), appPod)
@@ -440,26 +433,6 @@ export async function isAuthPermissionsOK(): Promise<boolean> {
return responses.every(resp => resp.body.status?.allowed) return responses.every(resp => resp.body.status?.allowed)
} }
export async function isSecretsAuthOK(): Promise<boolean> {
const sar = new k8s.V1SelfSubjectAccessReview()
const asyncs: Promise<{
response: unknown
body: k8s.V1SelfSubjectAccessReview
}>[] = []
for (const verb of secretPermission.verbs) {
sar.spec = new k8s.V1SelfSubjectAccessReviewSpec()
sar.spec.resourceAttributes = new k8s.V1ResourceAttributes()
sar.spec.resourceAttributes.verb = verb
sar.spec.resourceAttributes.namespace = namespace()
sar.spec.resourceAttributes.group = secretPermission.group
sar.spec.resourceAttributes.resource = secretPermission.resource
sar.spec.resourceAttributes.subresource = secretPermission.subresource
asyncs.push(k8sAuthorizationV1Api.createSelfSubjectAccessReview(sar))
}
const responses = await Promise.all(asyncs)
return responses.every(resp => resp.body.status?.allowed)
}
export async function isPodContainerAlpine( export async function isPodContainerAlpine(
podName: string, podName: string,
containerName: string containerName: string