add missing files and firewall setup

Signed-off-by: Jakub Sokołowski <jakub@status.im>
This commit is contained in:
Jakub Sokołowski 2019-01-20 13:57:15 +01:00
parent 6025427513
commit 0dc3aa9477
No known key found for this signature in database
GPG Key ID: 4EF064D0E6D63020
7 changed files with 302 additions and 0 deletions

2
files/tinc-down Normal file
View File

@ -0,0 +1,2 @@
#!/bin/sh
ifconfig $INTERFACE down

8
files/tinc-health.conf Normal file
View File

@ -0,0 +1,8 @@
# This is used by the Tinc healthcheck
server {
listen 1655;
location /health {
return 200 "OK";
}
}

248
files/tinc-refresh Normal file
View File

@ -0,0 +1,248 @@
#!/usr/bin/env python2
import re
import sys
import time
import json
import consul
import socket
import logging
import urllib3
import requests
import argparse
import ipaddress
from subprocess import call
from python_hosts import Hosts, HostsEntry
# If we can't trust localhost there is no God.
urllib3.disable_warnings()
"""
This script queries Consul for Tinc service metadata
And constructs host config files for Tinc.
"""
tinc_config_template = """
Name = {hostname}
AddressFamily = ipv4
Interface = tun0
# Ping once every 3 minutes
PingInterval = 180
# Prioritize other processes
ProcessPriority = low
# Do not forward packets to other hosts
# WARNING: Without these 1.0.34 has major CPU issues
Forwarding = off
DirectOnly = yes
TunnelServer = yes
{connect_to}
""".strip()
# this file is generated for every service in the catalog
host_config_template = """
Subnet = {vpn_address}/32
Address = {pub_address}
{pub_key}
""".strip()
# Setup logging
log_format = '%(asctime)s [%(levelname)s] %(message)s'
logging.basicConfig(level=logging.INFO, format=log_format)
LOG = logging.getLogger(__name__)
tinc_network_name = 'status.im'
tinc_network_path = '/etc/tinc/' + tinc_network_name
tinc_first_network = u'10.0.0.0'
hostname = socket.gethostname()
ca_path = '/certs/consul-ca.crt'
key_path = '/certs/consul-client.key'
cert_path = '/certs/consul-client.crt'
cert = (cert_path, key_path)
def to_filename(text):
return re.sub(r'[\-\.]', '_', text)
def get_local_pub_key():
with open('{}/rsa_key.pub'.format(tinc_network_path), 'r') as f:
return f.read()
def get_tinc_network(c):
# check the network address for this Data Center
_, rval = c.kv.get('tinc/network')
if rval is not None:
return unicode(rval['Value'], "utf-8")
# otherwise we have to check all other DCs
dcs = c.catalog.datacenters()
networks = []
for dc in dcs:
_, rval = c.kv.get('tinc/network', dc=dc)
if rval is None:
continue
ip_addr = unicode(rval['Value'], "utf-8")
networks.append(ipaddress.ip_network(ip_addr))
if len(networks) == 0:
# if no network has an address yet to do default
highest_net = ipaddress.ip_network(tinc_first_network)
else:
# we pick the highest Tinc network address
highest_net = sorted(networks)[-1]
# bump 2nd octet of network address to get one for this DC
new_net_addr = highest_net.network_address + 2**16
# create new nework
new_network = ipaddress.ip_network(
new_net_addr.compressed + '/' + unicode(highest_net.prefixlen)
)
# update the k/v store with Tinc network address for this DC
c.kv.put('tinc/network', new_network.compressed)
return new_network
def get_new_tinc_ip(c):
try:
# we lock to avoid getting the same IP as another host
ip_lock = c.session.create(name='tinc-ip-lock', ttl=10)
locked = c.kv.put('tinc/ip-lock', 'locked', acquire=ip_lock)
if not locked:
return False
# check current highest IP for Tinc in this DC
_, rval = c.kv.get('tinc/highest-ip')
if rval is not None:
highest_ip = unicode(rval['Value'], "utf-8")
else:
# if there is no highest IP we need to derive it from the network
network = get_tinc_network(c)
highest_ip = unicode(network.network_address)
# increment, this is important especially when IP is a network addr
incremented_ip = str(ipaddress.ip_address(highest_ip) + 1)
# update the k/v store with the current highest IP
c.kv.put('tinc/highest-ip', incremented_ip)
finally:
if ip_lock:
# remember to disable the lock
c.kv.put('tinc/ip-lock', 'unlocked', release=ip_lock)
c.session.destroy(ip_lock)
return incremented_ip
def retry_get_new_tinc_ip(c, retries_left=3):
while retries_left > 0:
rval = get_new_tinc_ip(c)
if rval is not False:
return rval
time.sleep(5)
raise 'Failed to get a new IP for this Tinc peer.'
def add_host_config(service):
host_config = host_config_template.format(
pub_address=service['Address'],
vpn_address=service['ServiceMeta']['tinc_address'],
pub_key=service['ServiceMeta']['tinc_pub_key']
)
host_config_path = '{}/hosts/{}'.format(
tinc_network_path,
to_filename(service['Node'])
)
LOG.debug('Writing: {} - {}'.format(
host_config_path,
service['ServiceMeta']['tinc_address']
))
with open(host_config_path, 'w') as f:
f.write(host_config)
def parse_args():
parser = argparse.ArgumentParser(
description='Automation for generating Tinc config.')
parser.add_argument('-d', '--debug', action='store_true',
help='Enable debug logging mode.')
return parser.parse_args()
#------------------------------------------------------------------------------
args = parse_args()
if args.debug:
LOG.setLevel(logging.DEBUG)
LOG.debug('Collecting Consul catalog info')
c = consul.Consul(port=8500)
dcs = c.catalog.datacenters()
# get existing tinc peers from all Data Centers
services = []
for dc in dcs:
services += c.catalog.service('tinc', dc=dc)[1]
# and our own public address
_, node = c.catalog.node(hostname)
if node is None:
LOG.error('Cannot find local host in catalog!')
sys.exit(1)
# turn list into dict by node name
services_dict = {s['Node']: s for s in services}
# sort by dc, env, stage, and finally name
sorted_services = sorted(
services, key=lambda s: (
s['Datacenter'],
s['NodeMeta']['env'],
s['NodeMeta']['stage'],
s['Node']
)
)
# if current host is not in the catalog add it
if hostname not in services_dict:
services_dict[hostname] = {
'Node': hostname,
'Address': node['Node']['Address'],
'ServiceMeta': {
'tinc_address': retry_get_new_tinc_ip(c),
'tinc_pub_key': get_local_pub_key(),
}
}
vpn_ip = services_dict[hostname]['ServiceMeta']['tinc_address']
LOG.debug('Saving VNP IP: %s', vpn_ip)
# save local host tinc IP for easy access by Ansible
with open('{}/tinc-ip'.format(tinc_network_path), 'w') as f:
f.write(vpn_ip)
LOG.debug('Updating main config file')
with open('{}/tinc.conf'.format(tinc_network_path), 'w') as f:
f.write(tinc_config_template.format(
hostname=to_filename(hostname),
tinc_network_path=tinc_network_path,
connect_to='\n'.join([
'ConnectTo = '+to_filename(s['Node'])
for s in sorted_services
# Don't connect to yourself
if s['Node'] != hostname
])
))
LOG.debug('Generating host config files')
for service in sorted_services:
add_host_config(service)
LOG.debug('Reloading tinc config')
call('systemctl reload tinc@{}'.format(tinc_network_name).split())
LOG.debug('Updating /etc/hosts')
# update hosts file
h = Hosts()
h.add([HostsEntry(
entry_type='ipv4',
address=service['ServiceMeta']['tinc_address'],
names=[service['Node']+'.tinc']
) for service in sorted_services], force=True)
h.write()
LOG.info('Completed Tinc config update')

5
files/tinc-up Normal file
View File

@ -0,0 +1,5 @@
#!/bin/sh
SCRIPT=$(readlink -f "$0")
SCRIPTPATH=$(dirname "$SCRIPT")
VPN_IP=$(cat "${SCRIPTPATH}/tinc-ip")
ifconfig $INTERFACE $VPN_IP netmask 255.0.0.0

6
handlers/main.yml Normal file
View File

@ -0,0 +1,6 @@
---
- name: restart tinc
service:
name: tinc@status.im
state: restarted
enabled: true

31
tasks/firewall.yml Normal file
View File

@ -0,0 +1,31 @@
---
# Enable connections from the Tinc VPN `tun0` interface
- name: Enable VPN connections
tags: ['role::bootstrap:firewall']
iptables:
comment: VPN connections
chain: INPUT
jump: ACCEPT
in_interface: tun0
- name: Enable VPN connections for Docker
tags: ['role::bootstrap:firewall']
iptables:
chain: DOCKER-USER
action: insert
comment: VPN connections
in_interface: tun0
jump: ACCEPT
- name: Enable various service ports
tags: ['role::bootstrap:firewall']
iptables:
chain: INPUT
jump: ACCEPT
source: '0.0.0.0/0'
comment: '{{ item.cmt }}'
protocol: '{{ item.prot | default("tcp") }}'
destination_port: '{{ item.port }}'
with_items:
- { "port": 655, "cmt": "Tinc VPN TCP" }
- { "port": 655, "cmt": "Tinc VPN UDP", "prot": "udp" }

View File

@ -7,6 +7,8 @@
tags: ['role::bootstrap:tinc']
- include_tasks: consul.yml
tags: ['role::bootstrap:tinc']
- include_tasks: firewall.yml
tags: ['role::bootstrap:tinc']
- name: Set Tinc IP as Ansible fact
tags: ['role::bootstrap:tinc']