diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..305046a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +templates/tmp/ +files/tmp/ diff --git a/README.md b/README.md index 5be89f2..94625be 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,17 @@ none Role Variables -------------- -TODO +Central configuration: +* `tinc_vpn_id`: name of the vpn to be created (default: `vpn0`) +* `tinc_central_host`: hostname of the always-on server (default: `hetzner-01`) +* `tinc_vpn_net`: local vpn network (IPv4, CIDR notation) +* `tinc_remote_nets`: list of remote networks, that should be forwarded to localhost (TODO: currently only one entry supported) + * `net_cidr`: IPv4 network range (CIDR notation) + * `gateway`: VPN network IP address of the gateway + +Configuration for each host: +* `tinc_client_ip`: own IP address in the tinc-local network +* `tinc_public_addr`: public domain or IP address of the central server Dependencies diff --git a/defaults/main.yml b/defaults/main.yml index 6ba3c95..8483868 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,2 +1,10 @@ --- -# defaults file for tinc \ No newline at end of file +# defaults file for tinc +tinc_base_dir: /etc/tinc +tinc_tmp_pubkey: "tmp/rsa_key-{{ ansible_hostname }}.pub" + +# ID of the vpn to create +tinc_vpn_id: vpn0 + +# hostname of the always-on server +tinc_central_host: hetzner-01 diff --git a/tasks/distribute.yml b/tasks/distribute.yml new file mode 100644 index 0000000..df13b84 --- /dev/null +++ b/tasks/distribute.yml @@ -0,0 +1,13 @@ +--- +# Tinc VPN Hostfile Distribution + +- name: Distribute - Set different base dir for macOS + set_fact: + tinc_base_dir: /usr/local/etc/tinc + when: (override_os_family is defined) | ternary(override_os_family,ansible_os_family) == "Darwin" + +- name: Distribute - Copy hostfiles to targets + become: yes + copy: + src: "{{ role_path }}/files/tmp/" + dest: "{{ tinc_base_dir }}/{{ tinc_vpn_id }}/hosts/" diff --git a/tasks/main.yml b/tasks/main.yml index 92d9ae7..a15213b 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,2 +1,93 @@ --- -# tasks file for tinc \ No newline at end of file +# Tinc VPN Setup and Configuration + +- name: Main - Set different base dir for macOS + set_fact: + tinc_base_dir: /usr/local/etc/tinc + when: (override_os_family is defined) | ternary(override_os_family,ansible_os_family) == "Darwin" + +- name: Main - Install tinc + include_tasks: "{{ item }}" + with_first_found: + - "setup-{{ ansible_distribution }}.yml" + - "setup-{{ (override_os_family is defined) | ternary(override_os_family,ansible_os_family) }}.yml" + + +- name: Main - Create tinc directories + become: yes + file: + path: "{{ tinc_base_dir }}/{{ tinc_vpn_id }}/hosts" + state: directory + mode: '0755' + +- name: Main - Create new host keypair + become: yes + shell: | + export PATH=/usr/local/sbin:/usr/local/bin:$PATH + tincd -n {{ tinc_vpn_id }} -K4096 + args: + chdir: "{{ tinc_base_dir }}/{{ tinc_vpn_id }}" + creates: rsa_key.priv + +- name: Main - Create config + become: yes + template: + src: "{{ role_path }}/templates/tinc.conf.j2" + dest: "{{ tinc_base_dir }}/{{ tinc_vpn_id }}/tinc.conf" + +- name: Main - Fetch public key + become: yes + fetch: + src: "{{ tinc_base_dir }}/{{ tinc_vpn_id }}/rsa_key.pub" + dest: "{{ role_path }}/templates/tmp/rsa_key-{{ ansible_hostname }}.pub" + flat: yes + +- name: Main - Create own hostfile + become: yes + template: + src: "{{ role_path }}/templates/hostfile.j2" + dest: "{{ tinc_base_dir }}/{{ tinc_vpn_id }}/hosts/{{ ansible_hostname }}" + + +- name: Main - Create tinc-up script + become: yes + template: + src: "{{ role_path }}/templates/tinc-up.j2" + dest: "{{ tinc_base_dir }}/{{ tinc_vpn_id }}/tinc-up" + mode: '0755' + +- name: Main - Create tinc-down script + become: yes + template: + src: "{{ role_path }}/templates/tinc-down.j2" + dest: "{{ tinc_base_dir }}/{{ tinc_vpn_id }}/tinc-down" + mode: '0755' + + +- name: Tinc - Fetch all hostfiles + become: yes + fetch: + src: "{{ tinc_base_dir }}/{{ tinc_vpn_id }}/hosts/{{ ansible_hostname }}" + dest: "{{ role_path }}/files/tmp/{{ ansible_hostname }}" + flat: yes + + +- name: "Main - Enable {{ tinc_vpn_id }}" + become: yes + block: + - name: "Main - Enable {{ tinc_vpn_id }} in tinc config" + lineinfile: + name: "{{ tinc_base_dir }}/nets.boot" + line: "{{ tinc_vpn_id }}" + create: yes + - name: "Main - Enable and restart tinc service" + systemd: + name: tinc + state: restarted + enabled: yes + - name: "Main - Enable and restart tinc@{{ tinc_vpn_id }} service" + systemd: + name: "tinc@{{ tinc_vpn_id }}" + state: restarted + enabled: yes + when: inventory_hostname != 'localhost' diff --git a/tasks/setup-Darwin.yml b/tasks/setup-Darwin.yml new file mode 100644 index 0000000..0b39743 --- /dev/null +++ b/tasks/setup-Darwin.yml @@ -0,0 +1,10 @@ +--- +# Tinc/Setup: Install tinc application - macOS Version + +- name: Setup - Install + package: + name: "{{ packages }}" + state: present + vars: + packages: + - tinc diff --git a/tasks/setup-Debian.yml b/tasks/setup-Debian.yml new file mode 100644 index 0000000..8f858b2 --- /dev/null +++ b/tasks/setup-Debian.yml @@ -0,0 +1,11 @@ +--- +# Tinc/Setup: Install tinc application - Debian Version + +- name: Setup - Install + become: yes + apt: + name: "{{ packages }}" + state: present + vars: + packages: + - tinc diff --git a/templates/hostfile.j2 b/templates/hostfile.j2 new file mode 100644 index 0000000..b558b14 --- /dev/null +++ b/templates/hostfile.j2 @@ -0,0 +1,9 @@ +{% if tinc_public_addr is defined %} +Address = {{ tinc_public_addr }} +{% endif %} +Subnet = {{ tinc_client_ip }}/32 +{% if tinc_client_ip == tinc_remote_nets[0].gateway %} +Subnet = {{ tinc_remote_nets[0].net_cidr }} +{% endif %} + +{% include tinc_tmp_pubkey %} diff --git a/templates/tinc-down.j2 b/templates/tinc-down.j2 new file mode 100644 index 0000000..193fa8d --- /dev/null +++ b/templates/tinc-down.j2 @@ -0,0 +1,15 @@ +#!/bin/sh + +{% if ansible_hostname == 'hetzner-01' %} +/sbin/ifconfig $INTERFACE down +/usr/sbin/ip rule del to {{ tinc_remote_nets[0].net_cidr }} table 5 + +{% elif ansible_hostname == 'RaspiBeyerstedt' %} +/sbin/ifconfig $INTERFACE down +/bin/ip route del {{ tinc_remote_nets[0].net_cidr }} dev eth0 + +{% elif ansible_hostname == 'Magrathea' %} +/sbin/ifconfig $INTERFACE down +/sbin/route -n delete -net {{ tinc_remote_nets[0].net_cidr }} {{ tinc_remote_nets[0].gateway }} + +{% endif %} diff --git a/templates/tinc-up.j2 b/templates/tinc-up.j2 new file mode 100644 index 0000000..8a2ea1f --- /dev/null +++ b/templates/tinc-up.j2 @@ -0,0 +1,23 @@ +#!/bin/sh + +{% if ansible_hostname == 'hetzner-01' %} +/sbin/ifconfig $INTERFACE {{ tinc_client_ip | ipaddr('address') }} netmask 255.255.255.0 + +/usr/sbin/ip rule add to {{ tinc_remote_nets[0].net_cidr }} table 5 +/usr/sbin/ip route add {{ tinc_remote_nets[0].net_cidr }} via {{ tinc_remote_nets[0].gateway }} dev {{ tinc_vpn_id }} table 5 + +{% elif ansible_hostname == 'RaspiBeyerstedt' %} +/sbin/ifconfig $INTERFACE {{ tinc_client_ip | ipaddr('address') }} netmask 255.255.255.0 + +/bin/bash -c "echo 1 > /proc/sys/net/ipv4/ip_forward" +/bin/ip route add {{ tinc_remote_nets[0].net_cidr }} dev eth0 + +iptables -t nat -A POSTROUTING -o eth0 -s {{ tinc_vpn_net }} -j MASQUERADE + +{% elif ansible_hostname == 'Magrathea' %} +# only a single endpoint works, because tun interface is p2p +/sbin/ifconfig tun0 inet {{ tinc_client_ip | ipaddr('address') }} {{ tinc_remote_nets[0].gateway }} up netmast 255.255.255.0 + +/sbin/route -n add -net {{ tinc_remote_nets[0].net_cidr }} {{ tinc_remote_nets[0].gateway }} + +{% endif %} diff --git a/templates/tinc.conf.j2 b/templates/tinc.conf.j2 new file mode 100644 index 0000000..da2c297 --- /dev/null +++ b/templates/tinc.conf.j2 @@ -0,0 +1,8 @@ +Name = {{ ansible_hostname }} +{% if (override_os_family is defined) | ternary(override_os_family,ansible_os_family) != 'Darwin' %} +Device = /dev/net/tun +{% endif %} +{% if ansible_hostname != tinc_central_host %} +ConnectTo = {{ tinc_central_host }} +{% endif %} +AddressFamily = any