Skip to content

Commit

Permalink
feat: add node facts after role installation completes (#464)
Browse files Browse the repository at this point in the history
* feat: add node facts after role installation completes

* fix: don't generate role facts if skip authentication is true

* change task names
  • Loading branch information
artis3n authored Apr 19, 2024
1 parent a1c655d commit 1f7228e
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 86 deletions.
212 changes: 127 additions & 85 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# artis3n.tailscale
# artis3n.tailscale <!-- omit in toc -->

[![Ansible Role](https://img.shields.io/ansible/role/d/artis3n/tailscale)](https://galaxy.ansible.com/ui/standalone/roles/artis3n/tailscale/)
[![GitHub release (latest SemVer including pre-releases)](https://img.shields.io/github/v/release/artis3n/ansible-role-tailscale?include_prereleases)](https://github.com/artis3n/ansible-role-tailscale/releases)
Expand Down Expand Up @@ -35,37 +35,54 @@ See the [CI worfklow](https://github.com/artis3n/ansible-role-tailscale/blob/mai
> 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.
# State Tracking

This role will create an `artis3n-tailscale` directory in the target's [`XDG_STATE_HOME`](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) directory,
or `$HOME/.local/state` if the variable is not present,
in order to maintain a concept of state from the configuration of the arguments passed to `tailscale up`.
This allows the role to idempotently update a Tailscale node's configuration when needed.
Deleting this directory will lead to this role re-configuring Tailscale when it is not needed,
but will not otherwise break anything.
However, it is recommended that you let this Ansible role manage this directory and its contents.

Note that:

> Flags are not persisted between runs; you must specify all flags each time.
>
> ...
>
> In Tailscale v1.8 or later, if you forget to specify a flag you added before,
> the CLI will warn you and provide a copyable command that includes all existing flags.
<small>
- [Role Outputs](#role-outputs)
- [Role Variables](#role-variables)
- [Required](#required)
- [tailscale\_authkey](#tailscale_authkey)
- [tailscale\_tags](#tailscale_tags)
- [tailscale\_up\_skip](#tailscale_up_skip)
- [Optional](#optional)
- [state](#state)
- [tailscale\_args](#tailscale_args)
- [tailscale\_oauth\_ephemeral](#tailscale_oauth_ephemeral)
- [tailscale\_oauth\_preauthorized](#tailscale_oauth_preauthorized)
- [insecurely\_log\_authkey](#insecurely_log_authkey)
- [release\_stability](#release_stability)
- [tailscale\_up\_timeout](#tailscale_up_timeout)
- [verbose](#verbose)
- [Dependencies](#dependencies)
- [Collections](#collections)
- [Example Playbook](#example-playbook)
- [State Tracking](#state-tracking)
- [License](#license)
- [Author Information](#author-information)
- [Development and Contributing](#development-and-contributing)

\- [docs: tailscale up][tailscale up docs]

</small>

This role will bubble up any stderr messages from the Tailscale binary
to resolve any end-user configuration errors with `tailscale up` arguments.
The `--authkey=` value will be redacted unless [`insecurely_log_authkey`](#insecurely_log_authkey) is set to `true`.

![logged stderr](docs/images/printed_stderr.png)

# Role Outputs

This role provides the IP v4 and v6 addresses of the Tailscale node as well as the output of `tailscale whois` against the node as facts.
Several key pieces of `whois` information are provided directly, with the rest of the whois output stored as a JSON fact for your convenience.

Outputted facts:

```
tailscale_node_ipv4 (string): The IPv4 address of the Tailscale node.
tailscale_node_ipv6 (string): The IPv6 address of the Tailscale node.
tailscale_node_hostname_full (string): The full hostname (node.domain.ts.net) of the Tailscale node.
tailscale_node_hostname_short (string): The short hostname (node) of the Tailscale node.
tailscale_node_created_at (string): The ISO-8601 timestamp the Tailscale node was created.
tailscale_node_tags (list): The tags assigned to the Tailscale node.
tailscale_node_services (list): The discovered services running on the Tailscale node.
tailscale_node_whois (dict): The full output of `tailscale whois` against the Tailscale node.
```

# Role Variables

## Required
Expand All @@ -76,7 +93,7 @@ In most cases you will use `tailscale_authkey`.
If you are uninstalling Tailscale (`state: absent`),
neither `tailscale_authkey` nor `tailscale_up_skip` is required.

If you are authenticating with an OAuth key, you must also set `tailscale_tags` (see below).
If you are authenticating with an OAuth key, you must also set `tailscale_tags`.

### tailscale_authkey

Expand All @@ -90,7 +107,7 @@ A Node Authorization key can be generated under your Tailscale account. The role
- OAuth key (`tskey-client-XXX-YYYY`) <https://login.tailscale.com/admin/settings/oauth>

> [!IMPORTANT]
> Using an OAuth key requires additionally setting the following variables:
> Using an OAuth key requires the following role variables:
> `tailscale_tags` (must be provided),
> `tailscale_oauth_ephemeral` (defaults to `true`),
> and `tailscale_oauth_preauthorized` (defaults to `false`).
Expand All @@ -107,24 +124,6 @@ If an OAuth key is used, be sure to grant the `write` Devices scope to the OAuth

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_tags

**Default**: `[]`
Expand All @@ -139,8 +138,6 @@ For more information, see [What are tags?](https://tailscale.com/kb/1068/acl-tag
Entries should not include `tag:`.
For example, `tailscale_tags: ['worker']` translates to `--advertise-tags=tag:worker`.

> 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 All @@ -153,39 +150,6 @@ when the server should not yet authenticate to your Tailscale network.

## Optional

### insecurely_log_authkey

**Default**: `false`

If set to `true`, the "Bring Tailscale Up" command will include the raw value of the Tailscale authkey
when logging any errors encountered during `tailscale up`.
By default, the authkey is not logged in successful task completions
and is redacted in the `stderr` output by this role if an error occurs.

![redacted authkey](docs/images/redacted_authkey.png)

If you are encountering an error bringing Tailscale up
and want the "Bring Tailscale Up" task to _not_ redact the value of the authkey,
set this variable to `true`.

Regardless, if the authkey is invalid, the role will relay Tailscale's error message on that fact:

![invalid authkey](docs/images/invalid_authkey.png)

### release_stability

**Default**: `stable`

Whether to use the Tailscale stable or unstable track.

`stable`:

> Stable releases. If you're not sure which track to use, pick this one.
`unstable`:

> The bleeding edge. Pushed early and often. Expect rough edges!
### state

**Default**: `latest`
Expand All @@ -197,6 +161,7 @@ This role uses `latest` by default to help ensure your software remains up-to-da
and incorporates the latest security and product features.
For users who desire more control over configuration drift,
`present` will not update Tailscale if it is already installed.

Changes to [`tailscale_args`](#tailscale_args) will be applied under both `latest` and `present`;
this parameter only impacts the version of Tailscale installed to the target system.

Expand All @@ -207,7 +172,7 @@ Note that neither `tailscale_authkey` nor `tailscale_up_skip` is required if `st

### tailscale_args

Pass any additional command-line arguments to `tailscale up`.
Pass 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 `&`.
Expand All @@ -233,6 +198,57 @@ Stderrs will continue to fail the role's execution.
The sensitive `--authkey` value will be redacted by default.
If you need to view the unredacted value, see [`insecurely_log_authkey`](#insecurely_log_authkey).

### 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`.

### insecurely_log_authkey

**Default**: `false`

If set to `true`, the "Bring Tailscale Up" command will include the raw value of the Tailscale authkey
when logging any errors encountered during `tailscale up`.
By default, the authkey is not logged in successful task completions
and is redacted in the `stderr` output by this role if an error occurs.

![redacted authkey](docs/images/redacted_authkey.png)

If you are encountering an error bringing Tailscale up
and want the "Bring Tailscale Up" task to _not_ redact the value of the authkey,
set this variable to `true`.

Regardless, if the authkey is invalid, the role will relay Tailscale's error message on that fact:

![invalid authkey](docs/images/invalid_authkey.png)

### release_stability

**Default**: `stable`

Whether to use the Tailscale stable or unstable track.

`stable`:

> Stable releases. If you're not sure which track to use, pick this one.
`unstable`:

> The bleeding edge. Pushed early and often. Expect rough edges!
### tailscale_up_timeout

**Default**: `120`
Expand Down Expand Up @@ -346,6 +362,31 @@ De-register and uninstall a Tailscale node:
state: absent
```
# State Tracking
This role will create an `artis3n-tailscale` directory in the target's [`XDG_STATE_HOME`](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) directory,
or `$HOME/.local/state` if the variable is not present,
in order to maintain a concept of state from the configuration of the arguments passed to `tailscale up`.
This allows the role to idempotently update a Tailscale node's configuration when needed.
Deleting this directory will lead to this role re-configuring Tailscale when it is not needed,
but will not otherwise break anything.
However, it is recommended that you let this Ansible role manage this directory and its contents.

Note that:

> Flags are not persisted between runs; you must specify all flags each time.
>
> ...
>
> In Tailscale v1.8 or later, if you forget to specify a flag you added before,
> the CLI will warn you and provide a copyable command that includes all existing flags.

<small>

\- [docs: tailscale up][tailscale up docs]

</small>

# License

MIT
Expand All @@ -359,18 +400,18 @@ Ari Kalfus ([@artis3n](https://www.artis3nal.com/)) <[email protected]>
This GitHub repository uses a dedicated "test" Tailscale account to authenticate Tailscale during CI runs.
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.
and are automatically cleaned up.

This value is stored in a [GitHub Action secret][] with the name `TAILSCALE_CI_KEY`.
This authkey is stored in a [GitHub Action secret][] with the name `TAILSCALE_CI_KEY`.
To test OAuth authkey compatibility, a Tailscale OAuth client secret is stored as `TAILSCALE_OAUTH_CLIENT_SECRET`.
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.
you can open a GitHub CodeSpace and these secrets will be pre-populated for you in the environment.

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_CI_KEY` env var.
add an OAuth client secret in a `TAILSCALE_OAUTH_CLIENT_SECRET` env var.

Alternatively for Molecule testing,
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 All @@ -382,6 +423,7 @@ USE_HEADSCALE=true molecule test
[ansible.builtin.command]: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/command_module.html
[ephemeral auth keys]: https://tailscale.com/kb/1111/ephemeral-nodes/
[github action secret]: https://docs.github.com/en/actions/reference/encrypted-secrets
[molecule]: https://ansible.readthedocs.io/projects/molecule/
[tailscale]: https://tailscale.com/
[tailscale up docs]: https://tailscale.com/kb/1080/cli/#up
[headscale]: https://github.com/juanfont/headscale/
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "artis3n.tailscale"
version = "4.4.4"
version = "4.5.0"
description = "Ansible role to install and enable a Tailscale node."
authors = ["Artis3n <[email protected]>"]
license = "MIT"
Expand Down
48 changes: 48 additions & 0 deletions tasks/facts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
- name: Facts | Get IPv4 address
ansible.builtin.command:
cmd: tailscale ip --4
register: tailscale_ipv4_cmd
changed_when: false

- name: Facts | Get IPv6 address
ansible.builtin.command:
cmd: tailscale ip --6
register: tailscale_ipv6_cmd
changed_when: false

- name: Facts | Register IP facts
ansible.builtin.set_fact:
tailscale_node_ipv4: "{{ tailscale_ipv4_cmd.stdout }}"
tailscale_node_ipv6: "{{ tailscale_ipv6_cmd.stdout }}"

- name: Facts | Get Tailscale host facts
ansible.builtin.command:
cmd: tailscale whois --json {{ tailscale_node_ipv4 }}
register: tailscale_whois_cmd
changed_when: false

- name: Facts | Parse Tailscale host information
ansible.builtin.set_fact:
tailscale_whois: "{{ tailscale_whois_cmd.stdout | from_json }}"

- name: Facts | Set Tailscale host facts
ansible.builtin.set_fact:
tailscale_node_hostname_full: "{{ tailscale_whois.Node.Name }}"
tailscale_node_hostname_short: "{{ tailscale_whois.Node.Hostinfo.Hostname }}"
tailscale_node_created_at: "{{ tailscale_whois.Node.Created }}"
tailscale_node_services: "{{ tailscale_whois.Node.Hostinfo.Services | default([]) }}"
tailscale_node_tags: "{{ tailscale_whois.Node.Tags | default([]) }}"
tailscale_node_whois: "{{ tailscale_whois.Node }}"

- name: Facts | Display key facts
ansible.builtin.debug:
var: "{{ item }}"
when: verbose
loop:
- tailscale_node_hostname_full
- tailscale_node_hostname_short
- tailscale_node_created_at
- tailscale_node_ipv4
- tailscale_node_ipv6
- tailscale_node_tags
5 changes: 5 additions & 0 deletions tasks/install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,8 @@
msg: "{{ tailscale_start.stderr | default () | regex_replace(tailscale_authkey, 'REDACTED') | regex_replace('\\t', '') | split('\n') }}"
when:
- tailscale_start is failed

- name: Install | Register role facts
ansible.builtin.include_tasks: facts.yml
when:
- not tailscale_up_skip

0 comments on commit 1f7228e

Please sign in to comment.