mirror of
https://github.com/actions/runner-container-hooks.git
synced 2025-12-12 07:36:43 +00:00
Allow non-root container (#264)
* Allow non-root container * format * add lint:fix and fix lint errors * fix tests and volume mounts
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
"format": "prettier --write '**/*.ts'",
|
"format": "prettier --write '**/*.ts'",
|
||||||
"format-check": "prettier --check '**/*.ts'",
|
"format-check": "prettier --check '**/*.ts'",
|
||||||
"lint": "eslint packages/**/*.ts",
|
"lint": "eslint packages/**/*.ts",
|
||||||
|
"lint:fix": "eslint packages/**/*.ts --fix",
|
||||||
"build-all": "npm run build --prefix packages/hooklib && npm run build --prefix packages/k8s && npm run build --prefix packages/docker"
|
"build-all": "npm run build --prefix packages/hooklib && npm run build --prefix packages/k8s && npm run build --prefix packages/docker"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -20,8 +20,10 @@ import {
|
|||||||
listDirAllCommand,
|
listDirAllCommand,
|
||||||
sleep,
|
sleep,
|
||||||
EXTERNALS_VOLUME_NAME,
|
EXTERNALS_VOLUME_NAME,
|
||||||
GITHUB_VOLUME_NAME
|
GITHUB_VOLUME_NAME,
|
||||||
|
WORK_VOLUME
|
||||||
} from './utils'
|
} from './utils'
|
||||||
|
import * as shlex from 'shlex'
|
||||||
|
|
||||||
const kc = new k8s.KubeConfig()
|
const kc = new k8s.KubeConfig()
|
||||||
|
|
||||||
@@ -91,13 +93,23 @@ export async function createJobPod(
|
|||||||
|
|
||||||
appPod.spec = new k8s.V1PodSpec()
|
appPod.spec = new k8s.V1PodSpec()
|
||||||
appPod.spec.containers = containers
|
appPod.spec.containers = containers
|
||||||
|
appPod.spec.securityContext = {
|
||||||
|
fsGroup: 1001
|
||||||
|
}
|
||||||
appPod.spec.initContainers = [
|
appPod.spec.initContainers = [
|
||||||
{
|
{
|
||||||
name: 'fs-init',
|
name: 'fs-init',
|
||||||
image:
|
image:
|
||||||
process.env.ACTIONS_RUNNER_IMAGE ||
|
process.env.ACTIONS_RUNNER_IMAGE ||
|
||||||
'ghcr.io/actions/actions-runner:latest',
|
'ghcr.io/actions/actions-runner:latest',
|
||||||
command: ['sh', '-c', 'mv /home/runner/externals/* /mnt/externals'],
|
command: [
|
||||||
|
'sh',
|
||||||
|
'-c',
|
||||||
|
`mkdir -p /mnt/externals && \\
|
||||||
|
mkdir -p /mnt/work && \\
|
||||||
|
mkdir -p /mnt/github && \\
|
||||||
|
mv /home/runner/externals/* /mnt/externals/`
|
||||||
|
],
|
||||||
securityContext: {
|
securityContext: {
|
||||||
runAsGroup: 1001,
|
runAsGroup: 1001,
|
||||||
runAsUser: 1001
|
runAsUser: 1001
|
||||||
@@ -106,6 +118,14 @@ export async function createJobPod(
|
|||||||
{
|
{
|
||||||
name: EXTERNALS_VOLUME_NAME,
|
name: EXTERNALS_VOLUME_NAME,
|
||||||
mountPath: '/mnt/externals'
|
mountPath: '/mnt/externals'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: WORK_VOLUME,
|
||||||
|
mountPath: '/mnt/work'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: GITHUB_VOLUME_NAME,
|
||||||
|
mountPath: '/mnt/github'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -121,6 +141,10 @@ export async function createJobPod(
|
|||||||
{
|
{
|
||||||
name: GITHUB_VOLUME_NAME,
|
name: GITHUB_VOLUME_NAME,
|
||||||
emptyDir: {}
|
emptyDir: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: WORK_VOLUME,
|
||||||
|
emptyDir: {}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -180,6 +204,10 @@ export async function createContainerStepPod(
|
|||||||
{
|
{
|
||||||
name: GITHUB_VOLUME_NAME,
|
name: GITHUB_VOLUME_NAME,
|
||||||
emptyDir: {}
|
emptyDir: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: WORK_VOLUME,
|
||||||
|
emptyDir: {}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -351,7 +379,15 @@ export async function execCpToPod(
|
|||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
const exec = new k8s.Exec(kc)
|
const exec = new k8s.Exec(kc)
|
||||||
const command = ['tar', 'xf', '-', '-C', containerPath]
|
// Use tar to extract with --no-same-owner to avoid ownership issues.
|
||||||
|
// Then use find to fix permissions. The -m flag helps but we also need to fix permissions after.
|
||||||
|
const command = [
|
||||||
|
'sh',
|
||||||
|
'-c',
|
||||||
|
`tar xf - --no-same-owner -C ${shlex.quote(containerPath)} 2>/dev/null; ` +
|
||||||
|
`find ${shlex.quote(containerPath)} -type f -exec chmod u+rw {} \\; 2>/dev/null; ` +
|
||||||
|
`find ${shlex.quote(containerPath)} -type d -exec chmod u+rwx {} \\; 2>/dev/null`
|
||||||
|
]
|
||||||
const readStream = tar.pack(runnerPath)
|
const readStream = tar.pack(runnerPath)
|
||||||
const errStream = new WritableStreamBuffer()
|
const errStream = new WritableStreamBuffer()
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
@@ -369,7 +405,7 @@ export async function execCpToPod(
|
|||||||
if (errStream.size()) {
|
if (errStream.size()) {
|
||||||
reject(
|
reject(
|
||||||
new Error(
|
new Error(
|
||||||
`Error from cpFromPod - details: \n ${errStream.getContentsAsString()}`
|
`Error from execCpToPod - status: ${status.status}, details: \n ${errStream.getContentsAsString()}`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,12 +15,17 @@ export const ENV_USE_KUBE_SCHEDULER = 'ACTIONS_RUNNER_USE_KUBE_SCHEDULER'
|
|||||||
|
|
||||||
export const EXTERNALS_VOLUME_NAME = 'externals'
|
export const EXTERNALS_VOLUME_NAME = 'externals'
|
||||||
export const GITHUB_VOLUME_NAME = 'github'
|
export const GITHUB_VOLUME_NAME = 'github'
|
||||||
|
export const WORK_VOLUME = 'work'
|
||||||
|
|
||||||
export const CONTAINER_VOLUMES: k8s.V1VolumeMount[] = [
|
export const CONTAINER_VOLUMES: k8s.V1VolumeMount[] = [
|
||||||
{
|
{
|
||||||
name: EXTERNALS_VOLUME_NAME,
|
name: EXTERNALS_VOLUME_NAME,
|
||||||
mountPath: '/__e'
|
mountPath: '/__e'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: WORK_VOLUME,
|
||||||
|
mountPath: '/__w'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: GITHUB_VOLUME_NAME,
|
name: GITHUB_VOLUME_NAME,
|
||||||
mountPath: '/github'
|
mountPath: '/github'
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ describe('e2e', () => {
|
|||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
await testHelper.cleanup()
|
await testHelper.cleanup()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should prepare job, run script step, run container step then cleanup without errors', async () => {
|
it('should prepare job, run script step, run container step then cleanup without errors', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
prepareJob(prepareJobData.args, prepareJobOutputFilePath)
|
prepareJob(prepareJobData.args, prepareJobOutputFilePath)
|
||||||
|
|||||||
@@ -231,4 +231,20 @@ describe('Prepare job', () => {
|
|||||||
expect(() => content.context.services[0].image).not.toThrow()
|
expect(() => content.context.services[0].image).not.toThrow()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
it('should prepare job with container with non-root user', async () => {
|
||||||
|
prepareJobData.args!.container!.image =
|
||||||
|
'ghcr.io/actions/actions-runner:latest' // known to use user 1001
|
||||||
|
await expect(
|
||||||
|
prepareJob(prepareJobData.args, prepareJobOutputFilePath)
|
||||||
|
).resolves.not.toThrow()
|
||||||
|
|
||||||
|
const content = JSON.parse(
|
||||||
|
fs.readFileSync(prepareJobOutputFilePath).toString()
|
||||||
|
)
|
||||||
|
expect(content.state.jobPod).toBeTruthy()
|
||||||
|
expect(content.context.container.image).toBe(
|
||||||
|
'ghcr.io/actions/actions-runner:latest'
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user