From 1de02f4d5cf28d3c7e48dc6937a243e18eacdfdc Mon Sep 17 00:00:00 2001 From: Max Kramarenko <32900310+McSim85@users.noreply.github.com> Date: Mon, 25 Dec 2023 20:23:13 +0100 Subject: [PATCH] [FEAT] Improve tags support (#407) * better tags handling * better tags handling * fix * Apply suggestions from code review Co-authored-by: Ari Kalfus * fix review comments * tailscale_up_timeout to README --------- Co-authored-by: Ari Kalfus --- README.md | 40 +++++++++++++++++++++++++++---------- defaults/main.yml | 6 ++++-- molecule/oauth/converge.yml | 3 ++- tasks/install.yml | 39 ++++++++++++++++++++++++++---------- tasks/main.yml | 18 +++++++++++++++++ tasks/templates/state.j2 | 4 ++-- 6 files changed, 83 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index cd5bc2de..a9280716 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,8 @@ 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). + ### tailscale_authkey Is **not** required if `tailscale_up_skip` is set to `true`. @@ -81,7 +83,7 @@ A Node Authorization key can be generated under your Tailscale account. The role - OAuth key (`tskey-client-XXX-YYYY`) > [!IMPORTANT] -> Using an OAuth key requires additionally setting the following variables: `tailscale_oauth_ephemeral`, `tailscale_oauth_preauthorized`, and `tailscale_oauth_tags`. +> Using an OAuth key requires additionally setting the following variables: `tailscale_tags` (must be provided), `tailscale_oauth_ephemeral` (defailts to `true`) and `tailscale_oauth_preauthorized` (defaults to `false`). 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. @@ -111,15 +113,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 +### tailscale_tags -> [!NOTE] -> Used only when `tailscale_authkey` is an OAuth key. +**Default**: `[]` -**Default**: `""` +Apply supplied tags to the Tailscale nodes configured by this role (via the `--advertise-tags` flag to `tailscale up`). +For more information, see [What are tags?](https://tailscale.com/kb/1068/acl-tags?q=acl%20tag#what-are-acl-tags) -Registering new nodes using OAuth credentials requires advertising one or more tags. -Tags are required for OAuth clients managing devices. +> [!NOTE] +> Tags are required for OAuth clients (`tailscale_authkey` OAuth key). + +Entries should not include `tag:`. +For example, `tailscale_args: ['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. @@ -190,9 +195,11 @@ Only `tailscale up` arguments can be passed in. > **Do not use this for `--authkey`.** > Use the `tailscale_authkey` variable instead. > -> **If authenticating with an OAuth key, do not use this for `--advertise-tags`.** -> Use the `tailscale_oauth_tags` variable instead. -> `--advertise-tags` may be used in this variable if you are not authenticating to Tailscale with OAuth. +> **Do not use this for `--advertise-tags`.** +> Use the `tailscale_tags` variable instead. +> +> **Do not use this for `--timeout`.** +> Use the `tailscale_up_timeout` 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. @@ -202,6 +209,16 @@ 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_up_timeout + +**Default**: `120s` + +Defines the timeout duration for the `tailscale up` command. + +> --timeout duration +> +> maximum amount of time to wait for tailscaled to enter a Running state + ### verbose **Default**: `false` @@ -276,7 +293,8 @@ Connect using an OAuth client secret: vars: verbose: true tailscale_authkey: "{{ lookup('env', 'TAILSCALE_OAUTH_CLIENT_SECRET') }}" - tailscale_oauth_tags: "tag:oauth" + tailscale_tags: + - "oauth" # Optionally, also include: tailscale_oauth_ephemeral: true tailscale_oauth_preauthorized: false diff --git a/defaults/main.yml b/defaults/main.yml index 41dbde6a..18d65869 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -7,14 +7,16 @@ state: latest tailscale_authkey: "" # Optional command-line arguments for 'tailscale up' tailscale_args: "" +# Apply provided tags to node +tailscale_tags: [] +# Set timeout for 'tailscale up' command +tailscale_up_timeout: "120s" # Used for OAuth authentication # Register as an ephemeral node (recommended) tailscale_oauth_ephemeral: true # 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 diff --git a/molecule/oauth/converge.yml b/molecule/oauth/converge.yml index a85078dd..f1f87be6 100644 --- a/molecule/oauth/converge.yml +++ b/molecule/oauth/converge.yml @@ -10,4 +10,5 @@ name: artis3n.tailscale vars: verbose: true - tailscale_oauth_tags: "tag:ci-worker" + tailscale_tags: + - "ci-worker" diff --git a/tasks/install.yml b/tasks/install.yml index df3b2a0b..2a081358 100644 --- a/tasks/install.yml +++ b/tasks/install.yml @@ -76,17 +76,12 @@ msg: "Ver: {{ tailscale_version }} Online: {{ tailscale_is_online }}" when: verbose -- name: Install | Save State - ansible.builtin.template: - src: state.j2 - dest: "{{ tailscale_state_folder }}/artis3n-tailscale/state" - owner: "{{ ansible_user_uid }}" - group: "{{ ansible_user_gid }}" - mode: '0644' - register: state_file +- name: Install | Prepend 'tag:' to each item in the list + ansible.builtin.set_fact: + tailscale_prepared_tags: "{{ tailscale_tags | map('regex_replace', '^', 'tag:') | list }}" # OAuth key starts with 'tskey-client-', auth key starts with 'tskey-auth-', with headscale it can be 'unused' -- name: Install | Create authkey string +- name: Install | Build `tailscale up` arguments strings ansible.builtin.set_fact: tailscale_authkey_type: >- {# Check if the key is an OAuth key #} @@ -100,11 +95,15 @@ 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 }} --advertise-tags={{ tailscale_oauth_tags | trim }} + {{ 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 %} + tailscale_tags_string: >- + {% if tailscale_tags | length > 0 %} + --advertise-tags={{ tailscale_prepared_tags | join(',') }} + {% endif %} no_log: "{{ not (insecurely_log_authkey | bool) }}" - name: Install | Authkey Type @@ -112,9 +111,27 @@ msg: "{{ tailscale_authkey_type | trim }}" when: verbose +- name: Install | Build the final tailscale_args + ansible.builtin.set_fact: + tailscale_args_string: "{{ tailscale_args }} {{ tailscale_tags_string | trim }} --timeout={{ tailscale_up_timeout | trim }}" + +- name: Install | Final `tailscale up` arguments string + ansible.builtin.debug: + msg: "{{ tailscale_args_string | trim }}" + when: verbose + +- name: Install | Save State + ansible.builtin.template: + src: state.j2 + dest: "{{ tailscale_state_folder }}/artis3n-tailscale/state" + owner: "{{ ansible_user_uid }}" + group: "{{ ansible_user_gid }}" + mode: '0644' + register: state_file + - 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_string | 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 diff --git a/tasks/main.yml b/tasks/main.yml index 96141b9b..64b8f5ef 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -18,6 +18,24 @@ - not tailscale_up_skip - state != "absent" +- name: Tailscale Tags Required with OAuth Key + ansible.builtin.fail: + msg: "When `tailscale_authkey` is an OAuth key, you must supply one or more tags in `tailscale_tags`." + when: + - tailscale_authkey.startswith('tskey-client-') + - not tailscale_tags + - state != "absent" + - not tailscale_up_skip + +- name: Use tailscale_tags instead of tailscale_args for tags + ansible.builtin.debug: + msg: You must use `tailscale_tags` instead of `tailscale_args` to assign tags. + when: + - '"--advertise-tags" in tailscale_args' + - not tailscale_tags + - state != "absent" + - not tailscale_up_skip + - name: Skipping Authentication ansible.builtin.debug: msg: You have set 'tailscale_up_skip', so this node will not authenticate to your Tailscale network. diff --git a/tasks/templates/state.j2 b/tasks/templates/state.j2 index c2dcfb4e..5129fb6d 100644 --- a/tasks/templates/state.j2 +++ b/tasks/templates/state.j2 @@ -1,5 +1,5 @@ {%- if auth_key_in_state -%} -{{ (tailscale_args + ' --authkey=' + tailscale_authkey) | trim | hash('sha256') }} +{{ (tailscale_args_string + ' --authkey=' + tailscale_authkey) | trim | hash('sha256') }} {% else %} -{{ tailscale_args | trim | hash('sha256') }} +{{ tailscale_args_string | trim | hash('sha256') }} {%- endif -%}