Skip to content

Commit

Permalink
fix: advertise required tags when using oauth authkey (#402)
Browse files Browse the repository at this point in the history
  • Loading branch information
artis3n authored Dec 21, 2023
1 parent 172ed73 commit 4a60487
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 45 deletions.
1 change: 0 additions & 1 deletion .ansible-lint
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ exclude_paths:
- .vscode/
- molecule/default/headscale.config.yaml
- molecule/default/init_tailscale_vars.yml
- molecule/oauth/headscale.config.yaml
- molecule/oauth/init_tailscale_vars.yml

skip_list:
Expand Down
29 changes: 29 additions & 0 deletions .github/workflows/pull_request_target.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,35 @@ jobs:
env:
TAILSCALE_CI_KEY: "${{ secrets.TAILSCALE_CI_KEY }}"

molecule-state-oauth:
name: "Test OAuth key support"
runs-on: ubuntu-22.04
environment: E2E

steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}

- name: Install dependency manager
run: pipx install poetry

- name: Set up Python 3.x
id: setup-python
uses: actions/[email protected]
with:
python-version-file: pyproject.toml
cache: 'poetry'

- name: Install packages
run: poetry install --no-interaction

- name: Molecule - State Present
run: poetry run molecule test --scenario-name oauth
env:
# Note the use of the oauth client secret
TAILSCALE_CI_KEY: "${{ secrets.TAILSCALE_OAUTH_CLIENT_SECRET }}"

molecule-headscale:
name: "Test Headscale Compatibility"
runs-on: ubuntu-22.04
Expand Down
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ else
poetry run molecule test --scenario-name state-absent
endif

.PHONY: test-oauth
test-oauth:
ifndef TAILSCALE_OAUTH_CLIENT_SECRET
$(error TAILSCALE_OAUTH_CLIENT_SECRET is not set)
else
poetry run molecule test --scenario-name oauth
endif

.PHONY: test-headscale
test-headscale:
USE_HEADSCALE=true poetry run molecule test --scenario-name default
50 changes: 43 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
This role installs and configures [Tailscale][] on a Linux target.

Supported operating systems:

- Debian / Ubuntu
- CentOS / RedHat
- Rocky Linux / AlmaLinux
Expand All @@ -31,8 +30,7 @@ See the [CI worfklow](https://github.com/artis3n/ansible-role-tailscale/blob/mai
<a href="https://asciinema.org/a/g8P2DT45oedUaxXSKGBKpU2Dl"><img src="docs/demo.gif" width=650 height=450></a>
</div>

> **Note**
>
> [!TIP]
> This role uses Ansible fully qualified collection names (FQCN) and therefore requires Ansible 2.11+.
> Ansible 2.12 is set as the minimum required version as this was the version tested for compatibility during the FQCN refactor.
Expand Down Expand Up @@ -82,10 +80,17 @@ A Node Authorization key can be generated under your Tailscale account. The role
- Auth key (`tskey-auth-XXX-YYYYY`) <https://login.tailscale.com/admin/authkeys>
- OAuth key (`tskey-client-XXX-YYYY`) <https://login.tailscale.com/admin/settings/oauth>

> [!IMPORTANT]
> Using an OAuth key requires additionally setting the following variables: `tailscale_oauth_ephemeral`, `tailscale_oauth_preauthorized`, and `tailscale_oauth_tags`.
Note that auth keys expire up to a maximum of 90 days after they are generated. OAuth secrets do not expire unless revoked, and the generated OAuth access token expires after 1 hour.

For more information, see Tailscale's [OAuth clients](https://tailscale.com/kb/1215/oauth-clients) page, especially [Generating long-lived auth keys](https://tailscale.com/kb/1215/oauth-clients#generating-long-lived-auth-keys).

If an OAuth key is used, be sure to grant the `write` Devices scope to the OAuth client.

![OAuth scopes](/docs/images/oauth_scopes.png)

This value should be treated as a sensitive secret.

### tailscale_oauth_ephemeral
Expand All @@ -106,6 +111,18 @@ Register as an [ephemeral node](https://tailscale.com/kb/1111/ephemeral-nodes),

Skip [manual device approval](https://tailscale.com/kb/1099/device-approval), if `true`.

### tailscale_oauth_tags

> [!NOTE]
> Used only when `tailscale_authkey` is an OAuth key.
**Default**: `""`

Registering new nodes using OAuth credentials requires advertising one or more tags.
Tags are required for OAuth clients managing devices.

> Auth keys generated by this OAuth client must assign tags (or tags managed by these tags) to devices they authorize.
### tailscale_up_skip

**If set to true, `tailscale_authkey` is not required.**
Expand Down Expand Up @@ -169,10 +186,12 @@ Pass any additional command-line arguments to `tailscale up`.
Note that the [command][ansible.builtin.command] module is used, which does not support subshell expressions (`$()`) or bash operations like `;` and `&`.
Only `tailscale up` arguments can be passed in.

> **Warning**
>
> [!CAUTION]
> **Do not use this for `--authkey`.**
> Use the `tailscale_authkey` variable instead.
>
> **Do not use this for `--advertise-tags`.**
> Use the `tailscale_oauth_tags` variable instead.
Any stdout/stderr output from the `tailscale` binary will be printed. Since the tasks move quickly in this section, a 5 second pause is introduced to grant more time for users to realize a message was printed.

Expand Down Expand Up @@ -246,6 +265,22 @@ Get verbose output:
tailscale_authkey: "{{ lookup('env', 'TAILSCALE_KEY') }}"
```
Connect using an OAuth client secret:
```yaml
- name: Servers
hosts: all
roles:
- role: artis3n.tailscale
vars:
verbose: true
tailscale_authkey: "{{ lookup('env', 'TAILSCALE_OAUTH_CLIENT_SECRET') }}"
tailscale_oauth_tags: "tag:oauth"
# Optionally, also include:
tailscale_oauth_ephemeral: true
tailscale_oauth_preauthorized: false
```
Install Tailscale, but don't authenticate to the network:
```yaml
Expand Down Expand Up @@ -283,8 +318,9 @@ Each Docker container creates a new authorized machine in that test account.
The machines are authorized with [ephemeral auth keys][] and are automatically cleaned up within 30 minutes-48 hours.
This value is stored in a [GitHub Action secret][] with the name `TAILSCALE_CI_KEY`.
To test this role locally, store the Tailscale ephemeral auth key in a `TAILSCALE_CI_KEY` env var.
If you are a Collaborator on this repository, you can open a GitHub CodeSpace and the `TAILSCALE_CI_KEY` will be populated for you.
To test OAuth authkey compatibility, a Tailscale OAuth client secret is stored as `TAILSCALE_OAUTH_CLIENT_SECRET`.
To test this role locally, store the Tailscale ephemeral auth key in a `TAILSCALE_CI_KEY` env var and, if running the `oauth` Molecule scenario, add an OAuth client secret in a `TAILSCALE_OAUTH_CLIENT_SECRET` env var.
If you are a Collaborator on this repository, you can open a GitHub CodeSpace and these secrets will be pre-populated for you into the environment.

Alternatively for Molecule testing, you can use a [Headscale][] container that is spun up as part of the create/prepare steps. To do this, set a `USE_HEADSCALE` env variable. For example:

Expand Down
5 changes: 4 additions & 1 deletion defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ state: latest
tailscale_authkey: ""
# Optional command-line arguments for 'tailscale up'
tailscale_args: ""

# Used for OAuth authentication
# Register as an ephemeral node (recommended)
tailscale_oauth_ephemeral: true
# Used for OAuth authentication
# Skip manual device approval
tailscale_oauth_preauthorized: false
# Required device tags to advertise
tailscale_oauth_tags: ""

# Whether to output debug information during role execution
verbose: false
# Whether to skip 'tailscale up'
Expand Down
Binary file added docs/images/oauth_scopes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions molecule/oauth/converge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
name: artis3n.tailscale
vars:
verbose: true
tailscale_oauth_tags: "tag:ci-worker"
34 changes: 0 additions & 34 deletions molecule/oauth/headscale.config.yaml

This file was deleted.

18 changes: 16 additions & 2 deletions tasks/install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,19 +88,33 @@
# OAuth key starts with 'tskey-client-', auth key starts with 'tskey-auth-', with headscale it can be 'unused'
- name: Install | Create authkey string
ansible.builtin.set_fact:
tailscale_authkey_type: >-
{# Check if the key is an OAuth key #}
{% if tailscale_authkey.startswith('tskey-client-') %}
OAuth Client Secret
{% elif tailscale_authkey.startswith('tskey-auth-') %}
API Token
{% else %}
Unknown token format
{% endif %}
tailscale_authkey_sting: >-
{# Check if the key is an OAuth key #}
{% if tailscale_authkey.startswith('tskey-client-') %}
{{ tailscale_authkey }}?ephemeral={{ tailscale_oauth_ephemeral | bool }}&preauthorized={{ tailscale_oauth_preauthorized | bool }}
{{ tailscale_authkey }}?ephemeral={{ tailscale_oauth_ephemeral | bool }}&preauthorized={{ tailscale_oauth_preauthorized | bool }} --advertise-tags={{ tailscale_oauth_tags | trim }}
{# Check if the key is not OAuth (regular authkey or unused) #}
{% else %}
{{ tailscale_authkey }}
{% endif %}
no_log: "{{ not (insecurely_log_authkey | bool) }}"

- name: Install | Authkey Type
ansible.builtin.debug:
msg: "{{ tailscale_authkey_type | trim }}"
when: verbose

- name: Install | Bring Tailscale Up
become: true
ansible.builtin.command: "tailscale up {{ tailscale_args | trim }} --authkey='{{ tailscale_authkey_sting | trim }}'"
ansible.builtin.command: "tailscale up {{ tailscale_args | trim }} --authkey={{ tailscale_authkey_sting | trim }}"
# Since the auth key is included in this task's output, we do not want to log output
no_log: "{{ not (insecurely_log_authkey | bool) }}"
changed_when: true
Expand Down

0 comments on commit 4a60487

Please sign in to comment.