mirror of
https://github.com/actions/runner-container-hooks.git
synced 2025-12-12 15:46:46 +00:00
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:
@@ -1,6 +1,5 @@
|
||||
import { pruneSecrets, prunePods } from '../k8s'
|
||||
import { prunePods, pruneSecrets } from '../k8s'
|
||||
|
||||
export async function cleanupJob(): Promise<void> {
|
||||
await prunePods()
|
||||
await pruneSecrets()
|
||||
await Promise.all([prunePods(), pruneSecrets()])
|
||||
}
|
||||
|
||||
@@ -6,11 +6,8 @@ import path from 'path'
|
||||
import {
|
||||
containerPorts,
|
||||
createPod,
|
||||
isAuthPermissionsOK,
|
||||
isPodContainerAlpine,
|
||||
namespace,
|
||||
prunePods,
|
||||
requiredPermissions,
|
||||
waitForPodPhases
|
||||
} from '../k8s'
|
||||
import {
|
||||
@@ -30,13 +27,6 @@ export async function prepareJob(
|
||||
}
|
||||
|
||||
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()
|
||||
let container: k8s.V1Container | undefined = undefined
|
||||
if (args.container?.image) {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Command, getInputFromStdin, prepareJobArgs } from 'hooklib'
|
||||
import * as core from '@actions/core'
|
||||
import { Command, getInputFromStdin, prepareJobArgs } from 'hooklib'
|
||||
import {
|
||||
cleanupJob,
|
||||
prepareJob,
|
||||
runContainerStep,
|
||||
runScriptStep
|
||||
} from './hooks'
|
||||
import { isAuthPermissionsOK, namespace, requiredPermissions } from './k8s'
|
||||
|
||||
async function run(): Promise<void> {
|
||||
const input = await getInputFromStdin()
|
||||
@@ -17,6 +18,13 @@ async function run(): Promise<void> {
|
||||
|
||||
let exitCode = 0
|
||||
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) {
|
||||
case Command.PrepareJob:
|
||||
await prepareJob(args as prepareJobArgs, responseFile)
|
||||
|
||||
@@ -45,16 +45,15 @@ export const requiredPermissions = [
|
||||
verbs: ['get', 'list', 'create', 'delete'],
|
||||
resource: 'jobs',
|
||||
subresource: ''
|
||||
},
|
||||
{
|
||||
group: '',
|
||||
verbs: ['create', 'delete', 'get', 'list'],
|
||||
resource: 'secrets',
|
||||
subresource: ''
|
||||
}
|
||||
]
|
||||
|
||||
const secretPermission = {
|
||||
group: '',
|
||||
verbs: ['get', 'list', 'create', 'delete'],
|
||||
resource: 'secrets',
|
||||
subresource: ''
|
||||
}
|
||||
|
||||
export async function createPod(
|
||||
jobContainer?: k8s.V1Container,
|
||||
services?: k8s.V1Container[],
|
||||
@@ -94,19 +93,13 @@ export async function createPod(
|
||||
]
|
||||
|
||||
if (registry) {
|
||||
if (await isSecretsAuthOK()) {
|
||||
const secret = await createDockerSecret(registry)
|
||||
if (!secret?.metadata?.name) {
|
||||
throw new Error(`created secret does not have secret.metadata.name`)
|
||||
}
|
||||
const secretReference = new k8s.V1LocalObjectReference()
|
||||
secretReference.name = secret.metadata.name
|
||||
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 secret = await createDockerSecret(registry)
|
||||
if (!secret?.metadata?.name) {
|
||||
throw new Error(`created secret does not have secret.metadata.name`)
|
||||
}
|
||||
const secretReference = new k8s.V1LocalObjectReference()
|
||||
secretReference.name = secret.metadata.name
|
||||
appPod.spec.imagePullSecrets = [secretReference]
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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(
|
||||
podName: string,
|
||||
containerName: string
|
||||
|
||||
Reference in New Issue
Block a user