From 21ea69e37cd4ba07580085f80f007045fc360ac6 Mon Sep 17 00:00:00 2001 From: Martin Loy Date: Tue, 30 Jan 2018 11:39:22 -0300 Subject: [PATCH 01/11] use a group instead of hostname --- site.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/site.yml b/site.yml index f2b27a5..0873cd5 100644 --- a/site.yml +++ b/site.yml @@ -3,7 +3,6 @@ roles: - conduit -- hosts: jumphost.example.com +- hosts: ssh_host roles: - sshhost - From dd58b7973fc099ed978924e366bb7b2de087fc12 Mon Sep 17 00:00:00 2001 From: Martin Loy Date: Tue, 30 Jan 2018 19:05:44 -0300 Subject: [PATCH 02/11] we dont want to support this anymore --- roles/sshhost/files/authorized_keys | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 roles/sshhost/files/authorized_keys diff --git a/roles/sshhost/files/authorized_keys b/roles/sshhost/files/authorized_keys deleted file mode 100644 index 2b8e18f..0000000 --- a/roles/sshhost/files/authorized_keys +++ /dev/null @@ -1,4 +0,0 @@ -ssh-dsa AAAAB3NzaC1kc3MAAACBAMUmf8ZU7QeZ/HK9D9ILPFkROcqbNIDryfe/Z/f5O2H5oSu729oS6cHqjnxJUgjTAKzkyfWC9fgWIh4XonoWGGoI6IRufMDR5ZouJR2/wldO7gsKp0LRdeCUp71Vf0Z3cu9+Do0DXZXEIphrKnsWpXwEF1wA5+/S7bxFL065dwlJAAAAFQDLB1Hob89vlyFKbiG3lsGygpUTdwAAAIBFlmG92tFqWaal62lAkPEO+2YMrC9iS9Y97gZkWaArPCsBTwKuDQm+7WDqTY1/iOqA9WHKl+3Ct02Oy+QGPYh+fGcEhEu1buLMyW8t4lfjQp8/ZrXZhezpvdJnqXvRzYnYrB4ry7EBWenYCn9YuCDJkmjcWwkwh5AzWh/3kAMY7AAAAIEAvQvmtkjDf8nciH9YfCEEBQKREPfnhS1Z31RBEd8hwnOLmsazRp9Pb3s5I0DyTEZRFfPOJ1Hj5Y+oNji3N8T+OHAHvESaTIwIOfkJsGVxad4MlGgFfcH6GPv+eNQDzwHJHmpqQLb6ptLSCQRz4m5YujwHberkzj4S5uKIATgjx4k= ttn-ith-test -ssh-ecdsa AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBG5dANSc7cFD4J14P68HDJjD/t7AWOWMURaZVLRJ4l+1dtIQ8Kf4o2cL3PeaZkk80DXP/43lSg6ddQhhoz93N9c= ttn-ith-test -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGTLB90gQyl0sDRbrmVyddLEukJdAkcd4pEJQG4cOb9V ttn-ith-test -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCjy8iezeWkHQNRm5mmnr9BFWw6xztGhHJMt6p3OFrhxYDdDx4YeCt7Mtw9WNBGq7b1f5gT5wfqmEi3tbxYOECVxmFiBNqirWrgRuknycmorqWOEh4MgKyzd3/1EFTQYmalFIarEHNFwBSL6W5iDeycMIgRtHfada5OD0gw2IJSu6cck6r2m09JG0H+DaxlWAv/PFZT0FiiL9jf2pfEuWX5QpBNH1PxdwBodAaIE9YAmcyQIJTDWCqyCDp2jD8iKpQIZhszayQWjI5VPIA+sselLCWUWTiPv3/lxiIoHPSC0Eqn7+IyTHyNWtj3i2+DXnbgjUApjMZ2+8mWXlq7Ts3l ttn-ith-test From 0bd0bff45049124d8fef76cb84dc0f460327791c Mon Sep 17 00:00:00 2001 From: Martin Loy Date: Tue, 30 Jan 2018 22:20:25 -0300 Subject: [PATCH 03/11] query the ansible inventory --- roles/sshhost/tasks/main.yml | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/roles/sshhost/tasks/main.yml b/roles/sshhost/tasks/main.yml index 3e191d0..4e2204d 100644 --- a/roles/sshhost/tasks/main.yml +++ b/roles/sshhost/tasks/main.yml @@ -25,20 +25,14 @@ group: "{{ ssh_tunnel_gateway_user_on_jumphost }}" tags: users -- name: Create authorized keys - local_action: - module: shell - args: "( cat roles/conduit/files/authorized_keys; bin/gen_authorized_keys ) > {{ role_path }}/files/authorized_keys" - tags: users - -# Using a template? - name: "Install {{ ssh_tunnel_gateway_user_on_jumphost }} authorized keys" - copy: - dest: "~{{ ssh_tunnel_gateway_user_on_jumphost }}/.ssh/authorized_keys" - src: authorized_keys - mode: "0600" - owner: "{{ ssh_tunnel_gateway_user_on_jumphost }}" - group: "{{ ssh_tunnel_gateway_user_on_jumphost }}" + authorized_key: + user: "{{ ssh_tunnel_gateway_user_on_jumphost }}" + state: present + key: '{{ item }}' + with_items: + - "{{ authorized_keys }}" + - "{% for lora_gw in groups['conduits'] %}{% if 'ansible_ssh_host_key_rsa_public' in hostvars[lora_gw] %}ssh-rsa {{ hostvars[lora_gw]['ansible_ssh_host_key_rsa_public'] }} {{ hostvars[lora_gw]['ansible_hostname'] }}{% endif %}{% endfor %}" tags: users - name: Only allow gateway ports from localhost @@ -48,5 +42,5 @@ state=present notify: Restart ssh tags: sshd - + # tasks file for sshhost From a6f31bc3e1a1c321a6d4d86f29eaac55df57c85c Mon Sep 17 00:00:00 2001 From: Martin Loy Date: Wed, 31 Jan 2018 01:56:21 -0300 Subject: [PATCH 04/11] bastion authorized_keys should be global authorized_keys + lora gw rsa pubkeys --- roles/sshhost/tasks/main.yml | 10 ++-------- roles/sshhost/vars/main.yml | 6 +++++- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/roles/sshhost/tasks/main.yml b/roles/sshhost/tasks/main.yml index 4e2204d..160a6a0 100644 --- a/roles/sshhost/tasks/main.yml +++ b/roles/sshhost/tasks/main.yml @@ -1,9 +1,5 @@ --- # -# XXX - Need to do this as said user -# XXX - Probably not necessary to create user -# XXX - local_action to run script? -# # Create ssh user on jumphost. Note that the MCCI port-mapping # numbering scheme is not really handled here, so this will # usually be done by a separate script. @@ -16,7 +12,7 @@ password: "*" tags: users -- name: Create {{ ssh_tunnel_gateway_user_on_jumphost }} .ssh dir +- name: Create {{ ssh_tunnel_gateway_user_on_jumphost }} .ssh dir file: dest: "~{{ ssh_tunnel_gateway_user_on_jumphost }}/.ssh" state: directory @@ -30,9 +26,7 @@ user: "{{ ssh_tunnel_gateway_user_on_jumphost }}" state: present key: '{{ item }}' - with_items: - - "{{ authorized_keys }}" - - "{% for lora_gw in groups['conduits'] %}{% if 'ansible_ssh_host_key_rsa_public' in hostvars[lora_gw] %}ssh-rsa {{ hostvars[lora_gw]['ansible_ssh_host_key_rsa_public'] }} {{ hostvars[lora_gw]['ansible_hostname'] }}{% endif %}{% endfor %}" + with_flattened: "{{ authorized_keys_list }}" tags: users - name: Only allow gateway ports from localhost diff --git a/roles/sshhost/vars/main.yml b/roles/sshhost/vars/main.yml index 3815d64..1c12265 100644 --- a/roles/sshhost/vars/main.yml +++ b/roles/sshhost/vars/main.yml @@ -1,2 +1,6 @@ --- -# vars file for sshhost \ No newline at end of file +# Accumulate list of authorized keys + all lora gw public keys +authorized_keys_list: + - "{{ authorized_keys }}" + # public rsa of each lora gw + - "{% for lora_gw in groups['conduits'] %}{% if 'ansible_ssh_host_key_rsa_public' in hostvars[lora_gw] %}ssh-rsa {{ hostvars[lora_gw]['ansible_ssh_host_key_rsa_public'] }} {{ hostvars[lora_gw]['ansible_hostname'] }}{% endif %}{% endfor %}" From ebf98e98dbccd0bea71d3b1181ec2ed2ec533aab Mon Sep 17 00:00:00 2001 From: Martin Loy Date: Wed, 31 Jan 2018 01:57:09 -0300 Subject: [PATCH 05/11] lora gw authorized_keys should only be global authorized_keys and not all keys --- roles/conduit/tasks/users.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/conduit/tasks/users.yml b/roles/conduit/tasks/users.yml index 606399d..0faa26f 100644 --- a/roles/conduit/tasks/users.yml +++ b/roles/conduit/tasks/users.yml @@ -43,7 +43,7 @@ path: /var/config/home/root/.ssh/authorized_keys user: root key: "{{ item }}" - with_flattened: "{{ authorized_keys_list }}" + with_flattened: "{{ authorized_keys }}" - name: Remove ~root/.ssh if it is a dir file: @@ -58,7 +58,7 @@ state: link src: /var/config/home/root/.ssh force: yes - + # # Create ttn user # @@ -101,7 +101,7 @@ path: /var/config/home/{{ ttn_user }}/.ssh/authorized_keys user: root key: "{{ item }}" - with_flattened: "{{ authorized_keys_list }}" + with_flattened: "{{ authorized_keys }}" - name: "Remove ~{{ ttn_user }}/.ssh" file: From 0ca1c39121a97865ca70ed85c93ddc5970b7c272 Mon Sep 17 00:00:00 2001 From: Martin Loy Date: Wed, 31 Jan 2018 01:58:39 -0300 Subject: [PATCH 06/11] this doesnt belong here anymore --- roles/conduit/vars/main.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/roles/conduit/vars/main.yml b/roles/conduit/vars/main.yml index e7cebcd..ffd3ece 100644 --- a/roles/conduit/vars/main.yml +++ b/roles/conduit/vars/main.yml @@ -23,8 +23,3 @@ ssh_tunnel_daemon_args: "-f -M {{ ssh_tunnel_keepalive_base_port }} -o ServerAli # Is the GPS present? have_gps: "{{ gps_device in ansible_local.dev }}" - -# Accumulate list of authorized keys -authorized_keys_list: - - "{{ authorized_keys }}" - - "{% for group in group_names %}{% if 'authorized_keys_' ~ group in vars %}{{ vars['authorized_keys_' ~ group] }}{% endif %}{% endfor %}" From d2fc9247de0251a5c3eddc987188373723be8806 Mon Sep 17 00:00:00 2001 From: Martin Loy Date: Wed, 31 Jan 2018 02:06:30 -0300 Subject: [PATCH 07/11] tweak docs --- roles/conduit/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/roles/conduit/README.md b/roles/conduit/README.md index 439269b..5d10686 100644 --- a/roles/conduit/README.md +++ b/roles/conduit/README.md @@ -15,7 +15,7 @@ The following setup must be set up performed on the Contduit: + python-distutils + Install authorized keys + This is required to allow secure login to the gateway - + These are maintained in `authorized_keys` or `authorized_keys_GROUP` + + These are maintained in `authorized_keys` + Configure ssh tunnel + If accessing the Conduit remotely and it is not availble on the public Internet (and it should not be), an ssh tunnel needs to @@ -104,7 +104,7 @@ The following tags can be used to run a subset of the playbook.
Registers gateway and sets up /var/config/lora and the necessary config files
ca-certificates
Installs additional certificate authoritiy certificates for validating secure connections - ssh_tunnel +
ssh_tunnel
Sets up an ssh tunnel back to a control host
@@ -117,4 +117,3 @@ Author Information ------------------ Jeffrey Honig - From 5d5e90a922217f0b441cfd6b295a79f0130ffcf2 Mon Sep 17 00:00:00 2001 From: Martin Loy Date: Wed, 31 Jan 2018 11:43:15 -0300 Subject: [PATCH 08/11] improve docs++ --- README.md | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 4338839..91199b5 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This repo contains Ansible playbooks and configuration used to manage a group of Multi-Tech Conduits as [Things Network gateways](http://www.thethingsnetwork.org) in an a Things Network -organization. + The [MultiConnect® Conduit™](http://www.multitech.com/brands/multiconnect-conduit) is one of the more popular [LoRa®](http://lora.multitech.com/) Gateways is use. @@ -35,7 +35,7 @@ private keys on cloud hosts. This configuration relies on a *jump host* or ssh tunnel host. For various reasons, including security and the complexity of traversing firewalls, each conduit will set up a reverse SSH tunnel to a jump -host. +host. It is recommended that these ports only be accessible from that jump host. That will mean you need to be logged into the jump host to run @@ -52,7 +52,12 @@ If you do not want to use a jump host, comment out *ssh_tunnel_remote_port* or set it to *0* in your conduit's config file in *host_vars*. -## Branches +The ssh_host playbook will add the SSH RSA public keys of each device to `ssh_tunnel_gateway_user_on_jumphost` authorized_keys, in order to do this it relays on ansible fact caching, so make sure to run the conduits playbook first or increase your `fact_caching_timeout` in ansible.cfg + +*NOTICE* that the jumphost playbook works only on a Ubuntu host. + + +## Branches This repo has a few main branches: @@ -99,7 +104,7 @@ Instructions for installing Ansible ## Fetch the upstream files There is *Makefile* in the root of this repo that can be used to fetch -files from upstream. +files from upstream. ### make all This command will fetch files that are required to run ansible on the @@ -139,7 +144,7 @@ forward keys from your laptop or desktop. 1. Edit *hosts* and change *jumphost.example.com* to the FQDN of your ssh tunnel server, aka jumphost. 2. Copy *group_vars/jumphost.example.com* to -*group_vars/FQDN_OF_YOUR_JUMPHOST.yam* and edit it as necessary. +*group_vars/FQDN_OF_YOUR_JUMPHOST.yml* and edit it as necessary. ## Add each of your gateways to *hosts* Normally you would put them in the *production* group. There is also @@ -186,7 +191,7 @@ local network. DHCP should also supply one or more nameservers. You can override this in *host_vars/**HOST**.yml* by uncommenting and setting the appropriate variable definitions. See the examples in -*host_vars/ttn-org-example.yml*. +*host_vars/ttn-org-example.yml*. Note that if you make a mistake you may render your Conduit unreachable except via the USB serial console. So double check the @@ -216,13 +221,13 @@ configuration, or turning your Conduit into a BotNet node. On the Conduit: ``` mtctd login: root -passwd: +passwd: root@mtcdt:~# passwd Enter new UNIX password: Retype new UNIX password: root@mtcdt:~# ``` -Remember the password you supplied above. +Remember the password you supplied above. ## Provide initial authorizied keys in .root/.ssh/authorized_keys The easy way to do this is to open *authorized_keys* with `gedit` on your host, then copy/paste @@ -256,7 +261,7 @@ $ make apply TAGS=loraconfig TARGET=*HOSTNAME* ``` Specify the name of your Conduit with *HOSTNAME*. If you leave that off, all Conduit's will be registered, or their registration will be -updated. +updated. # Upgrading mLinux It is possible to remotely upgrade to a specific version of mLinux @@ -333,7 +338,7 @@ The available variables are defined in the [conduit role README](roles/conduit/R --- -# Development +# Development This is a temporary section to track development on this repo. @@ -430,4 +435,3 @@ restricting root access. ### Bugs + [ ] Not owner of gateway - From dc784cba4a7f030d05866633d99f27ce1080c75d Mon Sep 17 00:00:00 2001 From: Martin Loy Date: Wed, 31 Jan 2018 11:46:48 -0300 Subject: [PATCH 09/11] we want every ssh key in authorized_keys to be able to run ansible in bastion --- roles/sshhost/tasks/main.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/roles/sshhost/tasks/main.yml b/roles/sshhost/tasks/main.yml index 160a6a0..bf8c335 100644 --- a/roles/sshhost/tasks/main.yml +++ b/roles/sshhost/tasks/main.yml @@ -28,6 +28,14 @@ key: '{{ item }}' with_flattened: "{{ authorized_keys_list }}" tags: users + +- name: "Install {{ ansible_user }} authorized keys" + authorized_key: + user: "{{ ssh_tunnel_gateway_user_on_jumphost }}" + state: present + key: '{{ item }}' + with_flattened: "{{ authorized_keys }}" + tags: users - name: Only allow gateway ports from localhost lineinfile: dest=/etc/ssh/sshd_config From ec1b2c7a041389aa3ffa84ae70ff71f88087abf4 Mon Sep 17 00:00:00 2001 From: Martin Loy Date: Wed, 31 Jan 2018 11:48:26 -0300 Subject: [PATCH 10/11] we dont need this anymore --- bin/catalog | 177 ---------------------------------------------------- 1 file changed, 177 deletions(-) delete mode 100755 bin/catalog diff --git a/bin/catalog b/bin/catalog deleted file mode 100755 index e38eed4..0000000 --- a/bin/catalog +++ /dev/null @@ -1,177 +0,0 @@ -#!/usr/bin/python - -from __future__ import print_function -import argparse -import json -import os -import sys - -""" -MIT License - -Copyright (c) 2017 Jeffrey C Honig - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -""" - -def print_vars(options, parser, nodes): - """Print the specified vars for each node""" - - for host in sorted(options.host if options.host else nodes.keys()): - node = nodes[host] - - for var in options.vars: - parts = var.split(".") - dp = node - for part in parts: - try: - dp = dp[part] - except KeyError as err: - parser.error("%s %s: Can not find %s" % (host, var, part)) - - print("%s.%s: %s" % (host, part, dp)) - -def print_all_vars_sub(options, parser, nodes, names=[]): - """Print all vars for this node""" - - for k, v in sorted(nodes.items()): - if type(v) == type({}): - print_all_vars_sub(options, parser, v, names + [ k ]) - continue - - if type(v) == type([]): - parts = [] - for v1 in v: - if type(v1) == type({}): - print_all_vars_sub(options, parser, v1, names + [ k ]) - continue - - if type(v1) == type(u''): - v1 = v1.decode() - - parts.append(str(v1)) - - if len(parts): - v = ", ".join(parts) - - if type(v) == type(u''): - v = v.decode() - - print("%s: %s" % (".".join(names + [ k ]), v)) - - -def print_all_vars(options, parser, nodes, names=[]): - """Print all the vars for each node""" - - for host in sorted(options.host if options.host else nodes.keys()): - node = nodes[host] - print_all_vars_sub(options, parser, node, [ host ] + names) - -def read_catalogs(options, parser): - """Read in all the catalog files (*.json in options.catalog)""" - - nodes = dict() - - catalog_dir = os.path.expanduser(options.catalog) - if not os.path.exists(catalog_dir): - parser.error("%s does not exist" % options.catalog) - if not os.path.exists(catalog_dir): - parser.error("%s is not a directory" % options.catalog) - for root, dir, files in os.walk(catalog_dir, topdown=False): - for name in files: - path = os.path.join(root, name) - if not path.endswith(".json"): - if options.debug: - print("DBG: Ignoring %s" % path) - continue - if options.debug: - print("DBG: Opending %s" % path) - try: - fp = open(path) - except OSError as err: - parser.error("Opening %s: %s" % (path, err)) - continue - - try: - node = json.load(fp) - except ValueError as err: - parser.error("Reading %s: %s" % (path, err)) - continue - - try: - ansible_facts = node['ansible_facts'] - host = ansible_facts['ansible_nodename'] - except NameError as err: - parser.error("Parsing %s: %s" % (path, err)) - continue - - nodes[host] = ansible_facts - - return nodes - -def main(): - """ Figure out what we should do """ - - parser = argparse.ArgumentParser(description="Register or re-regiter a Conduit with TTN") - - # Debugging - group = parser.add_argument_group("Debugging options") - group.add_argument("-d", "--debug", - dest="debug", default=False, - action='store_true', - help="print debugging messages") - group.add_argument("--nodebug", - dest="debug", - action='store_false', - help="print debugging messages") - group.add_argument("-v", "--verbose", - dest="verbose", default=False, - action='store_true', - help="print verbose messages") - - # Options - group = parser.add_argument_group("Configuration options") - group.add_argument("-a", - dest="all", default=False, - action='store_true', - help="Print all values") - group.add_argument("--catalog", "-C", - dest="catalog", required=True, - help="Catalog directory of json files from Conduits") - group.add_argument("--host", "-H", - dest="host", nargs="+", - help="Limit to a specific host") - group.add_argument(dest="vars", nargs="*", - help="Variables to print") - - options = parser.parse_args() - - nodes = read_catalogs(options, parser) - if options.all: - print_all_vars(options, parser, nodes) - elif options.vars: - print_vars(options, parser, nodes) - -if __name__ == "__main__": - try: - main() - sys.exit(0) - except KeyboardInterrupt: - print() - sys.exit(1) From 4dd9fd663c46c1d8f1898a2d04a678ab4f4377ac Mon Sep 17 00:00:00 2001 From: Martin Loy Date: Wed, 31 Jan 2018 16:51:49 -0300 Subject: [PATCH 11/11] create authorized_keys with correct user --- roles/conduit/tasks/users.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/conduit/tasks/users.yml b/roles/conduit/tasks/users.yml index 0faa26f..9aca60e 100644 --- a/roles/conduit/tasks/users.yml +++ b/roles/conduit/tasks/users.yml @@ -99,7 +99,7 @@ authorized_key: manage_dir: no path: /var/config/home/{{ ttn_user }}/.ssh/authorized_keys - user: root + user: "{{ ttn_user }}" key: "{{ item }}" with_flattened: "{{ authorized_keys }}"