Изучение способов динамического обслуживания пиров VPN-сервера Wireguard

Я развертываю большое количество серверов в разных географических точках (которые будут время от времени перемещаться), и мне нужно получить удаленный доступ и администрировать эти серверы по низкой цене без какого-либо дополнительного оборудования. После некоторого беглого исследования я решил, что Wireguard - моя лучшая ставка из-за его возможности роуминга (пока один из публичных IP-адресов концентратора или говорящих партнеров остается согласованным, туннель будет сохранен). Я написал быстрый и грязный скрипт Python для запуска при загрузке, который создает закрытые и открытые ключи, вызывает интерфейс WG и передает параметры сети в хранилище параметров AWS SSM. Затем WG VPN-концентратор считывает эти значения по мере их добавления и динамически вызывает новых партнеров.

Это решение предлагает быстрое и грязное исправление, но я не программист Python по профессии, и я бы хотел повторить написанный мной скрипт Python. Есть еще множество случаев, когда реестр может не синхронизироваться (файлы открытого / закрытого ключа случайно удаляются).

Это довольно широкий вопрос, но так как этот код будет распространяться повсеместно (мои навыки Python до сих пор распространялись только на быстрые и грязные скрипты), я ищу рекомендации по проектированию, в которых можно было бы использовать макет и читабельность кода. улучшено в соответствии с лучшими практиками Python (использование классов, более эффективное для разработки цикла и т. д.).

import random
import string
import urllib2
import re
import json
from urllib2 import urlopen

#this is a module obtained from https://pypi.org/project/ssm-parameter-store/
from ssm_parameter_store import SSMParameterStore

import os
import subprocess
import logging
import logging.handlers
from botocore.exceptions import *

log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
handler = logging.handlers.SysLogHandler(address = '/dev/log')
formatter = logging.Formatter('%(module)s.%(funcName)s: %(message)s')
handler.setFormatter(formatter)
log.addHandler(handler)

ssm_client = boto3.client('ssm',
    region_name="us-west-1",
    aws_access_key_id="xxxx",
    aws_secret_access_key="xxxx"
)

#create a global read only object that parses SSM data with dictionaries
store = SSMParameterStore(ssm_client=ssm_client,prefix='/')
peer_list = store['wg_peers']
hostname = subprocess.Popen("awk -F: '{ print \"server-\" $5 $6 }' /sys/class/net/eth0/address", stdout=subprocess.PIPE, shell=True)
hostname = hostname.stdout.read().strip()

def new_store():
    store = SSMParameterStore(ssm_client=ssm_client,prefix='/')
    peer_list = store['wg_peers']
    return peer_list

def new_aws_client(type):
    try:
        ssm_client = boto3.client(type,
        region_name="us-west-1",
        aws_access_key_id="xxxx",
        aws_secret_access_key="xxxx"
    )
        return ssm_client
    except Exception as e:
        log.critical("Could not instantiate a new AWS boto3 client")

def geolocate(datatypes=['public_ip','region','city']):
    url = 'http://ipinfo.io/json'
    response = urlopen(url)
    data = json.load(response)
    data['public_ip'] = data.pop('ip')
    for metadata in datatypes:
        ssm_put('/'+metadata,data[metadata],True)
    return data

def add_ssm_metadata():
    geolocate()
    for datatype in data[metadata]:
        print datatype

def check_wg_exists():
    #set wg_missing to 1 if there is no output
    wg_missing = subprocess.call("ip link show wg0", shell=True)

    #set key_exists to 1 if the public key exists
    key_exists = os.path.isfile('/root/publickey')

    #query SSM again and assign to a new object since a new private IP may have been added for this server
    local_peerlist = new_store()
    if key_exists == 0:
        print "Creating a new private and public key for wg"
        subprocess.check_call("cd /root/ ; wg genkey | tee privatekey | wg pubkey > publickey", shell=True)
        publickey = open('/root/publickey', 'r')
        ssm_put('/peer_id',publickey.read(),False)

    if wg_missing:
        print "Adding generating private and public keys and creating wg interface..."
        subprocess.check_call("ip link add dev wg0 type wireguard", shell=True)
        subprocess.check_call("ip address add dev wg0 {}/24".format(local_peerlist[hostname]['private_ip']), shell=True)
        subprocess.check_call("wg set wg0 listen-port 5555 private-key /root/privatekey peer <public_key_of_wg_hub> allowed-ips 10.1.1.1/16 endpoint <wg_hub_public_ip>:5555 persistent-keepalive 30", shell=True)
        subprocess.check_call("ip link set up dev wg0", shell=True)
        print "wg interface is now active"

    else:
        print "WG interface and private and public key already exists on this server"

def next_ssm_ip():
    aws_ip_list = []
    key_list = []

    #if the hostname already exists in SSM, return the private IP
    if hostname in peer_list.keys():
        print "IP already exists in SSM"
        return peer_list[hostname]['private_ip']

    #find the last used private ip and
    for id in peer_list.keys():
        for private_ip_record in peer_list[id].keys():
            if 'private_ip' in peer_list[id].keys():
                aws_ip_list.append(peer_list[id]['private_ip'])
    current_ip = max(aws_ip_list)
    start = list(map(int, current_ip.split(".")))
    temp_ip = start
    start[3] += 1
    for i in (3, 2, 1):
        if temp_ip[i] == 255:
            temp_ip[i] = 0
            temp_ip[i-1] += 1
    new_ip = ".".join(map(str, temp_ip))
    return new_ip

def add_ssm_private_ip():
    if hostname not in peer_list.keys():
        print "adding private key for this host"

        #call the next_ssm_ip function to find the next available private IP for this peer
        ssm_put('/private_ip', next_ssm_ip(), False)

#function to write parameters to the SSM database
def ssm_put(key, value, overwrite_param):
    try:
        response = ssm_client.put_parameter(
        Name='/wg_peers/' + hostname + key,
        Description='string',
        Value=value,
        Type='String',
        #KeyId='string',
        Overwrite=overwrite_param,
        #AllowedPattern='string')
        )
    except Exception as e:
        print e

if __name__ == "__main__":
    #1. check if a private IP already exists for this peer in SSM - if not, add the IP
    add_ssm_private_ip()

    #2. check if wg private key, public key and interface exists. If not, create and add them
    check_wg_exists()

    #3. add location based metadata to the peers SSM datastore record
    geolocate()



0 ответов

Другие вопросы по тегам