From c03c2b5a3f9c40f01a60a57b69e4195874790f51 Mon Sep 17 00:00:00 2001 From: Marcus Meurs Date: Mon, 3 Dec 2018 07:05:37 +0100 Subject: [PATCH 1/8] Set custom facts persistently Create /etc/ansible/facts.d/zerotier.fact on each node containing custom facts in json format. This can then be used to prevent pointless reconfiguration of existing nodes whenever a new one is added to the inventory. In this commit it merely skips the installation tasks. --- files/set_facts.sh | 35 +++++++++++++++++++++++++++++++++++ tasks/authorize_node.yml | 21 ++------------------- tasks/main.yml | 10 ++++++++++ 3 files changed, 47 insertions(+), 19 deletions(-) create mode 100644 files/set_facts.sh diff --git a/files/set_facts.sh b/files/set_facts.sh new file mode 100644 index 0000000..ffbef4c --- /dev/null +++ b/files/set_facts.sh @@ -0,0 +1,35 @@ +#!/bin/bash +FACTS_DIR='/etc/ansible/facts.d' +FACT_FILE="${FACTS_DIR}/zerotier.fact" +NODE_STATUS=($(zerotier-cli status)) +NETWORKS=$(zerotier-cli listnetworks | tail -n+2) + +function file_content { + if [ ! -z "$NETWORKS" ]; then + echo "{" + echo " \"node_id\":\"${NODE_STATUS[2]}\"," + echo " \"networks\": [" + while read -r; do + network=($REPLY) + echo " {" + echo " \"id\":\"${network[2]}\"," + echo " \"status\":\"${network[5]}\"" + echo " }" + done <<< $NETWORKS + echo " ]" + echo "}" + else + echo "{\"node_id\":\"${NODE_STATUS[2]}\"}" + fi +} + +if [ ! -d "$FACTS_DIR" ]; then + mkdir -p $FACTS_DIR +fi + +file_content > $FACT_FILE + + +# TO-DO +# Consider something that hadles JSON better than Bash does +# The above will fail when it runs in to more than 1 network diff --git a/tasks/authorize_node.yml b/tasks/authorize_node.yml index e291655..a4cd763 100644 --- a/tasks/authorize_node.yml +++ b/tasks/authorize_node.yml @@ -1,24 +1,8 @@ --- -- block: - - name: Get Zerotier NodeID - shell: zerotier-cli info | awk '{print $3}' - register: nodeid - changed_when: false - - - name: Set NodeID as fact - set_fact: - zerotier_node_id: "{{ nodeid.stdout }}" - - when: - - zerotier_accesstoken is defined - - not ansible_check_mode - tags: - - configuration - - block: - name: Authorize members to network uri: - url: "{{ zerotier_api_url }}/api/network/{{ zerotier_network_id }}/member/{{ zerotier_node_id }}" + url: "{{ zerotier_api_url }}/api/network/{{ zerotier_network_id }}/member/{{ ansible_local.zerotier.node_id }}" method: POST headers: Authorization: bearer {{ zerotier_accesstoken }} @@ -32,7 +16,7 @@ - name: Configure members in network uri: - url: "{{ zerotier_api_url }}/api/network/{{ zerotier_network_id }}/member/{{ zerotier_node_id }}" + url: "{{ zerotier_api_url }}/api/network/{{ zerotier_network_id }}/member/{{ ansible_local.zerotier.node_id }}" method: POST headers: Authorization: bearer {{ zerotier_accesstoken }} @@ -46,7 +30,6 @@ delegate_to: "{{ zerotier_api_delegate }}" when: - - zerotier_accesstoken is defined - not ansible_check_mode tags: - configuration diff --git a/tasks/main.yml b/tasks/main.yml index 0b2aa25..a498ec8 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -3,10 +3,20 @@ - import_tasks: install.yml when: - not skip_install|default(false)|bool + - ansible_local.zerotier is not defined + +- block: + - name: Update ansible_local facts + script: set_facts.sh + + - name: Re-gather facts + setup: ~ + - import_tasks: authorize_node.yml when: - zerotier_accesstoken is defined + - ansible_local.zerotier.node_id is defined - import_tasks: join_network.yml when: From 672c67e087fd2a4da6e262bbe5db956c6666b382 Mon Sep 17 00:00:00 2001 From: Marcus Meurs Date: Tue, 4 Dec 2018 01:06:35 +0100 Subject: [PATCH 2/8] Reduce unnecessary API calls The role will no longer make API calls to authorize already authorized members to a network. --- files/set_facts.sh | 13 ++++++------- tasks/authorize_node.yml | 4 +++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/files/set_facts.sh b/files/set_facts.sh index ffbef4c..fed1f2f 100644 --- a/files/set_facts.sh +++ b/files/set_facts.sh @@ -8,18 +8,17 @@ function file_content { if [ ! -z "$NETWORKS" ]; then echo "{" echo " \"node_id\":\"${NODE_STATUS[2]}\"," - echo " \"networks\": [" + echo " \"networks\": {" while read -r; do network=($REPLY) - echo " {" - echo " \"id\":\"${network[2]}\"," - echo " \"status\":\"${network[5]}\"" - echo " }" + echo " \"${network[2]}\": {" + echo " \"status\":\"${network[5]}\"" + echo " }" done <<< $NETWORKS - echo " ]" + echo " }" echo "}" else - echo "{\"node_id\":\"${NODE_STATUS[2]}\"}" + echo "{\"node_id\":\"${NODE_STATUS[2]}\",\"networks\":{}}" fi } diff --git a/tasks/authorize_node.yml b/tasks/authorize_node.yml index a4cd763..d114e25 100644 --- a/tasks/authorize_node.yml +++ b/tasks/authorize_node.yml @@ -1,6 +1,6 @@ --- - block: - - name: Authorize members to network + - name: Authorize new members to network uri: url: "{{ zerotier_api_url }}/api/network/{{ zerotier_network_id }}/member/{{ ansible_local.zerotier.node_id }}" method: POST @@ -13,6 +13,8 @@ body_format: json register: auth_apiresult delegate_to: "{{ zerotier_api_delegate }}" + when: + - ansible_local.zerotier.networks[zerotier_network_id] is not defined or ansible_local.zerotier.networks[zerotier_network_id].status != 'OK' - name: Configure members in network uri: From 046415b1686f9a744c581d2de2363e0b1f03ba1b Mon Sep 17 00:00:00 2001 From: Marcus Meurs Date: Tue, 4 Dec 2018 02:48:29 +0100 Subject: [PATCH 3/8] Fix for loop generating invalid json --- files/set_facts.sh | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/files/set_facts.sh b/files/set_facts.sh index fed1f2f..bc895d4 100644 --- a/files/set_facts.sh +++ b/files/set_facts.sh @@ -6,6 +6,9 @@ NETWORKS=$(zerotier-cli listnetworks | tail -n+2) function file_content { if [ ! -z "$NETWORKS" ]; then + network_count=$(echo $NETWORKS |wc -l) + counter=1 + echo "{" echo " \"node_id\":\"${NODE_STATUS[2]}\"," echo " \"networks\": {" @@ -13,7 +16,13 @@ function file_content { network=($REPLY) echo " \"${network[2]}\": {" echo " \"status\":\"${network[5]}\"" - echo " }" + + if [ "$counter" -eq "$network_count" ]; then + echo " }" + else + echo " }," + fi + ((counter++)) done <<< $NETWORKS echo " }" echo "}" @@ -31,4 +40,3 @@ file_content > $FACT_FILE # TO-DO # Consider something that hadles JSON better than Bash does -# The above will fail when it runs in to more than 1 network From 4dac8000fcfac782cc97b44021df163459be0d0e Mon Sep 17 00:00:00 2001 From: Marcus Meurs Date: Tue, 4 Dec 2018 05:00:38 +0100 Subject: [PATCH 4/8] Variable naming convention Updated 2 variable names to follow naming convention. For backwards compatibility the old names are rewritten to the new ones in the role's defaults. --- README.md | 64 +++++++++++++++++++++++++--------------- defaults/main.yml | 3 +- tasks/authorize_node.yml | 6 ++-- tasks/main.yml | 2 +- 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index e04c1e1..2e5fc74 100644 --- a/README.md +++ b/README.md @@ -3,38 +3,54 @@ ZeroTier ========= -This Ansible role installs the `zerotier-one` package, adds and authorizes new members to (existing) ZeroTier networks, and tells the new member to join the network. +This Ansible role adds the ZeroTier repository and installs the `zerotier-one` package using your system's package manager. Depending on the provided variables this role can also add and authorize new members to (existing) ZeroTier networks, and tell the new member to join the network. Requirements ------------ -This role has an optional access token variable to authorize the member using the ZeroTier API. The role also takes the ID of the ZeroTier network to automatically join the new member. +Technically this role has no requirements. If it's ran without any variables set it will only run the installation tasks. The following variables impact the role's behavior: -Role Variables --------------- - -### zerotier_api_url -The url where the Zerotier API lives. Must use HTTPS protocol. -Default: https://my.zerotier.com - -### zerotier_accesstoken -The access token needed to authorize with the ZeroTier API. You can generate one in your account settings at https://my.zerotier.com/. If this is left out then the newly joined member will not be automatically authorized. +[**zerotier_network_id**](#zerotier_network_id): when set hosts are told to join this network. +[**zerotier_api_accesstoken**](#zerotier_api_accesstoken): when set the role can handle member authentication and configuration using the ZeroTier API. -### zerotier_network_id -The 16 character network ID of the network the new members should join. The node will not join any network if omitted. -### zerotier_register_short_hostname -Used to register the short hostname (without the FQDN) on the network instead of the long one. -Default: `false` - -### zerotier_member_ip_assignments -A list of IP addresses to assign this member. The member will be automatically assigned an address on the network if left out. - -### zerotier_member_description -Optional desription for a member. +Role Variables +-------------- -### zerotier_api_delegate -Option to delegate tasks for Zerotier API calls. By default the API calls are made from the machine running the role. +#### zerotier_network_id +*Type*: string +*Default value*: +*Description*: The 16 character network ID of the network the new members should join. The node will not join any network if omitted. + +#### zerotier_member_register_short_hostname +*Type*: boolean +*Default value*: `false` +*Description*: Used to register the short hostname (without the FQDN) on the network instead of the long one. + +#### zerotier_member_ip_assignments +*Type*: list +*Default value*: `[]` +*Description*: A list of IP addresses to assign this member. The member will be automatically assigned an address on the network if left out. + +#### zerotier_member_description +*Type*: string +*Default value*: +*Description*: Optional desription for a member. + +#### zerotier_api_accesstoken +*Type*: string +*Default value*: +*Description*: The access token needed to authorize with the ZeroTier API. You can generate one in your account settings at https://my.zerotier.com/. If this is left out then the newly joined member will not be automatically authorized. + +#### zerotier_api_url +*Type*: string +*Default value*: `https://my.zerotier.com` +*Description*: The url where the Zerotier API lives. Must use HTTPS protocol. + +#### zerotier_api_delegate +*Type*: string +*Default value*: `localhost` +*Description*: Option to delegate tasks for Zerotier API calls. By default the API calls are made from the machine running the role. Example Playbook ---------------- diff --git a/defaults/main.yml b/defaults/main.yml index 64c3ab0..ae8b479 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,7 +1,8 @@ --- # defaults file for ansible-role-zerotier +zerotier_api_accesstoken: "{{ zerotier_accesstoken | default() }}" # For backwards compatibility zerotier_api_url: https://my.zerotier.com zerotier_api_delegate: localhost zerotier_apt_state: present -zerotier_register_short_hostname: false +zerotier_member_register_short_hostname: "{{ zerotier_register_short_hostname | default(false) }}" # For backwards compatibility zerotier_authorize_member: true diff --git a/tasks/authorize_node.yml b/tasks/authorize_node.yml index d114e25..62c7308 100644 --- a/tasks/authorize_node.yml +++ b/tasks/authorize_node.yml @@ -5,7 +5,7 @@ url: "{{ zerotier_api_url }}/api/network/{{ zerotier_network_id }}/member/{{ ansible_local.zerotier.node_id }}" method: POST headers: - Authorization: bearer {{ zerotier_accesstoken }} + Authorization: bearer {{ zerotier_api_accesstoken }} body: hidden: false config: @@ -21,9 +21,9 @@ url: "{{ zerotier_api_url }}/api/network/{{ zerotier_network_id }}/member/{{ ansible_local.zerotier.node_id }}" method: POST headers: - Authorization: bearer {{ zerotier_accesstoken }} + Authorization: bearer {{ zerotier_api_accesstoken }} body: - name: "{{ zerotier_register_short_hostname | ternary(inventory_hostname_short, inventory_hostname) }}" + name: "{{ zerotier_member_register_short_hostname | ternary(inventory_hostname_short, inventory_hostname) }}" description: "{{ zerotier_member_description | default() }}" config: ipAssignments: "{{ zerotier_member_ip_assignments | default([]) | list }}" diff --git a/tasks/main.yml b/tasks/main.yml index a498ec8..25f1c62 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -15,7 +15,7 @@ - import_tasks: authorize_node.yml when: - - zerotier_accesstoken is defined + - zerotier_api_accesstoken | length > 0 - ansible_local.zerotier.node_id is defined - import_tasks: join_network.yml From fd68894bad84ffe584a92f442dd6c0f248df9abc Mon Sep 17 00:00:00 2001 From: Marcus Meurs Date: Thu, 6 Dec 2018 05:35:32 +0100 Subject: [PATCH 5/8] Possible fix for #26 If ansible_distribution is Ubuntu the role will check if there is a dedicated repo matching the ansible_distribution_release. If this is not the case but the ansible_distribution_major_version is 18, the repo for the bionic release will be used. --- tasks/install/Debian.yml | 23 ++++++++++++++++++++++- vars/main.yml | 1 + 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/tasks/install/Debian.yml b/tasks/install/Debian.yml index 8c0ff69..daf9b2e 100644 --- a/tasks/install/Debian.yml +++ b/tasks/install/Debian.yml @@ -2,8 +2,29 @@ apt_key: url: "{{ zerotier_gpg_url }}" +- name: Check if Ubuntu release has dedicated repo + uri: + url: "{{ zerotier_download_base_url }}/debian/{{ zerotier_deb_release_repo }}" + failed_when: false + when: + - ansible_facts['distribution'] == "Ubuntu" + register: release_repo + +- block: + - name: Overwrite Ubuntu release repo name + set_fact: + zerotier_deb_release_repo: bionic + + - name: Re-gather facts + setup: ~ + + when: + - release_repo.status == 404 + - ansible_facts['distribution'] == "Ubuntu" + - ansible_facts['distribution_major_version'] == "18" + - name: Add ZeroTier APT repository apt_repository: - repo: deb {{ zerotier_download_base_url }}/debian/{{ ansible_distribution_release }} {{ ansible_distribution_release }} main + repo: deb {{ zerotier_download_base_url }}/debian/{{ zerotier_deb_release_repo }} {{ zerotier_deb_release_repo }} main filename: zerotier register: zerotier_repo diff --git a/vars/main.yml b/vars/main.yml index e91184b..d83c2e2 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -1,4 +1,5 @@ --- # vars file for ansible-role-zerotier zerotier_download_base_url: http://download.zerotier.com +zerotier_deb_release_repo: "{{ ansible_facts['distribution_release'] }}" zerotier_gpg_url: https://download.zerotier.com/contact@zerotier.com.gpg From b86bc4c73dbfbb4f64378cea36935042ecab94ca Mon Sep 17 00:00:00 2001 From: Marcus Meurs Date: Fri, 7 Dec 2018 23:35:32 +0100 Subject: [PATCH 6/8] Rearrange conditionals release_repo.status needs to be checked after distribution. --- tasks/install/Debian.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/install/Debian.yml b/tasks/install/Debian.yml index daf9b2e..cf3636e 100644 --- a/tasks/install/Debian.yml +++ b/tasks/install/Debian.yml @@ -19,9 +19,9 @@ setup: ~ when: - - release_repo.status == 404 - ansible_facts['distribution'] == "Ubuntu" - ansible_facts['distribution_major_version'] == "18" + - release_repo.status == 404 - name: Add ZeroTier APT repository apt_repository: From 95d9213746890a6ebed41c180789e3d2e2909448 Mon Sep 17 00:00:00 2001 From: Marcus Meurs Date: Fri, 7 Dec 2018 23:40:48 +0100 Subject: [PATCH 7/8] Adds network device to ansible_local.zerotier Also removes the conditional for skipping installation based on ansible_local.zerotier value. Skipping tasks like that should be up to the user, not the role. --- files/set_facts.sh | 3 ++- tasks/main.yml | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/files/set_facts.sh b/files/set_facts.sh index bc895d4..1586e30 100644 --- a/files/set_facts.sh +++ b/files/set_facts.sh @@ -16,6 +16,7 @@ function file_content { network=($REPLY) echo " \"${network[2]}\": {" echo " \"status\":\"${network[5]}\"" + echo " \"device\":\"${network[7]}\"" if [ "$counter" -eq "$network_count" ]; then echo " }" @@ -39,4 +40,4 @@ file_content > $FACT_FILE # TO-DO -# Consider something that hadles JSON better than Bash does +# Handle different states than "OK". Other statuses can mess up positions. diff --git a/tasks/main.yml b/tasks/main.yml index 25f1c62..95fb317 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -3,7 +3,6 @@ - import_tasks: install.yml when: - not skip_install|default(false)|bool - - ansible_local.zerotier is not defined - block: - name: Update ansible_local facts From fd555f86ac4e9eac0dfc1c4cf146f622def966f9 Mon Sep 17 00:00:00 2001 From: Marcus Meurs Date: Fri, 7 Dec 2018 23:43:27 +0100 Subject: [PATCH 8/8] Update Galaxy meta and readme Readme mostly formatting changes. Meta file now includes supported Ubuntu releases. --- README.md | 22 +++++++++++----------- meta/main.yml | 6 +++++- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 2e5fc74..6f73300 100644 --- a/README.md +++ b/README.md @@ -17,40 +17,40 @@ Technically this role has no requirements. If it's ran without any variables set Role Variables -------------- -#### zerotier_network_id +### zerotier_network_id *Type*: string *Default value*: *Description*: The 16 character network ID of the network the new members should join. The node will not join any network if omitted. -#### zerotier_member_register_short_hostname +### zerotier_member_register_short_hostname *Type*: boolean *Default value*: `false` -*Description*: Used to register the short hostname (without the FQDN) on the network instead of the long one. +*Description*: By default `inventory_hostname` will be used to name a member in a network. If set to `true`, `inventory_hostname_short` will be used instead. -#### zerotier_member_ip_assignments +### zerotier_member_ip_assignments *Type*: list *Default value*: `[]` *Description*: A list of IP addresses to assign this member. The member will be automatically assigned an address on the network if left out. -#### zerotier_member_description +### zerotier_member_description *Type*: string -*Default value*: +*Default value*: `""` *Description*: Optional desription for a member. -#### zerotier_api_accesstoken +### zerotier_api_accesstoken *Type*: string -*Default value*: +*Default value*: `""` *Description*: The access token needed to authorize with the ZeroTier API. You can generate one in your account settings at https://my.zerotier.com/. If this is left out then the newly joined member will not be automatically authorized. -#### zerotier_api_url +### zerotier_api_url *Type*: string *Default value*: `https://my.zerotier.com` *Description*: The url where the Zerotier API lives. Must use HTTPS protocol. -#### zerotier_api_delegate +### zerotier_api_delegate *Type*: string *Default value*: `localhost` -*Description*: Option to delegate tasks for Zerotier API calls. By default the API calls are made from the machine running the role. +*Description*: Option to delegate tasks for Zerotier API calls. This is usefull in a situation where API calls can only be made from a whitelisted management server, for example. Example Playbook ---------------- diff --git a/meta/main.yml b/meta/main.yml index b424db0..f589906 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -27,7 +27,7 @@ galaxy_info: # this branch. If Travis integration is configured, only notifications for this # branch will be accepted. Otherwise, in all cases, the repo's default branch # (usually master) will be used. - #github_branch: + github_branch: master # # platforms is a list of platforms, and each platform has a name and a list of versions. @@ -39,6 +39,10 @@ galaxy_info: - name: Debian versions: - stretch + - name: Ubuntu + versions: + - Bionic + - Cosmic - name: Fedora versions: - 28