Skip to content
This repository was archived by the owner on Jan 16, 2025. It is now read-only.

Commit cfbcc94

Browse files
fix: allow disable JIT config for ephemeral runners (#3393)
* fix: allow disable JIT config for ephemeral runners * docs: auto update terraform docs * format * review, stricter gates test coverage * docs: auto update terraform docs * fix: disable JIT by default * fix: disable JIT by default * fix: disable JIT by default * docs: auto update terraform docs * use app for generating docs * docs: auto update terraform docs * use app for generating docs * use app for generating docs * docs: auto update terraform docs * use app for generating docs * docs: auto update terraform docs * enable jit config by default for ephemeral runners * enable jit config by default for ephemeral runners * docs: auto update terraform docs --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 5710e15 commit cfbcc94

File tree

20 files changed

+137
-26
lines changed

20 files changed

+137
-26
lines changed

Diff for: .github/workflows/update-docs.yml

+23-3
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,28 @@ jobs:
1616
name: Auto update terraform docs
1717
runs-on: ubuntu-latest
1818
steps:
19-
- if: github.event_name == 'push'
20-
name: Checkout branch
19+
- name: Get installation token
20+
uses: philips-software/app-token-action@a37926571e4cec6f219e06727136efdd073d8657 # ratchet:philips-software/[email protected]
21+
id: token
22+
with:
23+
app_id: ${{ secrets.FOREST_RELEASER_APP_ID }}
24+
app_base64_private_key: ${{ secrets.FOREST_RELEASER_APP_PRIVATE_KEY_BASE64 }}
25+
auth_type: installation
26+
27+
- name: Dump GitHub context
28+
env:
29+
GITHUB_CONTEXT: ${{ toJson(github) }}
30+
run: echo "$GITHUB_CONTEXT"
31+
32+
# We use the app for branches in this this repo to ensure PR chekcs are kept in place.
33+
- if: github.event_name == 'push' && github.repository_owner == 'philips-labs'
34+
name: Checkout with App Token
35+
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # ratchet:actions/checkout@v3
36+
with:
37+
token: ${{ steps.token.outputs.token }}
38+
39+
- if: github.event_name == 'push' && github.repository_owner != 'philips-labs'
40+
name: Checkout with GITHUB Action token
2141
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # ratchet:actions/checkout@v3
2242

2343
- name: Generate TF docs
@@ -31,7 +51,7 @@ jobs:
3151
if: github.ref == 'refs/heads/main'
3252
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # ratchet:peter-evans/[email protected]
3353
with:
34-
token: ${{ secrets.GITHUB_TOKEN }}
54+
token: ${{ steps.token.outputs.token || secrets.GITHUB_TOKEN }}
3555
commit-message: "Update Terraform docs"
3656
title: "docs: Update Terraform docs"
3757
branch: ${{ github.event.pull_request.base.ref }}-update-docs

Diff for: README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ To be able to support a number of use-cases the module has quite a lot of config
9292
- Multi-Runner module. This modules allows you to create multiple runner configurations with a single webhook and single GitHub App to simplify deployment of different types of runners. Refer to the [ReadMe](.modules/../modules/multi-runner/README.md) for more information to understand the functionality.
9393
- Workflow job event. You can configure the webhook in GitHub to send workflow job events to the webhook. Workflow job events were introduced by GitHub in September 2021 and are designed to support scalable runners. We advise using the workflow job event when possible.
9494
- Linux vs Windows. You can configure the OS types linux and win. Linux will be used by default.
95-
- Re-use vs Ephemeral. By default runners are re-used, until detected idle. Once idle they will be removed from the pool. To improve security we are introducing ephemeral runners. Those runners are only used for one job. Ephemeral runners are only working in combination with the workflow job event. For ephemeral runners the lambda requests a JIT (just in time) configuration object via the GitHub to register the runner. [JIT configuration](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-just-in-time-runners) is limited to ephemeral runners, for non ephemeral a registration token is requested. In both cases the configuration is made available to the instance via the same SSM parameter. We also suggest using a pre-build AMI to improve the start time of jobs.
95+
- Re-use vs Ephemeral. By default runners are re-used, until detected idle. Once idle they will be removed from the pool. To improve security we are introducing ephemeral runners. Those runners are only used for one job. Ephemeral runners are only working in combination with the workflow job event. For ephemeral runners the lambda requests a JIT (just in time) configuration via the GitHub API to register the runner. [JIT configuration](https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-just-in-time-runners) is limited to ephemeral runners (and currently not supported by GHES). For non-ephemeral a registration token is requested always. In both cases the configuration is made available to the instance via the same SSM parameter. To disable JIT configuration for ephermeral runners set `enable_jit_config` to `false`. We also suggest using a pre-build AMI to improve the start time of jobs for ephemeral runners.
9696
- GitHub Cloud vs GitHub Enterprise Server (GHES). The runners support GitHub Cloud as well GitHub Enterprise Server. For GHES we rely on our community for support and testing. We have no possibility to test ourselves on GHES.
9797
- Spot vs on-demand. The runners use either the EC2 spot or on-demand life cycle. Runners will be created via the AWS [CreateFleet API](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateFleet.html). The module (scale up lambda) will request via the CreateFleet API to create instances in one of the subnets and of the specified instance types.
9898
- ARM64 support via Graviton/Graviton2 instance-types. When using the default example or top-level module, specifying `instance_types` that match a Graviton/Graviton 2 (ARM64) architecture (e.g. a1, t4g or any 6th-gen `g` or `gd` type), you must also specify `runner_architecture = "arm64"` and the sub-modules will be automatically configured to provision with ARM64 AMIs and leverage GitHub's ARM64 action runner. See below for more details.
@@ -504,6 +504,7 @@ We welcome any improvement to the standard module to make the default as secure
504504
| <a name="input_enable_ephemeral_runners"></a> [enable\_ephemeral\_runners](#input\_enable\_ephemeral\_runners) | Enable ephemeral runners, runners will only be used once. | `bool` | `false` | no |
505505
| <a name="input_enable_event_rule_binaries_syncer"></a> [enable\_event\_rule\_binaries\_syncer](#input\_enable\_event\_rule\_binaries\_syncer) | Option to disable EventBridge Lambda trigger for the binary syncer, useful to stop automatic updates of binary distribution. | `bool` | `true` | no |
506506
| <a name="input_enable_fifo_build_queue"></a> [enable\_fifo\_build\_queue](#input\_enable\_fifo\_build\_queue) | Enable a FIFO queue to keep the order of events received by the webhook. Recommended for repo level runners. | `bool` | `false` | no |
507+
| <a name="input_enable_jit_config"></a> [enable\_jit\_config](#input\_enable\_jit\_config) | Overwrite the default behavior for JIT configuration. By default JIT configuration is enabled for ephemeral runners and disabled for non-ephemeral runners. In case of GHES check first if the JIT config API is avaialbe. In case you upgradeing from 3.x to 4.x you can set `enable_jit_config` to `false` to avoid a breaking change when having your own AMI. | `bool` | `null` | no |
507508
| <a name="input_enable_job_queued_check"></a> [enable\_job\_queued\_check](#input\_enable\_job\_queued\_check) | Only scale if the job event received by the scale up lambda is in the queued state. By default enabled for non ephemeral runners and disabled for ephemeral. Set this variable to overwrite the default behavior. | `bool` | `null` | no |
508509
| <a name="input_enable_managed_runner_security_group"></a> [enable\_managed\_runner\_security\_group](#input\_enable\_managed\_runner\_security\_group) | Enables creation of the default managed security group. Unmanaged security groups can be specified via `runner_additional_security_group_ids`. | `bool` | `true` | no |
509510
| <a name="input_enable_organization_runners"></a> [enable\_organization\_runners](#input\_enable\_organization\_runners) | Register runners to organization, instead of repo level | `bool` | `false` | no |

Diff for: lambdas/functions/control-plane/jest.config.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ const config: Config = {
66
...defaultConfig,
77
coverageThreshold: {
88
global: {
9-
statements: 97,
10-
branches: 93,
11-
functions: 96,
9+
statements: 97.6,
10+
branches: 94.6,
11+
functions: 97,
1212
lines: 98,
1313
},
1414
},

Diff for: lambdas/functions/control-plane/src/pool/pool.ts

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export async function adjust(event: PoolEvent): Promise<void> {
2929
const instanceTypes = process.env.INSTANCE_TYPES.split(',');
3030
const instanceTargetTargetCapacityType = process.env.INSTANCE_TARGET_CAPACITY_TYPE;
3131
const ephemeral = yn(process.env.ENABLE_EPHEMERAL_RUNNERS, { default: false });
32+
const enableJitConfig = yn(process.env.ENABLE_JIT_CONFIG, { default: ephemeral });
3233
const disableAutoUpdate = yn(process.env.DISABLE_RUNNER_AUTOUPDATE, { default: false });
3334
const launchTemplateName = process.env.LAUNCH_TEMPLATE_NAME;
3435
const instanceMaxSpotPrice = process.env.INSTANCE_MAX_SPOT_PRICE;
@@ -94,6 +95,7 @@ export async function adjust(event: PoolEvent): Promise<void> {
9495
await createRunners(
9596
{
9697
ephemeral,
98+
enableJitConfig,
9799
ghesBaseUrl,
98100
runnerLabels,
99101
runnerGroup,

Diff for: lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts

+43-1
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,8 @@ describe('scaleUp with public GH', () => {
592592

593593
describe('on repo level', () => {
594594
beforeEach(() => {
595+
mockSSMClient.reset();
596+
595597
process.env.ENABLE_ORGANIZATION_RUNNERS = 'false';
596598
process.env.RUNNER_NAME_PREFIX = 'unit-test';
597599
expectedRunnerParams = { ...EXPECTED_RUNNER_PARAMS };
@@ -668,12 +670,52 @@ describe('scaleUp with public GH', () => {
668670
).rejects.toBeInstanceOf(Error);
669671
});
670672

671-
it('creates a ephemeral runner.', async () => {
673+
it('creates a ephemeral runner with JIT config.', async () => {
672674
process.env.ENABLE_EPHEMERAL_RUNNERS = 'true';
673675
process.env.ENABLE_JOB_QUEUED_CHECK = 'false';
676+
process.env.SSM_TOKEN_PATH = '/github-action-runners/default/runners/config';
677+
await scaleUpModule.scaleUp('aws:sqs', TEST_DATA);
678+
expect(mockOctokit.actions.getJobForWorkflowRun).not.toBeCalled();
679+
expect(createRunner).toBeCalledWith(expectedRunnerParams);
680+
681+
expect(mockSSMClient).toHaveReceivedNthSpecificCommandWith(1, PutParameterCommand, {
682+
Name: '/github-action-runners/default/runners/config/i-12345',
683+
Value: 'TEST_JIT_CONFIG_REPO',
684+
Type: 'SecureString',
685+
});
686+
});
687+
688+
it('creates a ephemeral runner with registration token.', async () => {
689+
process.env.ENABLE_EPHEMERAL_RUNNERS = 'true';
690+
process.env.ENABLE_JIT_CONFIG = 'false';
691+
process.env.ENABLE_JOB_QUEUED_CHECK = 'false';
692+
process.env.SSM_TOKEN_PATH = '/github-action-runners/default/runners/config';
693+
await scaleUpModule.scaleUp('aws:sqs', TEST_DATA);
694+
expect(mockOctokit.actions.getJobForWorkflowRun).not.toBeCalled();
695+
expect(createRunner).toBeCalledWith(expectedRunnerParams);
696+
697+
expect(mockSSMClient).toHaveReceivedNthSpecificCommandWith(1, PutParameterCommand, {
698+
Name: '/github-action-runners/default/runners/config/i-12345',
699+
Value: '--url https://github.com/Codertocat/hello-world --token 1234abcd --ephemeral',
700+
Type: 'SecureString',
701+
});
702+
});
703+
704+
it('JIT config is ingored for non-ephemeral runners.', async () => {
705+
process.env.ENABLE_EPHEMERAL_RUNNERS = 'false';
706+
process.env.ENABLE_JIT_CONFIG = 'true';
707+
process.env.ENABLE_JOB_QUEUED_CHECK = 'false';
708+
process.env.RUNNER_LABELS = 'jit';
709+
process.env.SSM_TOKEN_PATH = '/github-action-runners/default/runners/config';
674710
await scaleUpModule.scaleUp('aws:sqs', TEST_DATA);
675711
expect(mockOctokit.actions.getJobForWorkflowRun).not.toBeCalled();
676712
expect(createRunner).toBeCalledWith(expectedRunnerParams);
713+
714+
expect(mockSSMClient).toHaveReceivedNthSpecificCommandWith(1, PutParameterCommand, {
715+
Name: '/github-action-runners/default/runners/config/i-12345',
716+
Value: '--url https://github.com/Codertocat/hello-world --token 1234abcd --labels jit',
717+
Type: 'SecureString',
718+
});
677719
});
678720

679721
it('creates a ephemeral runner after checking job is queued.', async () => {

Diff for: lambdas/functions/control-plane/src/scale-runners/scale-up.ts

+14-11
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export interface ActionRequestMessage {
3232
interface CreateGitHubRunnerConfig {
3333
ephemeral: boolean;
3434
ghesBaseUrl: string;
35+
enableJitConfig: boolean;
3536
runnerLabels: string;
3637
runnerGroup: string;
3738
runnerNamePrefix: string;
@@ -57,8 +58,8 @@ function generateRunnerServiceConfig(githubRunnerConfig: CreateGitHubRunnerConfi
5758
`--token ${token}`,
5859
];
5960

60-
if (githubRunnerConfig.runnerLabels !== undefined) {
61-
config.push(`--labels ${githubRunnerConfig.runnerLabels}`);
61+
if (githubRunnerConfig.runnerLabels) {
62+
config.push(`--labels ${githubRunnerConfig.runnerLabels}`.trim());
6263
}
6364

6465
if (githubRunnerConfig.disableAutoUpdate) {
@@ -69,6 +70,10 @@ function generateRunnerServiceConfig(githubRunnerConfig: CreateGitHubRunnerConfi
6970
config.push(`--runnergroup ${githubRunnerConfig.runnerGroup}`);
7071
}
7172

73+
if (githubRunnerConfig.ephemeral) {
74+
config.push(`--ephemeral`);
75+
}
76+
7277
return config;
7378
}
7479

@@ -221,6 +226,7 @@ export async function scaleUp(eventSource: string, payload: ActionRequestMessage
221226
const instanceTypes = process.env.INSTANCE_TYPES.split(',');
222227
const instanceTargetTargetCapacityType = process.env.INSTANCE_TARGET_CAPACITY_TYPE;
223228
const ephemeralEnabled = yn(process.env.ENABLE_EPHEMERAL_RUNNERS, { default: false });
229+
const enableJitConfig = yn(process.env.ENABLE_JIT_CONFIG, { default: ephemeralEnabled });
224230
const disableAutoUpdate = yn(process.env.DISABLE_RUNNER_AUTOUPDATE, { default: false });
225231
const launchTemplateName = process.env.LAUNCH_TEMPLATE_NAME;
226232
const instanceMaxSpotPrice = process.env.INSTANCE_MAX_SPOT_PRICE;
@@ -276,6 +282,7 @@ export async function scaleUp(eventSource: string, payload: ActionRequestMessage
276282
await createRunners(
277283
{
278284
ephemeral,
285+
enableJitConfig,
279286
ghesBaseUrl,
280287
runnerLabels,
281288
runnerGroup,
@@ -313,10 +320,10 @@ async function createStartRunnerConfig(
313320
instances: string[],
314321
ghClient: Octokit,
315322
) {
316-
if (githubRunnerConfig.ephemeral) {
317-
await createStartRunnerConfigForEphemeralRunners(githubRunnerConfig, instances, ghClient);
323+
if (githubRunnerConfig.enableJitConfig && githubRunnerConfig.ephemeral) {
324+
await createJitConfig(githubRunnerConfig, instances, ghClient);
318325
} else {
319-
await createStartRunnerConfigForNonEphemeralRunners(githubRunnerConfig, instances, ghClient);
326+
await createRegistrationTokenConfig(githubRunnerConfig, instances, ghClient);
320327
}
321328
}
322329

@@ -327,7 +334,7 @@ function addDelay(instances: string[]) {
327334
return { isDelay, delay };
328335
}
329336

330-
async function createStartRunnerConfigForNonEphemeralRunners(
337+
async function createRegistrationTokenConfig(
331338
githubRunnerConfig: CreateGitHubRunnerConfig,
332339
instances: string[],
333340
ghClient: Octokit,
@@ -349,11 +356,7 @@ async function createStartRunnerConfigForNonEphemeralRunners(
349356
}
350357
}
351358

352-
async function createStartRunnerConfigForEphemeralRunners(
353-
githubRunnerConfig: CreateGitHubRunnerConfig,
354-
instances: string[],
355-
ghClient: Octokit,
356-
) {
359+
async function createJitConfig(githubRunnerConfig: CreateGitHubRunnerConfig, instances: string[], ghClient: Octokit) {
357360
const runnerGroupId = await getRunnerGroupId(githubRunnerConfig, ghClient);
358361
const { isDelay, delay } = addDelay(instances);
359362
const runnerLabels = githubRunnerConfig.runnerLabels.split(',');

Diff for: main.tf

+1
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ module "runners" {
208208
github_app_parameters = local.github_app_parameters
209209
enable_organization_runners = var.enable_organization_runners
210210
enable_ephemeral_runners = var.enable_ephemeral_runners
211+
enable_jit_config = var.enable_jit_config
211212
enable_job_queued_check = var.enable_job_queued_check
212213
disable_runner_autoupdate = var.disable_runner_autoupdate
213214
enable_managed_runner_security_group = var.enable_managed_runner_security_group

0 commit comments

Comments
 (0)