mirror of
https://github.com/tailscale/github-action.git
synced 2026-08-20 07:19:22 +00:00
action.yml,src: add support for workload identity federation
Add support for workload identity federation based authentication. Updates https://github.com/tailscale/corp/issues/31264 Signed-off-by: Mario Minardi <mario@tailscale.com>
This commit is contained in:
parent
c8de7faf7a
commit
ef403f0d06
7 changed files with 190 additions and 50 deletions
47
.github/workflows/test.yml
vendored
47
.github/workflows/test.yml
vendored
|
|
@ -1,5 +1,8 @@
|
|||
name: "Integration Tests"
|
||||
|
||||
permissions:
|
||||
id-token: write # This is required for requesting the JWT for workload identity
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
|
@ -11,7 +14,7 @@ jobs:
|
|||
# Matrix test for all supported platforms and architectures
|
||||
|
||||
integration-tests:
|
||||
name: ${{ matrix.os }} (${{ matrix.arch }}) tailscale-${{ matrix.version }}
|
||||
name: ${{ matrix.os }} (${{ matrix.arch }}) (${{ matrix.credential-type }}) tailscale-${{ matrix.version }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
|
@ -22,17 +25,20 @@ jobs:
|
|||
arch: amd64
|
||||
version: latest
|
||||
ping: 100.99.0.2,lax-pve.pineapplefish.ts.net,lax-pve
|
||||
credential-type: oauth
|
||||
|
||||
# Try unstable too
|
||||
- os: ubuntu-latest
|
||||
runner-os: Linux
|
||||
arch: amd64
|
||||
version: unstable
|
||||
credential-type: oauth
|
||||
|
||||
# Try a pinned version
|
||||
- os: ubuntu-latest
|
||||
runner-os: Linux
|
||||
arch: amd64
|
||||
credential-type: oauth
|
||||
# leave version blank to fall back to default
|
||||
|
||||
# Linux tests (ARM64)
|
||||
|
|
@ -40,6 +46,7 @@ jobs:
|
|||
runner-os: Linux
|
||||
arch: arm64
|
||||
version: latest
|
||||
credential-type: oauth
|
||||
|
||||
# Windows tests (AMD64)
|
||||
- os: windows-latest
|
||||
|
|
@ -47,17 +54,14 @@ jobs:
|
|||
arch: amd64
|
||||
version: latest
|
||||
ping: 100.99.0.2,lax-pve.pineapplefish.ts.net,lax-pve
|
||||
|
||||
- os: windows-latest
|
||||
runner-os: Windows
|
||||
arch: amd64
|
||||
version: unstable
|
||||
credential-type: oauth
|
||||
|
||||
# Windows tests (ARM64)
|
||||
- os: windows-11-arm
|
||||
runner-os: Windows
|
||||
arch: arm64
|
||||
version: latest
|
||||
credential-type: oauth
|
||||
|
||||
# macOS 13 (AMD64)
|
||||
- os: macos-13
|
||||
|
|
@ -65,6 +69,7 @@ jobs:
|
|||
arch: amd64
|
||||
version: latest
|
||||
ping: 100.99.0.2,lax-pve.pineapplefish.ts.net,lax-pve
|
||||
credential-type: oauth
|
||||
|
||||
# macOS 14 (ARM)
|
||||
- os: macos-14
|
||||
|
|
@ -72,6 +77,7 @@ jobs:
|
|||
arch: arm64
|
||||
version: latest
|
||||
ping: 100.99.0.2,lax-pve.pineapplefish.ts.net,lax-pve
|
||||
credential-type: oauth
|
||||
|
||||
# macOS latest (ARM)
|
||||
- os: macos-latest
|
||||
|
|
@ -79,6 +85,30 @@ jobs:
|
|||
arch: arm64
|
||||
version: latest
|
||||
ping: 100.99.0.2,lax-pve.pineapplefish.ts.net,lax-pve
|
||||
credential-type: oauth
|
||||
|
||||
# Try workload identity for each platform
|
||||
- os: macos-latest
|
||||
runner-os: macOS
|
||||
arch: amd64
|
||||
version: latest
|
||||
ping: 100.99.0.2,lax-pve.pineapplefish.ts.net,lax-pve
|
||||
credential-type: workload-identity
|
||||
|
||||
- os: windows-latest
|
||||
runner-os: Windows
|
||||
arch: amd64
|
||||
ping: 100.99.0.2,lax-pve.pineapplefish.ts.net,lax-pve
|
||||
credential-type: workload-identity
|
||||
# leave version blank to fall back to default
|
||||
|
||||
# Try adding in an unstable
|
||||
- os: ubuntu-latest
|
||||
runner-os: Linux
|
||||
arch: amd64
|
||||
version: unstable
|
||||
credential-type: workload-identity
|
||||
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
|
|
@ -103,8 +133,9 @@ jobs:
|
|||
id: tailscale-oauth
|
||||
uses: ./
|
||||
with:
|
||||
oauth-client-id: ${{ secrets.TS_AUTH_KEYS_OAUTH_CLIENT_ID }}
|
||||
oauth-secret: ${{ secrets.TS_AUTH_KEYS_OAUTH_CLIENT_SECRET }}
|
||||
oauth-client-id: ${{ matrix.credential-type == 'oauth' && secrets.TS_AUTH_KEYS_OAUTH_CLIENT_ID || secrets.TS_WORKLOAD_IDENTITY_CLIENT_ID }}
|
||||
oauth-secret: ${{ matrix.credential-type == 'oauth' && secrets.TS_AUTH_KEYS_OAUTH_CLIENT_SECRET || '' }}
|
||||
audience: ${{ matrix.credential-type == 'workload-identity' && secrets.TS_AUDIENCE || ''}}
|
||||
tags: "tag:ci"
|
||||
version: "${{ matrix.version }}"
|
||||
use-cache: false
|
||||
|
|
|
|||
74
README.md
74
README.md
|
|
@ -14,29 +14,58 @@ by adding a step to your workflow.
|
|||
|
||||
Subsequent steps in the Action can then access nodes in your Tailnet.
|
||||
|
||||
oauth-client-id and oauth-secret are an [OAuth client](https://tailscale.com/s/oauth-clients/)
|
||||
oauth-client-id and oauth-secret are an [OAuth client][kb-oauth-clients]
|
||||
for the tailnet to be accessed. We recommend storing these as
|
||||
[GitHub Encrypted Secrets.](https://docs.github.com/en/actions/security-guides/encrypted-secrets)
|
||||
OAuth clients used for this purpose must have the
|
||||
[`auth_keys` scope.](https://tailscale.com/kb/1215/oauth-clients#scopes)
|
||||
[`auth_keys` scope.][kb-trust-credentials-scopes]
|
||||
|
||||
tags is a comma-separated list of one or more [ACL Tags](https://tailscale.com/kb/1068/acl-tags/)
|
||||
tags is a comma-separated list of one or more [Tags][kb-tags]
|
||||
for the node. At least one tag is required: an OAuth client is not associated
|
||||
with any of the Users on the tailnet, it has to Tag its nodes.
|
||||
|
||||
Nodes created by this Action are [marked as Ephemeral](https://tailscale.com/s/ephemeral-nodes) to
|
||||
Nodes created by this Action are [marked as Ephemeral][kb-ephemeral-nodes] to
|
||||
and log out immediately after finishing their CI run, at which point they are automatically removed
|
||||
by the coordination server. The nodes are also [marked Preapproved](https://tailscale.com/kb/1085/auth-keys/)
|
||||
on tailnets which use [Device Approval](https://tailscale.com/kb/1099/device-approval/)
|
||||
by the coordination server. The nodes are also [marked Preapproved][kb-auth-keys]
|
||||
on tailnets which use [Device Approval][kb-device-approval]
|
||||
|
||||
|
||||
### Workload identity federation
|
||||
[Workload identity federation][kb-workload-identity-federation] can also be used for authenticating nodes with your tailnet:
|
||||
|
||||
```yaml
|
||||
- name: Tailscale
|
||||
uses: tailscale/github-action@v4
|
||||
with:
|
||||
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
|
||||
audience: ${{ secrets.TS_AUDIENCE }}
|
||||
tags: tag:ci
|
||||
```
|
||||
|
||||
Workload identity federation requires the `id-token: write` [permission setting](https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-cloud-providers#adding-permissions-settings) for the workflow:
|
||||
|
||||
```yaml
|
||||
permissions:
|
||||
id-token: write # This is required for the tailscale action to request a JWT from GitHub
|
||||
```
|
||||
|
||||
OIDC federated identity credentials used for this purpose must have the [`auth_keys` scope.][kb-trust-credentials-scopes]
|
||||
|
||||
tags is a comma-separated list of one or more [Tags][kb-tags]
|
||||
for the node. At least one tag is required: a federated identity is not associated
|
||||
with any of the Users on the tailnet, it has to Tag its nodes.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Tailscale version `1.90.1` or later is required for workload identity federation.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before using the Tailscale GitHub Action, ensure you have the following:
|
||||
|
||||
1. A Tailscale account with <Role>Owner, Admin, or Network admin</Role> permissions.
|
||||
1. A Tailscale account with Owner, Admin, or Network admin permissions.
|
||||
1. A GitHub repository that you have admin access to (required to set up the GitHub Action).
|
||||
1. At least one configured [tag][kb-tags].
|
||||
1. An [OAuth client][kb-oauth-clients] ID and secret OR an [auth key][kb-auth-keys].
|
||||
1. At least one configured [tag][kb-tags] if using OAuth or workload identity federation.
|
||||
1. An [OAuth client][kb-oauth-clients] ID and secret, [federated identity][kb-workload-identity-federation] client ID and audience, OR an [auth key][kb-auth-keys].
|
||||
1. A runner image version >= 2.237.1 (required to support running Node.js 24).
|
||||
|
||||
## Eventual consistency
|
||||
|
|
@ -55,22 +84,21 @@ You can do this by adding a list of hosts to ping to the action configuration:
|
|||
ping: 100.x.y.z,my-machine.my-tailnet.ts.net
|
||||
```
|
||||
|
||||
or with the [tailscale ping](https://tailscale.com/kb/1080/cli#ping) command if you do not know the peers at the time of installing Tailscale in the workflow:
|
||||
or with the [tailscale ping][kb-cli-ping] command if you do not know the peers at the time of installing Tailscale in the workflow:
|
||||
|
||||
```bash
|
||||
tailscale ping my-target.my-tailnet.ts.net
|
||||
```
|
||||
|
||||
The `ping` option will wait up to to 3 minutes for a connection (direct or relayed).
|
||||
The `ping` option will wait up to 3 minutes for a connection (direct or relayed).
|
||||
|
||||
## Tailnet Lock
|
||||
|
||||
If you are using this Action in a [Tailnet
|
||||
Lock](https://tailscale.com/kb/1226/tailnet-lock) enabled network, you need to:
|
||||
If you are using this Action in a [Tailnet Lock][kb-tailnet-lock] enabled network, you need to:
|
||||
|
||||
- Authenticate using an ephemeral reusable [pre-signed auth key](https://tailscale.com/kb/1226/tailnet-lock#add-a-node-using-a-pre-signed-auth-key)
|
||||
- Authenticate using an ephemeral reusable [pre-signed auth key][kb-tailnet-lock-pre-signed]
|
||||
rather than an OAuth client.
|
||||
- Specify a [state directory](https://tailscale.com/kb/1278/tailscaled#flags-to-tailscaled) for the
|
||||
- Specify a [state directory][kb-tailscaled-flags] for the
|
||||
client to store the Tailnet Key Authority data in.
|
||||
|
||||
```yaml
|
||||
|
|
@ -139,4 +167,18 @@ the GitHub Action leaves tailscale binaries installed but stops the tailscale ba
|
|||
|
||||
### requested tags [tag:mytag] are invalid or not permitted
|
||||
|
||||
You may encounter this error when using an OAuth client. OAuth clients must have the [`auth_keys` scope](https://tailscale.com/kb/1215/oauth-clients#scopes) with one or more [tags](https://tailscale.com/kb/1068/acl-tags/), and the tags specified with `tags` must match all tags on the OAuth client.
|
||||
You may encounter this error when using a trust credential (OAuth client or OIDC federated identity).
|
||||
Trust credentials must have the writable [`auth_keys` scope][kb-trust-credentials-scopes] with one or more [tags][kb-tags],
|
||||
and the tags specified with `tags` must match all tags on the trust credential or be tags owned by the tags on the trust credential.
|
||||
|
||||
[kb-auth-keys]: https://tailscale.com/kb/1085/auth-keys
|
||||
[kb-cli-ping]: https://tailscale.com/kb/1080/cli#ping
|
||||
[kb-device-approval]: https://tailscale.com/kb/1099/device-approval
|
||||
[kb-ephemeral-nodes]: https://tailscale.com/kb/1111/ephemeral-nodes
|
||||
[kb-oauth-clients]: https://tailscale.com/kb/1215/oauth-clients
|
||||
[kb-tags]: https://tailscale.com/kb/1068/tags
|
||||
[kb-tailnet-lock]: https://tailscale.com/kb/1226/tailnet-lock
|
||||
[kb-tailnet-lock-pre-signed]: https://tailscale.com/kb/1226/tailnet-lock#add-a-node-using-a-pre-signed-auth-key
|
||||
[kb-tailscaled-flags]: https://tailscale.com/kb/1278/tailscaled#flags-to-tailscaled
|
||||
[kb-trust-credentials-scopes]: https://tailscale.com/kb/1623/trust-credentials#scopes
|
||||
[kb-workload-identity-federation]: https://tailscale.com/kb/1581/workload-identity-federation
|
||||
|
|
|
|||
|
|
@ -13,7 +13,10 @@ inputs:
|
|||
required: false
|
||||
deprecationMessage: 'An OAuth API client https://tailscale.com/s/oauth-clients is recommended instead of an authkey'
|
||||
oauth-client-id:
|
||||
description: 'Your Tailscale OAuth Client ID.'
|
||||
description: 'Your Tailscale OAuth or OIDC Federated Identity clientID.'
|
||||
required: false
|
||||
audience:
|
||||
description: 'Your Tailscale OIDC Federated Identity Audience'
|
||||
required: false
|
||||
oauth-secret:
|
||||
description: 'Your Tailscale OAuth Client Secret.'
|
||||
|
|
@ -24,7 +27,7 @@ inputs:
|
|||
version:
|
||||
description: 'Tailscale version to use. Specify `latest` to use the latest stable version, and `unstable` to use the latest development version.'
|
||||
required: true
|
||||
default: '1.88.3'
|
||||
default: '1.90.4'
|
||||
args:
|
||||
description: 'Optional additional arguments to `tailscale up`.'
|
||||
required: false
|
||||
|
|
@ -66,4 +69,3 @@ runs:
|
|||
using: 'node24'
|
||||
main: 'dist/index.js'
|
||||
post: 'dist/logout/index.js'
|
||||
|
||||
|
|
|
|||
46
dist/index.js
generated
vendored
46
dist/index.js
generated
vendored
|
|
@ -41124,6 +41124,7 @@ const crypto = __importStar(__nccwpck_require__(76982));
|
|||
const fs = __importStar(__nccwpck_require__(79896));
|
||||
const os = __importStar(__nccwpck_require__(70857));
|
||||
const path = __importStar(__nccwpck_require__(16928));
|
||||
const semver = __importStar(__nccwpck_require__(39318));
|
||||
const promises_1 = __nccwpck_require__(16460);
|
||||
const cmdTailscale = "tailscale";
|
||||
const cmdTailscaleFullPath = "/usr/local/bin/tailscale";
|
||||
|
|
@ -41265,11 +41266,12 @@ async function getInputs() {
|
|||
}
|
||||
}
|
||||
const config = {
|
||||
version: core.getInput("version") || "1.88.3",
|
||||
version: core.getInput("version") || "1.90.4",
|
||||
resolvedVersion: "",
|
||||
arch: "",
|
||||
authKey: authKey,
|
||||
oauthClientId: core.getInput("oauth-client-id") || "",
|
||||
audience: core.getInput("audience") || "",
|
||||
oauthSecret: oauthSecret,
|
||||
tags: core.getInput("tags") || "",
|
||||
hostname: core.getInput("hostname") || "",
|
||||
|
|
@ -41288,8 +41290,15 @@ async function getInputs() {
|
|||
return config;
|
||||
}
|
||||
function validateAuth(config) {
|
||||
if (!config.authKey && (!config.oauthSecret || !config.tags)) {
|
||||
throw new Error("OAuth identity empty, please provide either an auth key or OAuth secret and tags.");
|
||||
if (!config.authKey &&
|
||||
(!config.oauthSecret || !config.tags) &&
|
||||
(!config.audience || !config.oauthClientId || !config.tags)) {
|
||||
throw new Error("Please provide either an auth key, OAuth secret and tags, or federated identity client ID and audience with tags.");
|
||||
}
|
||||
if (config.audience &&
|
||||
semver.valid(config.version) &&
|
||||
semver.gt("1.90.0", config.version)) {
|
||||
throw new Error("Workload identity federation requires using tailscale version 1.90.0 or later.");
|
||||
}
|
||||
}
|
||||
async function resolveVersion(version, runnerOS) {
|
||||
|
|
@ -41637,12 +41646,29 @@ async function connectToTailscale(config, runnerOS) {
|
|||
}
|
||||
// Limit hostname to 63 characters (more will result in the error "not a valid DNS label")
|
||||
hostname = hostname.substring(0, 63);
|
||||
// Prepare auth and tags
|
||||
let finalAuthKey = config.authKey;
|
||||
const tagsArg = [];
|
||||
if (config.oauthSecret) {
|
||||
finalAuthKey = `${config.oauthSecret}?preauthorized=true&ephemeral=true`;
|
||||
tagsArg.push(`--advertise-tags=${config.tags}`);
|
||||
// Prepare auth and tags.
|
||||
//
|
||||
// Items higher in this list take precedence for auth:
|
||||
// 1. Workload identity
|
||||
// 2. OAuth client
|
||||
// 3. Auth key
|
||||
let authArgs;
|
||||
let tagsArg = [];
|
||||
authArgs = [`--authkey=${config.authKey}`];
|
||||
if (config.audience || config.oauthSecret) {
|
||||
tagsArg = [`--advertise-tags=${config.tags}`];
|
||||
if (config.audience) {
|
||||
const token = await core.getIDToken(config.audience);
|
||||
authArgs = [
|
||||
`--client-id=${config.oauthClientId}?preauthorized=true&ephemeral=true`,
|
||||
`--id-token=${token}`,
|
||||
];
|
||||
}
|
||||
else if (config.oauthSecret) {
|
||||
authArgs = [
|
||||
`--authkey=${config.oauthSecret}?preauthorized=true&ephemeral=true`,
|
||||
];
|
||||
}
|
||||
}
|
||||
// Platform-specific args
|
||||
const platformArgs = [];
|
||||
|
|
@ -41653,11 +41679,11 @@ async function connectToTailscale(config, runnerOS) {
|
|||
const upArgs = [
|
||||
"up",
|
||||
...tagsArg,
|
||||
`--authkey=${finalAuthKey}`,
|
||||
`--hostname=${hostname}`,
|
||||
"--accept-routes",
|
||||
...platformArgs,
|
||||
...config.args.split(" ").filter(Boolean),
|
||||
...authArgs,
|
||||
];
|
||||
// Retry logic
|
||||
for (let attempt = 1; attempt <= config.retry; attempt++) {
|
||||
|
|
|
|||
6
package-lock.json
generated
6
package-lock.json
generated
|
|
@ -13,7 +13,8 @@
|
|||
"@actions/core": "^1.10.1",
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/github": "^6.0.1",
|
||||
"@actions/tool-cache": "^2.0.1"
|
||||
"@actions/tool-cache": "^2.0.1",
|
||||
"semver": "^6.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.17.6",
|
||||
|
|
@ -455,6 +456,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz",
|
||||
"integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^4.0.0",
|
||||
"@octokit/graphql": "^7.1.0",
|
||||
|
|
@ -1078,6 +1080,7 @@
|
|||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
},
|
||||
|
|
@ -1183,6 +1186,7 @@
|
|||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@
|
|||
"@actions/core": "^1.10.1",
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/github": "^6.0.1",
|
||||
"@actions/tool-cache": "^2.0.1"
|
||||
"@actions/tool-cache": "^2.0.1",
|
||||
"semver": "^6.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.17.6",
|
||||
|
|
|
|||
56
src/main.ts
56
src/main.ts
|
|
@ -10,6 +10,7 @@ import * as crypto from "crypto";
|
|||
import * as fs from "fs";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import * as semver from "semver";
|
||||
import { setTimeout as wait } from "timers/promises";
|
||||
|
||||
const cmdTailscale = "tailscale";
|
||||
|
|
@ -30,6 +31,7 @@ interface TailscaleConfig {
|
|||
arch: string;
|
||||
authKey: string;
|
||||
oauthClientId: string;
|
||||
audience: string;
|
||||
oauthSecret: string;
|
||||
tags: string;
|
||||
hostname: string;
|
||||
|
|
@ -209,11 +211,12 @@ async function getInputs(): Promise<TailscaleConfig> {
|
|||
}
|
||||
|
||||
const config = {
|
||||
version: core.getInput("version") || "1.88.3",
|
||||
version: core.getInput("version") || "1.90.4",
|
||||
resolvedVersion: "",
|
||||
arch: "",
|
||||
authKey: authKey,
|
||||
oauthClientId: core.getInput("oauth-client-id") || "",
|
||||
audience: core.getInput("audience") || "",
|
||||
oauthSecret: oauthSecret,
|
||||
tags: core.getInput("tags") || "",
|
||||
hostname: core.getInput("hostname") || "",
|
||||
|
|
@ -237,9 +240,23 @@ async function getInputs(): Promise<TailscaleConfig> {
|
|||
}
|
||||
|
||||
function validateAuth(config: TailscaleConfig): void {
|
||||
if (!config.authKey && (!config.oauthSecret || !config.tags)) {
|
||||
if (
|
||||
!config.authKey &&
|
||||
(!config.oauthSecret || !config.tags) &&
|
||||
(!config.audience || !config.oauthClientId || !config.tags)
|
||||
) {
|
||||
throw new Error(
|
||||
"OAuth identity empty, please provide either an auth key or OAuth secret and tags."
|
||||
"Please provide either an auth key, OAuth secret and tags, or federated identity client ID and audience with tags."
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
config.audience &&
|
||||
semver.valid(config.version) &&
|
||||
semver.gt("1.90.0", config.version)
|
||||
) {
|
||||
throw new Error(
|
||||
"Workload identity federation requires using tailscale version 1.90.0 or later."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -677,15 +694,32 @@ async function connectToTailscale(
|
|||
// Limit hostname to 63 characters (more will result in the error "not a valid DNS label")
|
||||
hostname = hostname.substring(0, 63);
|
||||
|
||||
// Prepare auth and tags
|
||||
let finalAuthKey = config.authKey;
|
||||
const tagsArg: string[] = [];
|
||||
// Prepare auth and tags.
|
||||
//
|
||||
// Items higher in this list take precedence for auth:
|
||||
// 1. Workload identity
|
||||
// 2. OAuth client
|
||||
// 3. Auth key
|
||||
let authArgs: string[];
|
||||
let tagsArg: string[] = [];
|
||||
|
||||
if (config.oauthSecret) {
|
||||
finalAuthKey = `${config.oauthSecret}?preauthorized=true&ephemeral=true`;
|
||||
tagsArg.push(`--advertise-tags=${config.tags}`);
|
||||
authArgs = [`--authkey=${config.authKey}`];
|
||||
|
||||
if (config.audience || config.oauthSecret) {
|
||||
tagsArg = [`--advertise-tags=${config.tags}`];
|
||||
|
||||
if (config.audience) {
|
||||
const token = await core.getIDToken(config.audience);
|
||||
authArgs = [
|
||||
`--client-id=${config.oauthClientId}?preauthorized=true&ephemeral=true`,
|
||||
`--id-token=${token}`,
|
||||
];
|
||||
} else if (config.oauthSecret) {
|
||||
authArgs = [
|
||||
`--authkey=${config.oauthSecret}?preauthorized=true&ephemeral=true`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Platform-specific args
|
||||
const platformArgs: string[] = [];
|
||||
if (runnerOS === runnerWindows) {
|
||||
|
|
@ -696,11 +730,11 @@ async function connectToTailscale(
|
|||
const upArgs = [
|
||||
"up",
|
||||
...tagsArg,
|
||||
`--authkey=${finalAuthKey}`,
|
||||
`--hostname=${hostname}`,
|
||||
"--accept-routes",
|
||||
...platformArgs,
|
||||
...config.args.split(" ").filter(Boolean),
|
||||
...authArgs,
|
||||
];
|
||||
|
||||
// Retry logic
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue