mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2026-10-03 17:54:12 +00:00
ci: authenticate to AWS via GitHub OIDC using the role_arn credential path (#1582)
This repo is public and held a long-lived AWS access key as repository secrets
(set 2023-11-22). It is replaced with short-lived credentials from the GitHub OIDC
provider; no AWS key and no account id remain in the repo.
create-test-db.js wrote {access_key_id, secret_access_key, aws_region} into the test
database as the aws speech credential, which sends speech-utils' getAwsAuthToken down
its access-key branch and calls GetSessionToken -- rejected by AWS for session
credentials. The role_arn branch calls AssumeRole instead, which accepts them, and is
already plumbed through db-utils.js, call-session.js and stt-task.js. The pinned
speech-utils 0.2.30 already supports it, so no dependency change is needed.
Fork pull requests receive neither secrets nor an OIDC token, so the credentials step
is guarded by a condition; the AWS tests then skip for forks exactly as they do today.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
db18b558ce
commit
a6663198da
@@ -5,14 +5,24 @@ on: [push, pull_request]
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
id-token: write # required to request the GitHub OIDC token for AWS
|
||||||
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
# Pull requests from forks receive neither secrets nor an OIDC token, so this
|
||||||
|
# step is skipped for them; the AWS tests then skip too, exactly as they do
|
||||||
|
# today. Without the guard the step would hard-fail every fork PR.
|
||||||
|
- uses: aws-actions/configure-aws-credentials@v4
|
||||||
|
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
|
||||||
|
with:
|
||||||
|
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
|
||||||
|
aws-region: us-east-1
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run jslint
|
- run: npm run jslint
|
||||||
- run: npm run test:unit
|
|
||||||
- name: Install Docker Compose
|
- name: Install Docker Compose
|
||||||
run: |
|
run: |
|
||||||
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||||
@@ -22,8 +32,9 @@ jobs:
|
|||||||
- run: npm test
|
- run: npm test
|
||||||
env:
|
env:
|
||||||
GCP_JSON_KEY: ${{ secrets.GCP_JSON_KEY }}
|
GCP_JSON_KEY: ${{ secrets.GCP_JSON_KEY }}
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
# No AWS keys are stored as repository secrets. configure-aws-credentials
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
# above provides short-lived OIDC credentials, and AWS_ROLE_ARN makes the
|
||||||
AWS_REGION: ${{ secrets.AWS_REGION }}
|
# test speech credential use the AssumeRole path, which accepts them.
|
||||||
|
AWS_ROLE_ARN: ${{ secrets.AWS_ROLE_ARN }}
|
||||||
MICROSOFT_REGION: ${{ secrets.MICROSOFT_REGION }}
|
MICROSOFT_REGION: ${{ secrets.MICROSOFT_REGION }}
|
||||||
MICROSOFT_API_KEY: ${{ secrets.MICROSOFT_API_KEY }}
|
MICROSOFT_API_KEY: ${{ secrets.MICROSOFT_API_KEY }}
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ const getCleanupIntervalMins = () => {
|
|||||||
const AWS_REGION = process.env.AWS_REGION;
|
const AWS_REGION = process.env.AWS_REGION;
|
||||||
const AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID;
|
const AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID;
|
||||||
const AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY;
|
const AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY;
|
||||||
|
const AWS_ROLE_ARN = process.env.AWS_ROLE_ARN;
|
||||||
const AWS_SNS_PORT = parseInt(process.env.AWS_SNS_PORT, 10) || 3001;
|
const AWS_SNS_PORT = parseInt(process.env.AWS_SNS_PORT, 10) || 3001;
|
||||||
const AWS_SNS_TOPIC_ARN = process.env.AWS_SNS_TOPIC_ARN;
|
const AWS_SNS_TOPIC_ARN = process.env.AWS_SNS_TOPIC_ARN;
|
||||||
const AWS_SNS_PORT_MAX = parseInt(process.env.AWS_SNS_PORT_MAX, 10) || 3005;
|
const AWS_SNS_PORT_MAX = parseInt(process.env.AWS_SNS_PORT_MAX, 10) || 3005;
|
||||||
@@ -198,6 +199,7 @@ module.exports = {
|
|||||||
AWS_REGION,
|
AWS_REGION,
|
||||||
AWS_ACCESS_KEY_ID,
|
AWS_ACCESS_KEY_ID,
|
||||||
AWS_SECRET_ACCESS_KEY,
|
AWS_SECRET_ACCESS_KEY,
|
||||||
|
AWS_ROLE_ARN,
|
||||||
AWS_SNS_PORT,
|
AWS_SNS_PORT,
|
||||||
AWS_SNS_TOPIC_ARN,
|
AWS_SNS_TOPIC_ARN,
|
||||||
AWS_SNS_PORT_MAX,
|
AWS_SNS_PORT_MAX,
|
||||||
|
|||||||
+14
-1
@@ -5,6 +5,7 @@ const {encrypt} = require('../lib/utils/encrypt-decrypt');
|
|||||||
const {
|
const {
|
||||||
GCP_JSON_KEY,
|
GCP_JSON_KEY,
|
||||||
AWS_ACCESS_KEY_ID,
|
AWS_ACCESS_KEY_ID,
|
||||||
|
AWS_ROLE_ARN,
|
||||||
AWS_SECRET_ACCESS_KEY,
|
AWS_SECRET_ACCESS_KEY,
|
||||||
AWS_REGION,
|
AWS_REGION,
|
||||||
MICROSOFT_REGION,
|
MICROSOFT_REGION,
|
||||||
@@ -32,7 +33,19 @@ test('creating schema', (t) => {
|
|||||||
t.pass('adding google credentials');
|
t.pass('adding google credentials');
|
||||||
sql.push(`UPDATE speech_credentials SET credential='${google_credential}' WHERE vendor='google';`);
|
sql.push(`UPDATE speech_credentials SET credential='${google_credential}' WHERE vendor='google';`);
|
||||||
}
|
}
|
||||||
if (AWS_ACCESS_KEY_ID && AWS_SECRET_ACCESS_KEY) {
|
// Prefer role_arn. Under GitHub OIDC the ambient credentials are temporary, and
|
||||||
|
// speech-utils' getAwsAuthToken calls GetSessionToken on its access-key branch --
|
||||||
|
// which AWS rejects for session credentials. The role_arn branch calls AssumeRole
|
||||||
|
// instead, which works with temporary credentials.
|
||||||
|
if (AWS_ROLE_ARN) {
|
||||||
|
const aws_credential = encrypt(JSON.stringify({
|
||||||
|
role_arn: AWS_ROLE_ARN,
|
||||||
|
aws_region: AWS_REGION
|
||||||
|
}));
|
||||||
|
t.pass('adding aws credentials (role_arn)');
|
||||||
|
sql.push(`UPDATE speech_credentials SET credential='${aws_credential}' WHERE vendor='aws';`);
|
||||||
|
}
|
||||||
|
else if (AWS_ACCESS_KEY_ID && AWS_SECRET_ACCESS_KEY) {
|
||||||
const aws_credential = encrypt(JSON.stringify({
|
const aws_credential = encrypt(JSON.stringify({
|
||||||
access_key_id: AWS_ACCESS_KEY_ID,
|
access_key_id: AWS_ACCESS_KEY_ID,
|
||||||
secret_access_key: AWS_SECRET_ACCESS_KEY,
|
secret_access_key: AWS_SECRET_ACCESS_KEY,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const {provisionCallHook} = require('./utils')
|
|||||||
const {
|
const {
|
||||||
GCP_JSON_KEY,
|
GCP_JSON_KEY,
|
||||||
AWS_ACCESS_KEY_ID,
|
AWS_ACCESS_KEY_ID,
|
||||||
|
AWS_ROLE_ARN,
|
||||||
AWS_SECRET_ACCESS_KEY,
|
AWS_SECRET_ACCESS_KEY,
|
||||||
SONIOX_API_KEY,
|
SONIOX_API_KEY,
|
||||||
DEEPGRAM_API_KEY,
|
DEEPGRAM_API_KEY,
|
||||||
@@ -190,7 +191,7 @@ test('\'gather\' test - microsoft', async(t) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('\'gather\' test - aws', async(t) => {
|
test('\'gather\' test - aws', async(t) => {
|
||||||
if (!AWS_ACCESS_KEY_ID || !AWS_SECRET_ACCESS_KEY) {
|
if (!AWS_ROLE_ARN && (!AWS_ACCESS_KEY_ID || !AWS_SECRET_ACCESS_KEY)) {
|
||||||
t.pass('skipping aws tests');
|
t.pass('skipping aws tests');
|
||||||
return t.end();
|
return t.end();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ const clearModule = require('clear-module');
|
|||||||
const {provisionCallHook} = require('./utils')
|
const {provisionCallHook} = require('./utils')
|
||||||
const {
|
const {
|
||||||
GCP_JSON_KEY,
|
GCP_JSON_KEY,
|
||||||
AWS_ACCESS_KEY_ID,
|
AWS_ACCESS_KEY_ID,
|
||||||
|
AWS_ROLE_ARN,
|
||||||
AWS_SECRET_ACCESS_KEY,
|
AWS_SECRET_ACCESS_KEY,
|
||||||
MICROSOFT_REGION,
|
MICROSOFT_REGION,
|
||||||
MICROSOFT_API_KEY,
|
MICROSOFT_API_KEY,
|
||||||
@@ -103,7 +104,7 @@ test('\'transcribe\' test - microsoft', async(t) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('\'transcribe\' test - aws', async(t) => {
|
test('\'transcribe\' test - aws', async(t) => {
|
||||||
if (!AWS_ACCESS_KEY_ID || !AWS_SECRET_ACCESS_KEY) {
|
if (!AWS_ROLE_ARN && (!AWS_ACCESS_KEY_ID || !AWS_SECRET_ACCESS_KEY)) {
|
||||||
t.pass('skipping aws tests');
|
t.pass('skipping aws tests');
|
||||||
return t.end();
|
return t.end();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user