mirror of
https://github.com/actions/runner-container-hooks.git
synced 2025-12-13 16:16: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> {
|
export async function cleanupJob(): Promise<void> {
|
||||||
await prunePods()
|
await Promise.all([prunePods(), pruneSecrets()])
|
||||||
await pruneSecrets()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -45,16 +45,15 @@ export const requiredPermissions = [
|
|||||||
verbs: ['get', 'list', 'create', 'delete'],
|
verbs: ['get', 'list', 'create', 'delete'],
|
||||||
resource: 'jobs',
|
resource: 'jobs',
|
||||||
subresource: ''
|
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(
|
export async function createPod(
|
||||||
jobContainer?: k8s.V1Container,
|
jobContainer?: k8s.V1Container,
|
||||||
services?: k8s.V1Container[],
|
services?: k8s.V1Container[],
|
||||||
@@ -94,19 +93,13 @@ 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`)
|
|
||||||
}
|
|
||||||
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 secretReference = new k8s.V1LocalObjectReference()
|
||||||
|
secretReference.name = secret.metadata.name
|
||||||
|
appPod.spec.imagePullSecrets = [secretReference]
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
Reference in New Issue
Block a user