ERL VPN Auto-Config

**[[openvpn-server-configuration-script-ubiquiti-edgerouter-lite|See this post for information]]**

{{{ lang=python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
##
# VPN Configure-O-Matic
#
# Quickly sets up a VPN server, complete with client/cert config.
# Specifically created for EdgeRouter Lite;
# Probably works on any Linux distro..
# Client configuration can be performed via ssh
#
# @author David Lasley
# @package toolbox
__version__ = “6”

import pexpect
import subprocess
import os
from threading import Thread
from itertools import chain

”’
BEGIN EDITING
”’

CA_PASS = ‘CA_PASS’ #< CA Cert pass HOST_PASS = 'HOST_PASS' #< Host key pass CLIENT_PASS = 'CLIENT_PASS' #< Client key pass ''' Where the CA scripts are located on the server.. probably won't need to change this ''' SSL_DIR = '/usr/lib/ssl/misc/' ''' Where to save all of the certs/keys on the server.. probably won't need to change this ''' SAVE_DIR = '/config/auth/' ''' Default cert configuration options. These can be overridden in the individual configs. ''' DEFAULT_CERT = { 'country':'US', 'state':'Nevada', 'city':'Sin City', 'company':'LasLabs', 'ou':'', 'email':'dave@domain.com', 'pass':'', 'opt_company':'', } # CA cert configuration overrides, always set 'cn' CA_CERT = { 'cn':'router.domain.com', } # Host cert configuration overrides, always set 'cn' HOST_CERT = { 'cn':'vpn.domain.com', } ''' Client SSH users. {} to skip client configuration. Set key-file to file path of private key file if applicable ''' CLIENT_CREDS = {'uname':'username', 'passwd':'password', 'key-file':None} # Host cert configuration overrides, always set 'cn' CLIENTS = [ {'cn':'atl-vps-01.domain.com', 'city':'Atlanta', 'state':'Georgia', 'remote-creds':CLIENT_CREDS}, {'cn':'los-vps-01.domain.com', 'city':'Los Angeles', 'state':'California', 'remote-creds':CLIENT_CREDS}, {'cn':'chi-vps-01.domain.com', 'city':'Chicago', 'state':'Illinois', 'remote-creds':CLIENT_CREDS}, {'cn':'dlasley.vpn.domain.com', 'remote-creds':CLIENT_CREDS} ] # Diffie-helman filename, probably won't need to change DHP_PATH = os.path.join(SAVE_DIR, 'dhp.pem') DHP_BITS = 1024 #< Diffie-Helman Param Bits, probably won't need to change TUNNEL_SETTINGS = { #< VPN Tunnel settings 'vtun0':{ 'mode':'server', 'server subnet':'192.168.68.0/24', #'local-port':'1195', # Routes to LAN/WLAN, pushed to client 'server push-route':['192.168.69.0/24',], # Clients with static IPs 'server client':['atl-vps-01.domain.com ip 192.168.68.100', 'chi-vps-01.domain.com ip 192.168.68.101', 'los-vps-01.domain.com ip 192.168.68.102', ], # TLS Keys/Certs, probably won't need to change 'tls ca-cert-file':os.path.join(SAVE_DIR, 'demoCA', 'cacert.pem'), 'tls cert-file':'%s.pem' % os.path.join(SAVE_DIR, HOST_CERT['cn']), 'tls key-file':'%s.key' % os.path.join(SAVE_DIR, HOST_CERT['cn']), 'tls dh-file':DHP_PATH, } } # # END EDITING # CERT_MAP = [ ['Country Name .*:','country'], ['State or Province Name .*:','state'], ['Locality Name .*:','city'], ['Organization Name .*:','company'], ['Organizational Unit Name .*:','ou'], ['Common Name .*:','cn'], ['Email Address .*:','email'], ['A challenge password .*:','pass'], ['An optional company name .*','opt_company'], ] class erl_obj(object): VYATTA_SHELL_API = '/bin/cli-shell-api' VYATTA_SBIN = '/opt/vyatta/sbin/' def __init__(self, strip_host_pass=True, strip_client_passes=True, ): ''' Init ''' self.ca_sh = os.path.join(SSL_DIR, 'CA.sh') self.strip_host_pass = strip_host_pass self.strip_client_passes = strip_client_passes self.log_file = open('/var/log/erl_vpn.log', 'w') if not os.path.isdir(SAVE_DIR): os.mkdir(SAVE_DIR) os.chdir(SAVE_DIR) def complete_setup(self, ): ''' Perform complete OpenVPN server setup ''' democa = os.path.join(self.SAVE_DIR, 'demoCA') if os.path.isdir(democa): os.rmdir(democa) # Diffie-Helman Thread dhp_thread = Thread(target=self.gen_dhp) dhp_thread.daemon = False dhp_thread.start() # Create CA self.gen_ca() # Host cert/key req_opts = dict(chain(DEFAULT_CERT.items(), HOST_CERT.items())) self.new_req(HOST_PASS, req_opts) self.sign_req(HOST_CERT['cn'], self.strip_host_pass, HOST_PASS) # Client certs/keys self.setup_client_certs() # Wait for Diffie-Helman dhp_thread.join() # Configure the server self.setup_erl_server() def setup_erl_server(self, ): ''' Perform Vyatta/ERL server configuration ''' SET = os.path.join(self.VYATTA_SBIN, 'my_set') COMMIT = os.path.join(self.VYATTA_SBIN, 'my_commit') SAVE = os.path.join(self.VYATTA_SBIN, 'vyatta-save-config.pl') commands = [ 'session_env=$(%s getSessionEnv $PPID)' % self.VYATTA_SHELL_API, 'eval $session_env', '%s setupSession' % self.VYATTA_SHELL_API, ] for tun_name, tun_settings in TUNNEL_SETTINGS.iteritems(): tun_node = 'interfaces openvpn %s' % tun_name for setting_name, setting in tun_settings.iteritems(): if type(setting) == list: #< Allow iteration for _setting in setting: commands.append('%s %s %s %s' % ( SET, tun_node, setting_name, _setting)) else: commands.append('%s %s %s %s' % ( SET, tun_node, setting_name, setting)) commands.append(COMMIT) commands.append(SAVE) self.log_file.write( ' && '.join(commands) + '\r\n' ) subprocess.call([' && '.join(commands)], shell=True) return def setup_erl_client(self, config): ''' Perform Vyatta/ERL remote client config @param dict config Client configuration ''' try: if config['remote-creds']['uname']: if config['remote-creds'].get('key-file'): #< Key auth cmd = 'ssh -i "%s" %s@%s' % ( config['remote-creds']['key-file'], config['remote-creds']['uname'], config.get('ip') or config['cn'] ) elif config['remote-creds'].get('passwd'): #< Password cmd = 'ssh -i %s:%s@%s' % ( config['remote-creds']['uname'], config['remote-creds']['passwd'], config.get('ip') or config['cn'] ) console = pexpect.spawn except KeyError: #< No remote creds return False def setup_client_certs(self, ): ''' Generate client certificates ''' for client in CLIENTS: req_opts = dict(chain(DEFAULT_CERT.items(), client.items())) self.new_req(CLIENT_PASS, req_opts) self.sign_req(client['cn'], self.strip_client_passes, CLIENT_PASS) return def gen_ca(self, filename='', ): ''' Generate CA Cert @param str filename Filename of new cacert ''' ca_opts = dict(chain(DEFAULT_CERT.items(), CA_CERT.items())) console = pexpect.spawn('%s -newca' % self.ca_sh, logfile=self.log_file) console.expect([pexpect.EOF, 'CA certificate filename .*']) console.sendline(filename) self._generic_request(console, CA_PASS, ca_opts) console.expect([pexpect.EOF, 'Enter pass phrase for .*:']) console.sendline(CA_PASS) console.expect([pexpect.EOF, 'Data Base Updated']) return def new_req(self, pem_pass, req_opts): ''' New cert signing request @param str pem_pass PEM Pass for cert @param dic req_opts Request options ''' console = pexpect.spawn('%s -newreq' % self.ca_sh, logfile=self.log_file) self._generic_request(console, pem_pass, req_opts, True) return def sign_req(self, cn, strip_pass=True, passwd='12345'): ''' Sign cert request @param str cn CN of the certificate, will be the new filenames @param bool strip_pass Strip PEM pass? @param str passwd PEM Password ''' console = pexpect.spawn('%s -sign' % self.ca_sh, logfile=self.log_file) console.expect([pexpect.EOF, 'Enter pass phrase for .*:']) console.sendline(CA_PASS) console.expect([pexpect.EOF, 'Sign the certificate\? .*:']) console.sendline('y') console.expect([pexpect.EOF, '1 out of 1 certificate requests certified, .*']) console.sendline('y') console.expect([pexpect.EOF, 'Signed certificate is .*']) os.rename(os.path.join(SAVE_DIR, 'newcert.pem'), os.path.join(SAVE_DIR, '%s.pem' % cn)) key_file = os.path.join(SAVE_DIR, '%s.key' % cn) os.rename(os.path.join(SAVE_DIR, 'newkey.pem'), key_file) if strip_pass: self.strip_pem_pass(key_file, passwd) return def gen_dhp(self, ): ''' Generate Diffie-Helman Parameters... This should be threaded ''' subprocess.check_call(['openssl', 'dhparam', '-out', DHP_PATH, '-2', str(DHP_BITS)]) return def strip_pem_pass(self, file_in, passwd, file_out=None): ''' Strip PEM pass from key file @param str file_in @param str passwd PEM passphrase @param str file_out ''' if not file_out: file_out = file_in console = pexpect.spawn('openssl rsa -in %s -out %s' % (file_in, file_out), logfile=self.log_file) console.expect([pexpect.EOF, 'Enter pass phrase for .*:']) console.sendline(passwd) console.expect([pexpect.EOF, 'writing RSA key']) return def _generic_request(self, console, pem_pass, req_opts, wait_for_it=False): ''' Generic cert request @param pexpect console Pexpect console to manipulate @param str pem_pass PEM Pass for cert @param dic req_opts Request options ''' console.expect([pexpect.EOF, 'Enter PEM pass phrase:']) console.sendline(pem_pass) console.expect([pexpect.EOF, 'Verifying - Enter PEM pass phrase:']) console.sendline(pem_pass) for map_part in CERT_MAP: console.expect([pexpect.EOF, map_part[0]]) console.sendline(req_opts[map_part[1]]) if wait_for_it: console.expect([pexpect.EOF, 'Request is in .* newkey\.pem']) return if __name__ == '__main__': erl_obj = erl_obj() erl_obj.complete_setup() }}}

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *