Skip to content

Commit

Permalink
[FEAT] add OAuth key integration and additional URL-style params to -…
Browse files Browse the repository at this point in the history
…-auth-key flags (ephemeral & preauthorized) (#399)

* Add auth-key additional params

* fix bool

* fix identation

* add tailscale_authkey_sting, more docs

* add key info

* fix headscale case and README

* make md linter happy

* Update README.md

Co-authored-by: Ari Kalfus <[email protected]>

* Update README.md

Co-authored-by: Ari Kalfus <[email protected]>

* Update README.md

Co-authored-by: Ari Kalfus <[email protected]>

* Update README.md

Co-authored-by: Ari Kalfus <[email protected]>

* Update README.md

Co-authored-by: Ari Kalfus <[email protected]>

* Update defaults/main.yml

Co-authored-by: Ari Kalfus <[email protected]>

* Update README.md

Co-authored-by: Ari Kalfus <[email protected]>

* Update README.md

Co-authored-by: Ari Kalfus <[email protected]>

* Apply suggestions from code review

Co-authored-by: Ari Kalfus <[email protected]>

* molecule fix

* fix linter

---------

Co-authored-by: Ari Kalfus <[email protected]>
  • Loading branch information
McSim85 and artis3n authored Dec 21, 2023
1 parent d56b263 commit 172ed73
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 5 deletions.
3 changes: 2 additions & 1 deletion .ansible-lint
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ 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:
- yaml[line-length]
Expand Down
30 changes: 27 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
This role installs and configures [Tailscale][] on a Linux target.

Supported operating systems:

- Debian / Ubuntu
- CentOS / RedHat
- Rocky Linux / AlmaLinux
Expand Down Expand Up @@ -76,11 +77,35 @@ Is **not** required if `tailscale_up_skip` is set to `true`.

A Tailscale Node Authorization auth key.

A Node Authorization auth key can be generated under your Tailscale account at <https://login.tailscale.com/admin/authkeys>.
Note that reusable authorization keys now expire 90 days after they are generated.
A Node Authorization key can be generated under your Tailscale account. The role supports two type of keys:

- 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>

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).

This value should be treated as a sensitive secret.

### tailscale_oauth_ephemeral

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

Register as an [ephemeral node](https://tailscale.com/kb/1111/ephemeral-nodes), if `true`.

### tailscale_oauth_preauthorized

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

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

### tailscale_up_skip

**If set to true, `tailscale_authkey` is not required.**
Expand Down Expand Up @@ -271,6 +296,5 @@ USE_HEADSCALE=true molecule test
[ephemeral auth keys]: https://tailscale.com/kb/1111/ephemeral-nodes/
[github action secret]: https://docs.github.com/en/actions/reference/encrypted-secrets
[tailscale]: https://tailscale.com/
[tailscale account]: https://login.tailscale.com/start
[tailscale up docs]: https://tailscale.com/kb/1080/cli/#up
[headscale]: https://github.com/juanfont/headscale/
6 changes: 6 additions & 0 deletions defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ 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
# Whether to output debug information during role execution
verbose: false
# Whether to skip 'tailscale up'
Expand Down
8 changes: 8 additions & 0 deletions molecule/oauth/cleanup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
- name: Cleanup
hosts: instance
tasks:
- name: De-register Tailscale node
become: true
ansible.builtin.command: tailscale logout
changed_when: false
12 changes: 12 additions & 0 deletions molecule/oauth/converge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
- name: Converge
hosts: instance
tasks:
- name: Init tailscale credentials variables
ansible.builtin.include_tasks: init_tailscale_vars.yml

- name: "Include artis3n.tailscale"
ansible.builtin.include_role:
name: artis3n.tailscale
vars:
verbose: true
34 changes: 34 additions & 0 deletions molecule/oauth/headscale.config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# minimal Headscale configuration for local testing
# See upstream example file for full description of all options:
# https://github.com/juanfont/headscale/blob/main/config-example.yaml
server_url: http://headscale:8080
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 0.0.0.0:9090
private_key_path: /etc/headscale/private.key
noise:
private_key_path: /etc/headscale/noise_private.key
db_type: sqlite3
db_path: /etc/headscale/db.sqlite

# Default Tailscale prefixes
ip_prefixes:
- fd7a:115c:a1e0::/48
- 100.64.0.0/10

# Disable TLS
tls_cert_path: ""
tls_key_path: ""

# Add DNS configuration so we can --accept-dns
dns_config:
override_local_dns: true
nameservers:
- 1.1.1.1

derp:
server:
enabled: true
region_id: 999
region_code: "headscale"
region_name: "Headscale Embedded DERP"
stun_listen_addr: "0.0.0.0:3478"
22 changes: 22 additions & 0 deletions molecule/oauth/init_tailscale_vars.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
- name: Use tailscale service
ansible.builtin.set_fact:
tailscale_authkey: "{{ lookup('ansible.builtin.env', 'TAILSCALE_OAUTH_CLIENT_SECRET') }}"
when: not lookup('ansible.builtin.env', 'USE_HEADSCALE', default=false)

- name: Fetch headscale preauth key
delegate_to: localhost
changed_when: false
community.docker.docker_container_exec:
container: headscale
command: headscale preauthkeys list -u test -o json
register: preauth_list
when: lookup('ansible.builtin.env', 'USE_HEADSCALE', default=false)

- name: Use headscale service
vars:
combined_args: "{{ tailscale_args|default('') }} --login-server=http://headscale:8080"
ansible.builtin.set_fact:
tailscale_authkey: "{{ (preauth_list.stdout|from_json)[0].key }}"
tailscale_args: "{{ combined_args }}"
when: lookup('ansible.builtin.env', 'USE_HEADSCALE', default=false)
46 changes: 46 additions & 0 deletions molecule/oauth/molecule.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
dependency:
name: galaxy
options:
requirements-file: requirements.yml
driver:
name: docker
platforms:
- name: instance
image: ${MOLECULE_DISTRO:-geerlingguy/docker-ubuntu2204-ansible:latest}
command: ${MOLECULE_COMMAND:-/usr/sbin/init}
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
docker_networks:
- name: headscale
networks:
- name: bridge
- name: headscale
cgroupns_mode: host
privileged: true
pre_build_image: true
- name: headscale
image: ${HEADSCALE_IMAGE:-headscale/headscale:latest}
command: headscale serve
pre_build_image: true
networks:
- name: headscale
volumes:
- "${MOLECULE_PROJECT_DIRECTORY}/molecule/default/headscale.config.yaml:/etc/headscale/config.yaml"
provisioner:
name: ansible
verifier:
name: ansible
scenario:
name: oauth
test_sequence:
- dependency
- destroy
- syntax
- create
- prepare
- converge
- idempotence
- verify
- cleanup
- destroy
26 changes: 26 additions & 0 deletions molecule/oauth/prepare.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
- name: Prepare
hosts: localhost
gather_facts: false
tasks:
- name: Create Headscale user
community.docker.docker_container_exec:
container: headscale
command: headscale users create test

- name: Create preauth key
community.docker.docker_container_exec:
container: headscale
command: headscale preauthkeys create -u test --reusable

- name: Fetch Headscale container info
community.docker.docker_container_info:
name: headscale
register: headscale_info

- name: Set hosts override for Headscale
delegate_to: instance
ansible.builtin.lineinfile:
path: /etc/hosts
line: "{{ headscale_info.container.NetworkSettings.Networks.headscale.IPAddress }} headscale"
unsafe_writes: true # Hosts file in the docker container can't be written to atomically
15 changes: 15 additions & 0 deletions molecule/oauth/verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
- name: Verify
hosts: instance
tasks:
- name: Get Tailscale status
become: true
ansible.builtin.command: tailscale status
changed_when: false
register: tailscale_status

- name: Assertions
ansible.builtin.assert:
that:
- "'Logged out.' not in tailscale_status.stdout"
- "'not logged in' not in tailscale_status.stdout"
15 changes: 14 additions & 1 deletion tasks/install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,22 @@
mode: '0644'
register: state_file

# 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_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 }}
{# Check if the key is not OAuth (regular authkey or unused) #}
{% else %}
{{ tailscale_authkey }}
{% endif %}
no_log: "{{ not (insecurely_log_authkey | bool) }}"

- name: Install | Bring Tailscale Up
become: true
ansible.builtin.command: "tailscale up {{ tailscale_args | trim }} --authkey={{ tailscale_authkey }}"
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 172ed73

Please sign in to comment.