From 380f1c6d5463e1dd200ab7801fd04b406c9a48c2 Mon Sep 17 00:00:00 2001 From: Fulvio Valenza Date: Fri, 11 Nov 2016 13:39:04 +0100 Subject: [PATCH] Initial commit --- .gitignore | 8 + M2LPlugin/Readme.md | 1 + MSPL/Readme.md | 1 + NED_files/TVDM/PSAManifest/strongswan | 22 ++ .../psaConfigs/strongswan/strongswan_user8 | 20 ++ NED_files/TVDM/userGraph/user8 | 26 ++ PSA/Config.py | 91 +++++++ PSA/Readme.md | 4 + PSA/boot_script_psa | 11 + PSA/defaultIpsecConf | 13 + PSA/dumpLogFile.py | 25 ++ PSA/execInterface.py | 149 +++++++++++ PSA/getConfiguration.py | 115 +++++++++ PSA/interfaces | 32 +++ PSA/json/psaStartup.json | 9 + PSA/parseConfig.py | 47 ++++ PSA/psaConfigs/README.md | 1 + PSA/psaConfigs/ipsec.conf | 29 +++ .../ipsec.d/cacerts/strongswanCert.pem | 30 +++ PSA/psaConfigs/ipsec.d/certs/ClientCert.pem | 25 ++ PSA/psaConfigs/ipsec.d/certs/vpnHostCert.pem | 26 ++ PSA/psaConfigs/ipsec.d/private/ClientKey.pem | 27 ++ PSA/psaConfigs/ipsec.d/private/vpnHostKey.pem | 27 ++ PSA/psaConfigs/psaconf | 20 ++ PSA/psaEE.conf | 10 + PSA/psaEE.py | 90 +++++++ PSA/psaExceptions.py | 13 + PSA/scripts/current_config.sh | 19 ++ PSA/scripts/init.sh | 39 +++ PSA/scripts/ip_conf.sh | 41 +++ PSA/scripts/ping.sh | 14 + PSA/scripts/start.sh | 32 +++ PSA/scripts/status.sh | 28 ++ PSA/scripts/stop.sh | 29 +++ Readme.md | 239 ++++++++++++++++++ copy_psa_sw_to_vm.sh | 31 +++ copy_psa_to_ned.sh | 51 ++++ hostname | 1 + hosts | 7 + images/end_user_psa_vpn_schema.png | Bin 0 -> 40653 bytes images/psa_vpn_schema.png | Bin 0 -> 66272 bytes strongswan.xml | 130 ++++++++++ strongswan/psa/ipsec.conf | 29 +++ .../psa/ipsec.d/cacerts/strongswanCert.pem | 30 +++ strongswan/psa/ipsec.d/certs/ClientCert.pem | 25 ++ strongswan/psa/ipsec.d/certs/vpnHostCert.pem | 26 ++ strongswan/psa/ipsec.d/private/ClientKey.pem | 27 ++ strongswan/psa/ipsec.d/private/vpnHostKey.pem | 27 ++ strongswan/server/ipsec.conf | 29 +++ strongswan/server/ipsec.d/Client.p12 | Bin 0 -> 4296 bytes strongswan/server/ipsec.d/ClientKey.pem | 27 ++ .../server/ipsec.d/cacerts/strongswanCert.pem | 30 +++ .../server/ipsec.d/certs/ClientCert.pem | 25 ++ .../server/ipsec.d/certs/vpnHostCert.pem | 26 ++ strongswan/server/ipsec.d/client.conf | 30 +++ .../server/ipsec.d/private/ClientKey.pem | 27 ++ .../server/ipsec.d/private/strongswanKey.pem | 51 ++++ .../server/ipsec.d/private/vpnHostKey.pem | 27 ++ tests/I_TC_1.py | 193 ++++++++++++++ tests/NEDtest.py | 187 ++++++++++++++ tests/README.md | 11 + 61 files changed, 2360 insertions(+) create mode 100644 .gitignore create mode 100644 M2LPlugin/Readme.md create mode 100644 MSPL/Readme.md create mode 100644 NED_files/TVDM/PSAManifest/strongswan create mode 100644 NED_files/TVDM/psaConfigs/strongswan/strongswan_user8 create mode 100644 NED_files/TVDM/userGraph/user8 create mode 100644 PSA/Config.py create mode 100644 PSA/Readme.md create mode 100644 PSA/boot_script_psa create mode 100644 PSA/defaultIpsecConf create mode 100644 PSA/dumpLogFile.py create mode 100644 PSA/execInterface.py create mode 100644 PSA/getConfiguration.py create mode 100644 PSA/interfaces create mode 100644 PSA/json/psaStartup.json create mode 100644 PSA/parseConfig.py create mode 100644 PSA/psaConfigs/README.md create mode 100644 PSA/psaConfigs/ipsec.conf create mode 100644 PSA/psaConfigs/ipsec.d/cacerts/strongswanCert.pem create mode 100644 PSA/psaConfigs/ipsec.d/certs/ClientCert.pem create mode 100644 PSA/psaConfigs/ipsec.d/certs/vpnHostCert.pem create mode 100644 PSA/psaConfigs/ipsec.d/private/ClientKey.pem create mode 100644 PSA/psaConfigs/ipsec.d/private/vpnHostKey.pem create mode 100644 PSA/psaConfigs/psaconf create mode 100644 PSA/psaEE.conf create mode 100644 PSA/psaEE.py create mode 100644 PSA/psaExceptions.py create mode 100644 PSA/scripts/current_config.sh create mode 100644 PSA/scripts/init.sh create mode 100644 PSA/scripts/ip_conf.sh create mode 100644 PSA/scripts/ping.sh create mode 100644 PSA/scripts/start.sh create mode 100644 PSA/scripts/status.sh create mode 100644 PSA/scripts/stop.sh create mode 100644 Readme.md create mode 100644 copy_psa_sw_to_vm.sh create mode 100644 copy_psa_to_ned.sh create mode 100644 hostname create mode 100644 hosts create mode 100644 images/end_user_psa_vpn_schema.png create mode 100644 images/psa_vpn_schema.png create mode 100644 strongswan.xml create mode 100644 strongswan/psa/ipsec.conf create mode 100644 strongswan/psa/ipsec.d/cacerts/strongswanCert.pem create mode 100644 strongswan/psa/ipsec.d/certs/ClientCert.pem create mode 100644 strongswan/psa/ipsec.d/certs/vpnHostCert.pem create mode 100644 strongswan/psa/ipsec.d/private/ClientKey.pem create mode 100644 strongswan/psa/ipsec.d/private/vpnHostKey.pem create mode 100644 strongswan/server/ipsec.conf create mode 100644 strongswan/server/ipsec.d/Client.p12 create mode 100644 strongswan/server/ipsec.d/ClientKey.pem create mode 100644 strongswan/server/ipsec.d/cacerts/strongswanCert.pem create mode 100644 strongswan/server/ipsec.d/certs/ClientCert.pem create mode 100644 strongswan/server/ipsec.d/certs/vpnHostCert.pem create mode 100644 strongswan/server/ipsec.d/client.conf create mode 100644 strongswan/server/ipsec.d/private/ClientKey.pem create mode 100644 strongswan/server/ipsec.d/private/strongswanKey.pem create mode 100644 strongswan/server/ipsec.d/private/vpnHostKey.pem create mode 100644 tests/I_TC_1.py create mode 100644 tests/NEDtest.py create mode 100644 tests/README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5602298 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.pyc +*.log +*.req +*.state +*.sublime* +tmp +*.original +*.directory diff --git a/M2LPlugin/Readme.md b/M2LPlugin/Readme.md new file mode 100644 index 0000000..0d7efd8 --- /dev/null +++ b/M2LPlugin/Readme.md @@ -0,0 +1 @@ +PSA M2LPlugin placeholder diff --git a/MSPL/Readme.md b/MSPL/Readme.md new file mode 100644 index 0000000..045e396 --- /dev/null +++ b/MSPL/Readme.md @@ -0,0 +1 @@ +PSA MSPL placeholder diff --git a/NED_files/TVDM/PSAManifest/strongswan b/NED_files/TVDM/PSAManifest/strongswan new file mode 100644 index 0000000..6ace8ea --- /dev/null +++ b/NED_files/TVDM/PSAManifest/strongswan @@ -0,0 +1,22 @@ +{ + "PSA_id":"strongswan", + "disk": "corporate-vpnPSA.qcow2", + "interface": [ + { + "network":"data", + "type":"data_in" + }, + { + "network":"data", + "type":"data_out" + }, + { + "network":"control", + "type":"manage" + } + ], + "memory": "1024", + "IP": true, + "os-architecture": "x86_64", + "vcpu": "1" +} diff --git a/NED_files/TVDM/psaConfigs/strongswan/strongswan_user8 b/NED_files/TVDM/psaConfigs/strongswan/strongswan_user8 new file mode 100644 index 0000000..baef919 --- /dev/null +++ b/NED_files/TVDM/psaConfigs/strongswan/strongswan_user8 @@ -0,0 +1,20 @@ +{ + "default": { + "keyexchange": "ikev2", + "rekeymargin": "3m", + "keyingtries": "3", + "ikelifetime": "60m" + }, + "psa": { + "rightsubnet": "0.0.0.0/0", + "leftfirewall": "yes", + "leftsourceip": "%config", + "auto": "add", + "left": "%any", + "leftsubnet": "10.2.2.0/16", + "leftcert": "ClientCert.pem", + "rightid": "@vpn.secured.eu", + "leftid": "psa@secured.eu", + "right": "147.83.42.191" + } +} \ No newline at end of file diff --git a/NED_files/TVDM/userGraph/user8 b/NED_files/TVDM/userGraph/user8 new file mode 100644 index 0000000..f9f66c8 --- /dev/null +++ b/NED_files/TVDM/userGraph/user8 @@ -0,0 +1,26 @@ +{ + "name": "user_profile_type", + "user_token": "", + "profile_type": "AD", + + "PSASet": [ + + { + "id": "strongswan", + "security_controls": [ + + { + "imgName": "strongswan.img", + "conf_id":"strongswan_user8" + } + + ] + + } + + ], + + "ingress_flow": ["strongswan"], + "egress_flow": ["strongswan"] + +} diff --git a/PSA/Config.py b/PSA/Config.py new file mode 100644 index 0000000..a3ccc69 --- /dev/null +++ b/PSA/Config.py @@ -0,0 +1,91 @@ +import ConfigParser +import os +import copy + + +class Configuration(object): + + _instance = None + #(fmignini) Not too meaningful use this var, I should change his name with something else like inizialized = False + _AUTH_SERVER = None + + def __new__(cls, *args, **kwargs): + + if not cls._instance: + cls._instance = super(Configuration, cls).__new__( + cls, *args, **kwargs) + return cls._instance + + def __init__(self): + #print 'Configuration - PATH : '+os.getcwd() + path = copy.copy(os.getcwd()) + path_dirs = path.split("/") + for path_dir in path_dirs: + if path_dir == 'tests': + self.test = True + else: + self.test = False + #print self.test + if self._AUTH_SERVER is None: + self.inizialize() + + def inizialize(self): + config = ConfigParser.RawConfigParser() + config.read('psaEE.conf') + self._LOG_FILE = 'PSA.log' + self._VERBOSE = 'true' + self._DEBUG = 'true' + self._PSC_ADDRESS = config.get('configuration', 'psc_address') + self._PSA_CONFIG_PATH = config.get('configuration', 'psa_config_path') + self._PSA_ID = config.get('configuration', 'psa_id') + self._PSA_SCRIPTS_PATH = config.get('configuration', 'scripts_path') + self._PSA_API_VERSION = config.get('configuration', 'psa_api_version') + self._PSA_VERSION = config.get('configuration', 'psa_version') + self._PSA_NAME = config.get('configuration', 'psa_name') + self._PSA_LOG_LOCATION = config.get('configuration', 'psa_log_location') + + #self._CONF_ID = config.get('configuration', 'conf_id') + + @property + def LOG_FILE(self): + return self._LOG_FILE + + @property + def VERBOSE(self): + return self._VERBOSE + + @property + def PSC_ADDRESS(self): + return self._PSC_ADDRESS + + @property + def PSA_CONFIG_PATH(self): + return self._PSA_CONFIG_PATH + + @property + def PSA_SCRIPTS_PATH(self): + return self._PSA_SCRIPTS_PATH + + @property + def PSA_ID(self): + return self._PSA_ID + + @property + def PSA_NAME(self): + return self._PSA_NAME + + @property + def PSA_API_VERSION(self): + return self._PSA_API_VERSION + + @property + def PSA_VERSION(self): + return self._PSA_VERSION + + @property + def PSA_LOG_LOCATION(self): + return self._PSA_LOG_LOCATION + + # @property + # def CONF_ID(self): + # return self._CONF_ID diff --git a/PSA/Readme.md b/PSA/Readme.md new file mode 100644 index 0000000..a9a4666 --- /dev/null +++ b/PSA/Readme.md @@ -0,0 +1,4 @@ +# Software for PSA Execution Environment (*ctrlmgmtd* agent) + +These files or functionality need to be a part of the PSA Execution Environment template. + diff --git a/PSA/boot_script_psa b/PSA/boot_script_psa new file mode 100644 index 0000000..14fb82f --- /dev/null +++ b/PSA/boot_script_psa @@ -0,0 +1,11 @@ +#!/bin/bash + +# Place this in /etc/network/if-up.d/ + +[ "$IFACE" = 'eth2' ] || exit 0 + +ifconfig eth2 mtu 1496 +dhclient -1 eth2 +cd /home/psa/pythonScript +ip=$(ifconfig eth2 | grep "inet addr" | awk '{print $2}' | cut -d: -f2) +gunicorn -b $ip:8080 --log-file /home/psa/GUNICORN.log --log-level debug psaEE:app & diff --git a/PSA/defaultIpsecConf b/PSA/defaultIpsecConf new file mode 100644 index 0000000..31627a1 --- /dev/null +++ b/PSA/defaultIpsecConf @@ -0,0 +1,13 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file +config setup + +conn my_lan + left=%any + leftsubnet=192.168.2.0/24 #,10.2.0.0/16 + rightsubnet=192.168.2.0/24 #,10.2.0.0/16 + authby=never + type=passthrough + auto=route + + + diff --git a/PSA/dumpLogFile.py b/PSA/dumpLogFile.py new file mode 100644 index 0000000..72ab5ff --- /dev/null +++ b/PSA/dumpLogFile.py @@ -0,0 +1,25 @@ +''' + File: dumpLogFile.py + Description: + REST resource to dump content of the log file from the PSC + For development purpose only! Disable this in production (TBD) + +''' +import falcon +import json + + +class dumpLogFile(): + def __init__(self): + pass + + def on_get(self, req, resp): + try: + in_file = open("PSA.log","r") + log = in_file.read() + in_file.close() + resp.status = falcon.HTTP_200 + resp.body = log + except Exception as e: + logging.exception(sys.exc_info()[0]) + resp.status = falcon.HTTP_501 diff --git a/PSA/execInterface.py b/PSA/execInterface.py new file mode 100644 index 0000000..57516a6 --- /dev/null +++ b/PSA/execInterface.py @@ -0,0 +1,149 @@ +# +# File: execInterface.py +# Created: 27/08/2014 +# Author: BSC, VTT +# +# Description: +# Web service running on the PSA receiving the configuration for the PSA from the PSC +# +# + +import falcon +import logging +import json +import sys +import subprocess +import os +import stat + +class execInterface(): + def __init__(self, configsPath, scriptsPath, psaLogLocation, psaID): + self.confsPath = configsPath + self.scripts_path = scriptsPath + self.log_location = psaLogLocation + self.psaID = psaID + + def on_post(self, request, response, command): + print "onPost" + try: + res = {} + res["command"] = command + if command == "init": + # receiev the configuration, or init package + script_file = self.confsPath + "/psaconf" + fp=open(script_file, 'wb') + while True: + chunk = request.stream.read(4096) + fp.write(chunk) + if not chunk: + break + fp.close() + + # Make script executable for current user + # hazardous.. we're root + #st = os.stat(script_file) + #os.chmod(script_file, st.st_mode | stat.S_IEXEC) + + # Run the init.sh and return it's return value + res["ret_code"] = str(self.callInitScript()) + logging.info("PSA "+self.psaID+" configuration registered") + elif command == "start": + res["ret_code"] = str(self.callStartScript()) + elif command == "stop": + res["ret_code"] = str(self.callStopScript()) + else: + logging.info("POST: unknown command: " + command) + response.status = falcon.HTTP_404 + return + + response.body = json.dumps(res) + response.status = falcon.HTTP_200 + response.set_header("Content-Type", "application/json") + except Exception as e: + logging.exception(sys.exc_info()[0]) + response.status = falcon.HTTP_501 + + def on_get(self, request, response, command): + try: + res = {} + res["command"] = command + if command == "status": + res["ret_code"] = self.callStatusScript().replace("\n", "") + elif command == "configuration": + res["ret_code"] = self.callGetConfigurationScript() + elif command == "internet": + res["ret_code"] = self.callGetInternetScript() + elif command == "log": + # Return PSA log or 501 + log = self.callGetLogScript() + if log != None: + response.body = log + response.status = falcon.HTTP_200 + response.set_header("Content-Type", "text/plain; charset=UTF-8") + else: + #res["ret_code"] = "not_available" + #response.body = json.dumps(res) + #response.set_header("Accept", "application/json") + response.status = falcon.HTTP_501 + return + else: + logging.info("GET: unknown command: " + command) + response.status = falcon.HTTP_404 + return + + response.body = json.dumps(res) + response.status = falcon.HTTP_200 + response.set_header("Content-Type", "application/json") + except Exception as e: + logging.exception(sys.exc_info()[0]) + response.status = falcon.HTTP_501 + + def callInitScript(self): + logging.info("callInitScript()") + ret = subprocess.call(['.' + self.scripts_path + 'init.sh']) + return ret + + def callStartScript(self): + logging.info("callStartScript()") + ret = subprocess.call(['.' + self.scripts_path + 'start.sh']) + return ret + + def callStopScript(self): + logging.info("callStopScript()") + ret = subprocess.call(['.' + self.scripts_path + 'stop.sh']) + return ret + + def callStatusScript(self): + proc = subprocess.Popen(['.' + self.scripts_path + 'status.sh'], stdout=subprocess.PIPE, shell=True) + (out, err) = proc.communicate() + return out + + def callGetConfigurationScript(self): + logging.info("callGetConfigurationScript()") + proc = subprocess.Popen(['.' + self.scripts_path + 'current_config.sh'], stdout=subprocess.PIPE, shell=True) + (out, err) = proc.communicate() + return out + + def callGetInternetScript(self): + logging.info("callGetInternetScript()") + proc = subprocess.Popen(['.' + self.scripts_path + 'ping.sh'], stdout=subprocess.PIPE, shell=True) + (out, err) = proc.communicate() + return out + + def callGetLogScript(self): + logging.info("callGetLogScript()") + ret = None + try: + with open(self.log_location, "r") as f: + ret = f.read() + except Exception as e: + logging.exception(sys.exc_info()[0]) + + return ret + + def get_client_address(self,environ): + try: + return environ['HTTP_X_FORWARDED_FOR'].split(',')[-1].strip() + except KeyError: + return environ['REMOTE_ADDR'] + diff --git a/PSA/getConfiguration.py b/PSA/getConfiguration.py new file mode 100644 index 0000000..7183ee1 --- /dev/null +++ b/PSA/getConfiguration.py @@ -0,0 +1,115 @@ +# +# File: getConfiguration.py +# Created: 05/09/2014 +# Author: BSC + +# Modified: 29/10/2015, 2016 +# Author: VTT, UPC +# +# Description: +# Web service running on the PSA interacting with the PSC +# +# + +import json +import requests +import logging +from psaExceptions import psaExceptions +import subprocess +import base64 + +class getConfiguration(): + + #def __init__(self, pscAddr, configsPath, confID, psaID): + def __init__(self, pscAddr, configsPath, scriptsPath, psaID, psaAPIVersion): + self.pscAddr = pscAddr + self.configsPath = configsPath + self.scripts_path = scriptsPath + #self.confID = confID + self.psaID = psaID + self.psaAPI = psaAPIVersion + + def send_start_event(self): + logging.info("PSA: send_start_event") + logging.info("PSA: "+self.psaID+" calling PSC") + resp = requests.get(self.pscAddr + "/" + self.psaAPI + "/psa_up/" + self.psaID) + logging.info("PSA: "+self.psaID+" calling PSC done") + return resp.content + + def pullPSAconf(self): + + header = {'Content-Type':'application/octet-stream'} + + #resp = requests.get(self.pscAddr+"/getConf/"+self.psaID+"/"+self.confID, headers=header) + resp = requests.get(self.pscAddr + "/" +self.psaAPI + "/getConf/"+self.psaID, headers=header) + if (resp.status_code == requests.codes.ok): + #fp=open(self.configsPath+"/"+self.confID,'wb') + #fp=open(self.configsPath+"/"+self.psaID,'wb') + # We don't have multiple security controls inside one PSA image at the moment. + json_config = False + try: + conf = json.loads(resp.content) + logging.info("PSA JSON conf received:") + logging.info(conf) + # Handle different config formats + if conf["conf_type"] == "base64": + decoded_conf = base64.b64decode(conf["conf"]) + elif conf["conf_type"] == "text": + decoded_conf = conf["conf"] + else: + # Use default format, presume text. + decoded_conf = conf["conf"] + json_config = True + except Exception as e: + logging.info("Could not load JSON config, reverting to old text format") + decoded_conf = resp.content + + fp=open(self.configsPath+"/psaconf", 'wb') + fp.write(decoded_conf) + fp.close() + + self.callInitScript() + if json_config: + self.enforceConfiguration(conf) + + logging.info("PSA "+self.psaID+" configuration registered") + return resp.content + else: + logging.error("Bad configuration request for PSA "+self.psaID) + raise psaExceptions.confRetrievalFailed() + + + # header = {'Accept':'application/octet-stream', 'Content-Type':'application/octet-stream'} + # resp = requests.get(self.pscAddr+"/getConfiguration/"+self.confURI, data={}, headers=header) + # if (resp.status_code != 200): + # msg = "PSC is not able to provide the conf for: [PSAid] " + self.psaID + ", [confURI] " + self.confURI + # raise psaExceptions.confRetrievalFailed(msg) + + # TODO check script validity + #return resp.text + + def callInitScript(self): + logging.info("callInitScript()") + ret = subprocess.call(['.' + self.scripts_path + 'init.sh']) + return ret + + def enforceConfiguration(self, jsonConf): + req_keys = ("IP", "dns", "netmask", "gateway") + has_req = False + if all (key in jsonConf for key in req_keys): + has_req = True + + if has_req: + logging.info("PSA requires IP, configuring...") + ip = jsonConf["IP"] + dns = jsonConf["dns"] + netmask = jsonConf["netmask"] + gateway = jsonConf["gateway"] + logging.info("ip: " + str(ip)) + logging.info("gateway: " + str(gateway)) + logging.info("dns: " + str(dns)) + logging.info("netmask: " + str(netmask)) + ret = subprocess.call(['.' + self.scripts_path + 'ip_conf.sh', ip, gateway, dns, netmask]) + logging.info("Result of setting config: " + str(ret)) + else: + logging.info("PSA doesn't require IP, skipping configuration.") diff --git a/PSA/interfaces b/PSA/interfaces new file mode 100644 index 0000000..ccf3246 --- /dev/null +++ b/PSA/interfaces @@ -0,0 +1,32 @@ +# PSA interface file +# Place this in /etc/network in your PSA image template + +# This file describes the network interfaces available on your system +# and how to activate them. For more information, see interfaces(5). + +# The loopback network interface +auto lo br0 eth2 +iface lo inet loopback + +# The primary network interface + +iface eth0 inet manual +iface eth1 inet manual +#iface eth2 inet dhcp +iface eth2 inet manual + +iface br0 inet manual + pre-up ip link set eth0 down + pre-up ip link set eth1 down + pre-up brctl addbr br0 + pre-up brctl addif br0 eth0 eth1 + pre-up ip addr flush dev eth0 + pre-up ip addr flush dev eth1 + pre-up ip link set eth0 up + pre-up ip link set eth1 up + pre-up ip link set br0 up + post-down ip link set eth0 down + post-down ip link set eth1 down + post down ip link set br0 down + post-down brctl delif br0 eth0 eth1 + post-down brctl delbr br0 diff --git a/PSA/json/psaStartup.json b/PSA/json/psaStartup.json new file mode 100644 index 0000000..846d571 --- /dev/null +++ b/PSA/json/psaStartup.json @@ -0,0 +1,9 @@ +{ + + "name": "psa_startup_file", + "user_token": "token1", + "psaID": "12345", + "pscAddr": "http://127.0.0.1:4321", + "confURI": "12345" + +} diff --git a/PSA/parseConfig.py b/PSA/parseConfig.py new file mode 100644 index 0000000..9a5a612 --- /dev/null +++ b/PSA/parseConfig.py @@ -0,0 +1,47 @@ +import json +import sys + +def parseJSON(jsonPath, defaultConfigPath, ipsecConfigPath): + try: + f = open(jsonPath, 'r') + strjson=f.read().replace('\n', '') + jsObj = json.loads(str(strjson)) + + try: + dc = open(defaultConfigPath) + lines = dc.readlines() + r = open(ipsecConfigPath, 'w') + r.writelines(lines) + + default = jsObj["default"] + if(default): + r.write("conn %default\n") + for p in default: + r.write("\t" + p + "=" + default[p] + "\n") + r.write("\n") + psa = jsObj["psa"] + if(psa): + r.write("conn psa\n") + for p in psa: + + if psa[p] != "": + r.write("\t" + p + "=" + psa[p] + "\n") + elif p == "left": + r.write("\t" + p + "=%any" + "\n") + elif p == "leftsourceip": + r.write("\t" + p + "=%config" + "\n") + + r.write("\n") + #r.close() + except Exception,e: + print str(e) + print "Can't open ipsec config file" + except Exception,e: + print str(e) + print "Can't open json config file" + +args = sys.argv +if len(args) == 4: + parseJSON(args[1],args[2],args[3]) +else: + print "Usage: parseConfig.py [jsonPath] [defaultConfigPath] [ipsecConfigPath]" diff --git a/PSA/psaConfigs/README.md b/PSA/psaConfigs/README.md new file mode 100644 index 0000000..e9ea7bb --- /dev/null +++ b/PSA/psaConfigs/README.md @@ -0,0 +1 @@ +Runtime PSA security control configs are stored in this folder. diff --git a/PSA/psaConfigs/ipsec.conf b/PSA/psaConfigs/ipsec.conf new file mode 100644 index 0000000..0098b53 --- /dev/null +++ b/PSA/psaConfigs/ipsec.conf @@ -0,0 +1,29 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=3 + keyexchange=ikev2 + +conn my_lan + left=%any + leftsubnet=192.168.2.0/24 #,10.2.0.0/16 + rightsubnet=192.168.2.0/24 #,10.2.0.0/16 + authby=never + type=passthrough + auto=route + +conn psa + left=%any + leftsourceip=%config + leftcert=ClientCert.pem + leftsubnet=10.2.2.0/16 + leftid=psa@secured.eu + leftfirewall=yes + right=147.83.42.191 + rightsubnet=0.0.0.0/0 + rightid=@vpn.secured.eu + auto=add diff --git a/PSA/psaConfigs/ipsec.d/cacerts/strongswanCert.pem b/PSA/psaConfigs/ipsec.d/cacerts/strongswanCert.pem new file mode 100644 index 0000000..00a71e8 --- /dev/null +++ b/PSA/psaConfigs/ipsec.d/cacerts/strongswanCert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFNjCCAx6gAwIBAgIIEkPUG0+7tv8wDQYJKoZIhvcNAQEFBQAwOTELMAkGA1UE +BhMCRVMxEDAOBgNVBAoTB1NlY3VyZWQxGDAWBgNVBAMTD1NlY3VyZWQgUm9vdCBD +QTAeFw0xNjAyMjkxNjE5NTVaFw0yNjAyMjYxNjE5NTVaMDkxCzAJBgNVBAYTAkVT +MRAwDgYDVQQKEwdTZWN1cmVkMRgwFgYDVQQDEw9TZWN1cmVkIFJvb3QgQ0EwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ18n1g4CaO4UmNQBCvVA0hZjs +yY2I1PLhgnQS4tUu6zTb8CqL2qNr/lPh/PuDMhCDnyAdSvO+RrBRBbIKrDet1Ee6 +74lQnrhcN0b+8wQu3xBTwhMpg8iAPduJdDhdzDGrc0sKeUvJ2RnjQmyqNGTvCezl +7F/xb1UKKg1uZGTorqdxYg09v/6rg51/vd1xnjAaMI5aC4zSbmhDMgNowrNdVtW/ +qC7XQ+mgRNqDdf/ac63vnpNbKt8GhPjEVo/pJUSgNThpyBob5DTbSgFDPXxUjUyQ +QMwB4NW4CcxqhzoYdrhZXL/BAi7i/bLliX5ivOzKQHEbmfnwHwIyO39/OoEHFNnE +QC3YjDynFfdyx9LthxeaMFYXFmU/iWtL7qMfZb6IeiV1vkXchC8O/4aYzSqBskHX +XDwojlxMWYKfb9cJSfiOSbaaLvr1Gof2rjQSDEiJhdvynTE/Qf1wCmQDnLBfwZOv +d8mMdpi9g0Bqg3JF9/GuvebRJK8CKLcQM6wvn3W8YrxoFDDkH9ni+eLTRjZcazX1 +UuDL6lnWn2ZzO/ND54VSBwE/hPMcV63iSYWcNJP/Q69T2pXBrTigSBhlwaXuzRIF +/7YmrOwqag7d9lA3mej+jsaygA3L/f1CpI9HWjgZD16S2D83qWjcR66UqNDz8mFf +DPdLOQROZi5u+OLTHQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQUMPENpHawLXFavG47snr+cfvNR5QwDQYJKoZIhvcN +AQEFBQADggIBAFfyR9Q8h4poKaPu4Yh0uKVPDQSnX+xE/xw/acWu6NTck85TYP7G +s3U7+7IcEOaA/JJc5XhKEINQKj5qi28wzQzUg+bBoGAkd2PlfJZrLAULdNKBBjwH +7biMUIZFuPKhIJkgVK45o9gukQZW6azZ6uB0QYRD1usFrmqnERtNSYYv7QR1OuTp +MjHNdyyPok3vrcaYORg5bFmbPeyy/LPvyzHnv9eKR/ykj9Y7nCjBRq2sEa30bwzU +LFsEYmhrZs2Ja2YA9NjUWE6DOZdLrOjQDLHObGxVnLgzRrnwVqq33RJxCHGhcV7A +DjhVSH1qRs0OD/LhRpJIO/n7z/xPAH/T2/UhVpiuPQoHIfVWEgfUaZjl9jOBMhXm +iXJAA4RQLSH35cj8OntuEegE4ZdW1zTqmGmh3VjWf7hKv8FhpdQTCctVlTCDzOs7 +fWEVmupmgc2xBp6sW6e3ioo4CtPDoT+S23ve56gb7QV6UfMuZPeyFkFNgVCDwRBM +aVXd3WLey3c3D+j4cI+kbtwY8340grA8KALrh2VNbRDFh45vn2c3TAzcfIP2g+Bu +byNuWZ24IqIyhx1ottLo9KvtPM7S4vcNAuVtiVon3WLeRQlR3UZ316yenJy49NUJ +Q6PsMWHks5fqE9nVWU/AdflWYOJnX3ooi8EK3UD7sQC/21UyF/UFZdGd +-----END CERTIFICATE----- diff --git a/PSA/psaConfigs/ipsec.d/certs/ClientCert.pem b/PSA/psaConfigs/ipsec.d/certs/ClientCert.pem new file mode 100644 index 0000000..9a878a7 --- /dev/null +++ b/PSA/psaConfigs/ipsec.d/certs/ClientCert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEMTCCAhmgAwIBAgIIPumjqfUrjEIwDQYJKoZIhvcNAQEFBQAwOTELMAkGA1UE +BhMCRVMxEDAOBgNVBAoTB1NlY3VyZWQxGDAWBgNVBAMTD1NlY3VyZWQgUm9vdCBD +QTAeFw0xNjAyMjkxNjIyMjlaFw0xODAyMjgxNjIyMjlaMDgxCzAJBgNVBAYTAkVT +MRAwDgYDVQQKEwdTZWN1cmVkMRcwFQYDVQQDFA5wc2FAc2VjdXJlZC5ldTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALUgqBMyLno3i4cBqByZJCpcZVsr +dvn9fSu2KsImPcl8Nh7m6fymspzyyV+G6ZwDCKL+HA7rzpC+SBf6I0/cj03FhCmo +eSxBCLn7ZICvb5OLCmXuOdrP5j7TMyH5fEGMOWoAQKL4OZlvDqWQO4FhSiGvY24m +e+cb81tkTRb34FxhzqA6fB/vVpusM7WygGhAI7AkA4UdmFG2cOa0UN+0d9XXUr7z +l8DZYxqvVd0UPCxN7Z4vs5+19ge9UMs9TEz1jmTLBtNfVdOKqflQf8PObWBkG2EC +ddWLQ57IrJeNaD4+t1VsXk+H45n/Zl0BIOgiWF9lW3qtEfZkqaMIvr7prt8CAwEA +AaM+MDwwHwYDVR0jBBgwFoAUMPENpHawLXFavG47snr+cfvNR5QwGQYDVR0RBBIw +EIEOcHNhQHNlY3VyZWQuZXUwDQYJKoZIhvcNAQEFBQADggIBALXhl4J3IvoMLuWT +X8ofgyg0M0D+Je0y/P1yjajQ6jTmszlH9E2j7vPCus598CCxDHn0URR60kaB6W0F +i4pSqsBo2ctW+8rZpyAhL/Uo3VwZCkSKSFTC5YeJR9zopPxZemQGqxbnD7pKWfTX +r+CMiA1IkoDUoLOh25MaHTn9OWeSouQJH41S1zuq+W7rpZPrABRrMNUqID32eQhK +rPtyTS96TIJTbng02PTk+0FLQLyVrDh4mVR8ZALzyBnAmZGsaIGGKw2ff7P+DgZZ +UlyXM+1F4HRCsGINoDTMkDUZkLUP2gZQfV/bysK3zvZy1UrFjQRbk/76SIwyE4+K +zdf32UcUpwoCy6FAAHSlUbiDVFX9VA70R1ibGnXSiy45CsYOvNGp6rX+zkE1hfva +6gFLiyCXt1ns8/GIw51RzGeCOYqzX0Xo4m6H2frfMBOGnJ3Mq6yaT5q/JUf95f31 +qXSgkmdV0NxYQugrFWAA3+mAtmCUj7qh5zejDDtbV6RGApEAr9w+MARbXd69UDRD +GHTpZLcfTti/czqUOfcUFcYxO6+2qxQXdloTOv0mIppoQ8Waawk7lNpT7cWqxUnU +isZCAEq6CvZ6VPiYoRPeYwrPfOl+/B5Mx6N0Grt5HNFSwzyBnXy8gq0hO873Hy4z +eG898qPQSAlOHnHeiGzPn104HHSh +-----END CERTIFICATE----- diff --git a/PSA/psaConfigs/ipsec.d/certs/vpnHostCert.pem b/PSA/psaConfigs/ipsec.d/certs/vpnHostCert.pem new file mode 100644 index 0000000..ebe9d7b --- /dev/null +++ b/PSA/psaConfigs/ipsec.d/certs/vpnHostCert.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEUDCCAjigAwIBAgIILdCBejeojQcwDQYJKoZIhvcNAQEFBQAwOTELMAkGA1UE +BhMCRVMxEDAOBgNVBAoTB1NlY3VyZWQxGDAWBgNVBAMTD1NlY3VyZWQgUm9vdCBD +QTAeFw0xNjAyMjkxNzIwNTVaFw0xODAyMjgxNzIwNTVaMDgxCzAJBgNVBAYTAkVT +MRAwDgYDVQQKEwdTZWN1cmVkMRcwFQYDVQQDEw52cG4uc2VjdXJlZC5ldTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzmpbOKgqqq7ahSVDybx7PGNEdL +ltVQXTFZbMKqr3tEmOqdU+42PRbO02PtVxx+a+hIqIFcBMMK9meuMtFM+es2GdKN +k5UC/sPDTBxDAD3o3s2MQh5gLrHaEIgSYJSoy0jsuY2mfwLDQNsskvV/hcTROi6s +outxEYT/oUWrsXSt+hOO/J+Eh21ItHlPyNQXVBYalxWN+esTVNL/7PfFwSWQT6AG +bTJJ6W+mpX5pdEnl9dbJrduTpMRCRxN7t1UGf7WyZbY+1ZgLfjrcxdtEcJpH1Fsw +r6W+aCYKO/6e86f7orYsRAkf5+hZvtxt33hB8YCq3rN0cLuWeOlrM9Cn/YECAwEA +AaNdMFswHwYDVR0jBBgwFoAUMPENpHawLXFavG47snr+cfvNR5QwGQYDVR0RBBIw +EIIOdnBuLnNlY3VyZWQuZXUwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFCAIC +MA0GCSqGSIb3DQEBBQUAA4ICAQAI4vR/sjwFVocDqjf64DvXVsVmo+5iinc9dU96 +l/YCheC8ZXGyMbeuzTZ2b3xkYFQXGFJ1qcFse0VBGec4aj1d79vw7mIixcbsVSRZ +jHqSY6LaqGWnmguDUmYViG+qoKJAh6N3802kDJw9/ovv22B2WssktLMbYvx0no7c +wt8dmlK2OCyEAUN6SzlItwYSdPtI5MjpTvDAKYGHP5NfEsPh1zrOr3Ll+wEzLfzg +MvZRiBJIjsCe9YBOalndi9HTm6dTvTtPVT6ZJX9tPdXfeyQ2sqXqU6UF819LDnnp +/b5+dADiAGbj3+Kj0c3rjc/dS15WHp1qJoTPUpxngcJDqELAFaNB1H2tCg9a9BwM +9ke8YX8wV+DBMzlLGfPT2ijfJrlcuc/AszgnH7Y0npKAFFVMLOR8wfh5gaBduB9I +rYEPrN3mn+El/AFYOKohKNXY9BJ+W3ZruNFZMYmxbCdmzPhFt4AYgumFkBNFI3XV +tsTxARLhmwfeuwaqbq5bBfkFAfRO0RGlsfz1HeYPeyIlGoBu+VzeDEKPXqpJQ02b +s8hwgNhawqjwN6lba1T5XJnKxgXcY1Duf9ohQZ94wLcP8fvKO9OqOuyww5lF88LP +4DytW81hUAmoJug0nwmdUmLB5Pv1J8AgXW7OtpBauQaeeByckWSvh685DFSYa48l +IyFUKQ== +-----END CERTIFICATE----- diff --git a/PSA/psaConfigs/ipsec.d/private/ClientKey.pem b/PSA/psaConfigs/ipsec.d/private/ClientKey.pem new file mode 100644 index 0000000..a2ad836 --- /dev/null +++ b/PSA/psaConfigs/ipsec.d/private/ClientKey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAtSCoEzIuejeLhwGoHJkkKlxlWyt2+f19K7YqwiY9yXw2Hubp +/KaynPLJX4bpnAMIov4cDuvOkL5IF/ojT9yPTcWEKah5LEEIuftkgK9vk4sKZe45 +2s/mPtMzIfl8QYw5agBAovg5mW8OpZA7gWFKIa9jbiZ75xvzW2RNFvfgXGHOoDp8 +H+9Wm6wztbKAaEAjsCQDhR2YUbZw5rRQ37R31ddSvvOXwNljGq9V3RQ8LE3tni+z +n7X2B71Qyz1MTPWOZMsG019V04qp+VB/w85tYGQbYQJ11YtDnsisl41oPj63VWxe +T4fjmf9mXQEg6CJYX2Vbeq0R9mSpowi+vumu3wIDAQABAoIBAHRaih3qupigXe1Y +Txov9l+QAzxR65gkEuilmUonLsHkHRA03lMC8vKHtHy9OgySllW+T1/2czfgRIfC +lDSVRyl6nK/2HgEjtetjZuiTymVJiGB6bIf3zbzGB67nib7ByZAioWMPelDqWspY +oSE30ltZQ9JavnV0Kxymji8XBPXSejT7hRRp3/eSGaXFxI0YOk4SDcEwnr1gA+bb +BpFIfFhvB80uMBk+iwHrY7fLoznXww7oZX4U9jGAixfYd6K7jtzR4Vj0toMkX1ww +FB2Hn1xlHNPjg2ldVom3p0MaWwJJ8DLaxWv7at4HO+kpwI9vDIgu41fBTps5d6L8 +O4dGGCECgYEA189T+qrb6fRyNEOkeZWf4GeUcgrxXwqOovAB9D7p2RHoSJ1h1cYa +VWgIohHC/J1uWiiKUgeLhBAl3pcG9wMWUKC4am4K4/yCvCjOOoZX5Zdw6+HVnHFv +PAk1fd4i9mkEzJxVtXJxu80LfKc2hH4Geq8Xs4EE3q+S58ATxb90H5UCgYEA1tvc +Dv5WT3sqOqYJZCIC/mL5NEzeai3wqA0II/pUkhAqe9DUP8gdLqDheyzVFclT6Frk +3Kdmw8Akilq34X3Of1BvuwzIDio/MzRoMb9fu/tq2PMGFR+DkspwnQ+nzIFayVjS +/cI0WOyiB6ADIJQkNX/hM92C5hJzj6geywGKh6MCgYBHFAkTyUxvDMzEe/bi+K3U +iijxOrtu0xpRff0WxdXdYbGAoR1E/F9V+9LEFleDPhLHbQzJoaSI1YyzeEiZ+JFT +8utqWl4J4vPoJwRtcCvo+Wz+s73YLeA2BM5ya0RWphYnkeIExfHBqfH7l1M0ZhGa +PKrwuzCwa2FWJQQeIEWN1QKBgCvw3OloFIi+vJ0v9b23wvr5jNOoYNhAOvZza9XH +zWHt0nJt++prZ6RwnIyPV6jT+sgLRsDlr3ubIR32faKtEv0wmxka/RMAitpS/ngm +FlMgkPJ7iSPqxQLRSgSk/gEx9zo0YzoobII/Ksf6bolMIreaRplP1QRug5m+nUWR +NB6XAoGAALSMJlqZq9UqIu22/vwIhP2PcZPjrFif+QkdU6f3cyJZ4mzPr7rQjCJP +fADhA5McGkTjh4kBTN7Bb0PTnxrUDYpAeWgkgwozZEKAp92g8UUtWkjK2KyIwQ9f +n+xD50NWlsgf/EGVPzh1RFGD/fl6Yfc6tCYK+wWctSLDbWtxgzc= +-----END RSA PRIVATE KEY----- diff --git a/PSA/psaConfigs/ipsec.d/private/vpnHostKey.pem b/PSA/psaConfigs/ipsec.d/private/vpnHostKey.pem new file mode 100644 index 0000000..e21fe7b --- /dev/null +++ b/PSA/psaConfigs/ipsec.d/private/vpnHostKey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEArsN9VEcJOJ+kxHYUdQ/3ih9gwRg/NrfK9mgh7IAt73SizXR/ +XYTQxkfQnma1m6R4OiTcQSbseNYKRRpbHbNBp2pN2tZ3PmMGztKdpxPE+ORu+65T +eKnzUJcIbI4n0axC76YtbFzS2JiFIlyNz+o8GJcvsJ1WMo3yjjLLopzOnOqzpi50 +Dy8OPpb2orORAVWp7NA+ySYrxPSH26zbGEv4fSmGBT/Zr/CcSyTZo7i/MnMnCVnr +FglFQSqrY6Zrmf50XCCYVcc28SmJfKoh+S9kAzZIzP7dvUp9AFQQgFb48scxMSnC +42yE2EgQW5DoX1XmOa1E3nSVpa0Q8FhkxQT/SQIDAQABAoIBAGtO6/jAuX/EPtbH +Ll6G1FdCVxWZvs7pYb2VSSc/uXTr6D9Bhic1M0BcvlNU+7Q68/u3wGpc7RxxSNMP +kN0WwXx7wJxbzdJH2dlK57S8Zdjx4te1Pdm8eQOGCY0maHrNTDh8kC2WfXyzgiQM +MqN0zzvGMWk/4rqeAE6iD/mkURV13+IUfY1Wl9IxHHugCBQppu7vsEWCarBS1eQm +qXjrdyF+929OT6vJRWvambOofcuYSM3adL6d74F4wk2Zyy/RcUMtgld+aZzzJ1qo +196spOEs6iWxXDziFHk6rBZoBqfeFJIMtaeBL1pvzdGlNVVm5RNl2a6cUaGDL8SU +CHFJIhECgYEA05IUpCIR9FQASl8xRWbUql/hktRIpP/8HeA7A/ryAF+K8xsfuXjf +gcsz1fYhpk11c3K2f4l5+4GINTjcObQ/jImWPuoInV5gFqr5Bv7bZhs7OU1dyrvO +W7nR/k0688h51XFQ8EQVU0VcRzfU1dW3Yf/SLZ/aUmtepEsy5FRt4j0CgYEA03av +bab4DmPwGDkSktPbJ71kPYxm/pl459kTTun396OJ0PIKgK7QGuFz+OF0qJxG0Ew7 +CLUSp+LQHuh+hjEqatMJwE1CzrXKUXDLw4bq8cNM9WluGt+myGAehyvTGjD1Sn7n +viSRVxjyndOT0n9CzboVBqnAkI+xoGLVbV8xnf0CgYBe9z4uomBexGnQ+EOcfFjP +FPSivIjTD1gybNjwo26G/lvXXYBy9y+UCgr9alqWVER5Cq+iNao8W1sxUJGBkUfJ +JGT9Xn15bchBxIK9Qh5nCagew3xrKLuq7pC0ziO9E2qkhRWok1bjJsoR6ZyuUxza +b0L/WbZcsncr1dlFJiSgnQKBgQCq/sc+8QyXfmLRA52sU1pdWT3ss1M5v9rQCG4f +mvykKH8yEfdCNZockoDYGV/fVHfCylHWaa3LSpKm27QsSSeWuE6jTRaf1sribkan +NrtXgmkB7h5V5tKUJ32oKl0LSdmgQtycmi9Y8diGnWdkiPn5J+cGu0/21et52VHi +nl6d9QKBgQC6D13Jskxe8xiGYaTwCchJZgoyw+40Mdk6VwbfFbg76LZtFog3q3d3 +ECt14ymqueGs6sJG76cKK9XLk1oQY0z+R1XHp2E1QBlz+cezThLA5tW3IIqfYnyV +t5isXbUL14L7/r6EZ1mFf+vMs2Np8g4k1IhKf16aQuv/cMeN/ZBrxA== +-----END RSA PRIVATE KEY----- diff --git a/PSA/psaConfigs/psaconf b/PSA/psaConfigs/psaconf new file mode 100644 index 0000000..53d395d --- /dev/null +++ b/PSA/psaConfigs/psaconf @@ -0,0 +1,20 @@ +{ + "default": { + "keyexchange": "ikev2", + "rekeymargin": "3m", + "keyingtries": "3", + "ikelifetime": "60m" + }, + "psa": { + "rightsubnet": "0.0.0.0/0", + "leftfirewall": "yes", + "leftsourceip": "%config", + "auto": "add", + "left": "%any", + "leftsubnet": "10.2.3.0/24", + "leftcert": "ClientCert.pem", + "rightid": "@vpn.secured.eu", + "leftid": "psa@secured.eu", + "right": "147.83.42.207" + } +} diff --git a/PSA/psaEE.conf b/PSA/psaEE.conf new file mode 100644 index 0000000..9067c06 --- /dev/null +++ b/PSA/psaEE.conf @@ -0,0 +1,10 @@ +[configuration] +psc_address=http://192.168.2.1:8080 +psa_config_path=/home/psa/pythonScript/psaConfigs +scripts_path=/scripts/ +psa_id=strongswan +psa_name=CorporateVPN PSA +psa_version=0.1.0 +psa_api_version=v0.5 +psa_log_location=/home/psa/pythonScript/psaConfigs/psa.log +conf_id= diff --git a/PSA/psaEE.py b/PSA/psaEE.py new file mode 100644 index 0000000..5d933a5 --- /dev/null +++ b/PSA/psaEE.py @@ -0,0 +1,90 @@ +# +# File: psaEE.py +# Created: 27/08/2014 +# Author: BSC +# +# Description: +# Web service running on the PSA interacting with the PSC +# +# + +import falcon +import json +import Config +import logging +import subprocess +from execInterface import execInterface +from getConfiguration import getConfiguration +from psaExceptions import psaExceptions +from dumpLogFile import dumpLogFile + + +#old +conf = Config.Configuration() +date_format = "%m/%d/%Y %H:%M:%S" +log_format = "[%(asctime)s.%(msecs)d] [%(module)s] %(message)s" +logging.basicConfig(filename=conf.LOG_FILE,level=logging.DEBUG,format=log_format, datefmt=date_format) + +#older logging +#logging.basicConfig(filename=conf.LOG_FILE,level=logging.DEBUG,format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p') + +pscAddr = conf.PSC_ADDRESS +configsPath = conf.PSA_CONFIG_PATH +psaID = conf.PSA_ID +#confID = conf.CONF_ID + +logging.info("--------") +logging.info("PSA EE init.") +logging.info("PSA ID: " + str(psaID)) +logging.info("PSA NAME: " + str(conf.PSA_NAME)) +logging.info("PSA VERSION: " + str(conf.PSA_VERSION)) +logging.info("PSA-PSC API version: " + str(conf.PSA_API_VERSION)) +logging.info("PSA log location: " + str(conf.PSA_LOG_LOCATION)) +logging.info("--------") + +# instantiate class object to manage REST interface to the PSC +execIntf = execInterface(configsPath, conf.PSA_SCRIPTS_PATH, conf.PSA_LOG_LOCATION, psaID) +#confHand = getConfiguration(pscAddr, configsPath, confID, psaID) +confHand = getConfiguration(pscAddr, configsPath, conf.PSA_SCRIPTS_PATH, psaID, str(conf.PSA_API_VERSION)) + +# start the HTTP falcon proxy and adds reachable resources as routes +app = falcon.API() +#app.add_route('/execInterface', excIntf) +app.add_route("/" + str(conf.PSA_API_VERSION) + '/execInterface/{command}', execIntf) + +dumpLog = dumpLogFile() +#FOR DEBUGGING ONLY, REMOVE IN PRODUCTION +app.add_route("/" + str(conf.PSA_API_VERSION) + '/execInterface/dump-log-ctrl', dumpLog) + + +logging.info("execInterface routes added.") + +# Inform our PSC that we are up +#TODO +''' +try: + start_res = confHand.send_start_event() + # We don't need to enable anything + #proc = subprocess.Popen(confScript, stdout=subprocess.PIPE, shell=True) + #(out, err) = proc.communicate() +except psaExceptions as exc: + pass +''' +# Pull configuration and start the PSA. +try: + confScript = confHand.pullPSAconf() + execIntf.callStartScript() +except psaExceptions as exc: + pass + +logging.info("PSA start done.") + +# http request to ask for the configuration and start the script +''' +try: + confScript = confHand.pullPSAconf() + proc = subprocess.Popen(confScript, stdout=subprocess.PIPE, shell=True) + (out, err) = proc.communicate() +except psaExceptions as exc: + pass +''' diff --git a/PSA/psaExceptions.py b/PSA/psaExceptions.py new file mode 100644 index 0000000..63adb35 --- /dev/null +++ b/PSA/psaExceptions.py @@ -0,0 +1,13 @@ +# +# File: psaExceptions.py +# Created: 05/09/2014 +# Author: BSC +# +# Description: +# Custom execption class to manage error in the PSC +# + +class psaExceptions(): + + class confRetrievalFailed(Exception): + pass diff --git a/PSA/scripts/current_config.sh b/PSA/scripts/current_config.sh new file mode 100644 index 0000000..e80838e --- /dev/null +++ b/PSA/scripts/current_config.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# +# current_config.sh +# Created: 03/01/2016 +# Author: Diego Montero +# +# Description: +# This script return the current squid3 configuration. +# (psaConfigs/psaconf) +# +# This script is called by the PSA API when the PSA's current runtime configuration is requested. +# +# Return value: +# Current configuration +# + +COMMAND_OUTPUT="$(cat /etc/ipsec.conf)" +printf '%s\n' "${COMMAND_OUTPUT[@]}" +exit 1; diff --git a/PSA/scripts/init.sh b/PSA/scripts/init.sh new file mode 100644 index 0000000..2038259 --- /dev/null +++ b/PSA/scripts/init.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# +# init.sh +# Created: 03/01/2016 +# Author: Diego Montero +# +# Description: +# + +# + +sysctl net.ipv4.ip_forward=1 +sysctl net.ipv6.conf.all.forwarding=1 + +ipsec stop + +echo "flushing routing cache" +ip route flush cache + +## interface facing clients +CLIENT_IFACE=eth0 + +## interface facing Internet +INET_IFACE=eth1 + +ebtables -P FORWARD DROP +ebtables -A FORWARD -p IPv4 -j ACCEPT +ebtables -A FORWARD -p ARP -j ACCEPT +ebtables -t broute -A BROUTING -i $CLIENT_IFACE -p ipv4 -j redirect --redirect-target DROP +#ebtables -t broute -A BROUTING -i $INET_IFACE -p ipv4 -j redirect --redirect-target DROP + +# Parse json and set the ipsec configuration +JSON_CONF_FILE=/home/psa/pythonScript/psaConfigs/psaconf +DEFAULT_CONF_FILE=/home/psa/pythonScript/defaultIpsecConf +IPSEC_CONF_FILE=/etc/ipsec.conf +python /home/psa/pythonScript/parseConfig.py $JSON_CONF_FILE $DEFAULT_CONF_FILE $IPSEC_CONF_FILE + +echo 1; +exit 1; diff --git a/PSA/scripts/ip_conf.sh b/PSA/scripts/ip_conf.sh new file mode 100644 index 0000000..de7af4d --- /dev/null +++ b/PSA/scripts/ip_conf.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# +# ip_conf.sh +# +# This script is called by the PSA API when the PSA should be configured with IP address. +# NOTE: This script is called right after init.sh script at the start-up of a PSA. +# !!! +# This should have the base setup for IP. init.sh should not change these values, since it +# will overwrite these values at the moment. +# !!! +# --> (We can change the logic to call this after init.sh always?) +# + +# Please, define the interface this PSA requires the IP for. +CLIENT_IFACE=br0 +if [ "$#" -ne 4 ] +then + echo "Illegal number of params. Should be 4 (IP, gateway, dns, netmask)" + exit 1; +fi + +echo "-------------" +echo "IP:" + $1 +echo "gateway:" + $2 +echo "dns:" + $3 +echo "netmask:" + $4 + +# Note that now we just replace any existing conf, since this should be the only DNS for the PSA. +SEARCH='nameserver '$3 +if grep -Fxq "$SEARCH" /etc/resolv.conf +then + echo "Had dns already" +else + echo "Didn't have dns, setting" + echo -e "$SEARCH" > /etc/resolv.conf +fi + +/sbin/ifconfig $CLIENT_IFACE $1 netmask $4 +ip route delete default +/sbin/route add default gw $2 $CLIENT_IFACE + diff --git a/PSA/scripts/ping.sh b/PSA/scripts/ping.sh new file mode 100644 index 0000000..2fac369 --- /dev/null +++ b/PSA/scripts/ping.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# +# status.sh +# +# This script is called by the PSA API when the PSA is requested to ping. +# +# Return value: +# ping result +# + +COMMAND_OUTPUT="$(ping -c 3 www.google.com)" +echo ${COMMAND_OUTPUT} +exit 1; + diff --git a/PSA/scripts/start.sh b/PSA/scripts/start.sh new file mode 100644 index 0000000..76163ae --- /dev/null +++ b/PSA/scripts/start.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# +# start.sh +# Created: 03/01/2016 +# Author: Diego Montero +# +# Description: +# Start script for the PSA-squid +# +# This script is called by the PSA API when the PSA is requested to be started. + +# Load PSA's current configuration + +############################################################## + +ipsec stop + +ipsec start + +sleep 1 + +if P=$(ipsec status psa | grep INSTALLED) +then + echo "ipsec already started" + exit 1 +else + ipsec up psa + echo "ipsec started" +fi + +exit 1; + diff --git a/PSA/scripts/status.sh b/PSA/scripts/status.sh new file mode 100644 index 0000000..33c354c --- /dev/null +++ b/PSA/scripts/status.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# +# status.sh +# Created: 03/01/2016 +# Author: Diego Montero +# +# Description: +# Script that returns the current status of the PSA-squid. +# TBD the expected returned result (this is an example)... +# +# This script is called by the PSA API when the PSA's runtime status is requested. +# +# Return value: +# 1: PSA is alive +# 0: PSA not running correctly. +# +#SERVICE=squid3 +#if P=$(pgrep $SERVICE) +#then +# echo 1 +# exit 1 +#else +# echo 0 +# exit 0 +#fi + +echo 1 +exit 1 diff --git a/PSA/scripts/stop.sh b/PSA/scripts/stop.sh new file mode 100644 index 0000000..00d8a8b --- /dev/null +++ b/PSA/scripts/stop.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# +# stop.sh +# Created: 03/01/2016 +# Author: Diego Montero +# +# Description: +# This script stops squid3 and clear the configuration environmet. +# +# This script is called by the PSA API when the PSA is requested to be stopped. +# + +ip route flush cache + +########################################################## +echo "flusing ebtables" +ebtables -F +ebtables -X +for T in filter nat broute; do ebtables -t $T -F; ebtables -t $T -X; done +ebtables -P INPUT ACCEPT +ebtables -P FORWARD ACCEPT +ebtables -P OUTPUT ACCEPT + +echo "stopping ipsec" +ipsec stop + +echo "PSA Stopped" +exit 1; + diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..e80fcb1 --- /dev/null +++ b/Readme.md @@ -0,0 +1,239 @@ +# 1. End-user + +## 1.1 Description / general readme + + +Most of the enterprises have more than one branch office and is very common to communicate them using an VPN. Corporate VPN PSA main point is to offer the user the possibility of having access to a server in the same enterprise, using an encrypted VPN tunnel. The tunnelling technology used for this PSA is Strongswan. + +![Corporate VPN PSA schema](images/end_user_psa_vpn_schema.png) + +## 1.2 Features / Capabilities + +The capabilities extracted from manifest: + +* Confidentiality protection +* Integrity protection +* IPSec protocol + +The most important features are: +* Strongswan tunnels +* IPsec default encription protocol using certificates + +## 1.3 Security policy examples + + +``` +father;prot_conf_integr;Internet_traffic +``` + +* Enables the integrity protection + +``` +father;prot_conf;Internet_traffic +``` + +* Enables the confidentiality protection + +``` +father;prot_integr;Internet_traffic +``` + +* Allows the IPsec protocol for the tunelling + + +## 1.4 Support, troubleshooting / known issues + +We totally supose both the server and the PSA have the correct certificates. For this reason, the only parameter the user must specify is the server ip address. + +If there is any problem please ask your admin. + +# 2. Developer / admin + +## Description / general readme + +The Corporate VPN PSA offers a secured tunnel to an enterprise server using the Strongswan technology. + +## Components and Requirements + +VM technology allows creating a full system for the PSA. The components used in this PSA are: + +* Operative System: Debian 7 "wheezy" +* iptables +* ebtables +* jq +* brigde-utils +* Strongswan 5.1.2 + +From the PSA configuration it is needed to have an IP address in the PSA belonging to the same LAN than End user. + +For the correct performance of the secured tunnel it is needed to have the VPN server well configured using the proper certificates and Strongswan configuration. For the ipsec.conf file you can follow the next example: + +``` +# /etc/ipsec.conf - strongSwan IPsec configuration file +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=3 + keyexchange=ikev2 + + # various keepalive settings + dpdaction=clear + dpddelay=300s + left=[admin must put the server IP address] + leftfirewall=yes + leftsubnet=0.0.0.0/0 + leftcert=clientCert.der + leftid=[must correspond to the certificate id] +conn psa + right=%any + #rightdns=10.31.0.1,8.8.8.8,8.8.4.4 + rightdns=8.8.8.8,8.8.4.4 + rightid=[must correspond to the certificate id] + rightsourceip=[local NED ip subnetwork] + rightsubnet=[local user ip subnetwork] + auto=add + rightfirewall=yes + +``` + +## Detailed architecture + +There are several components in the internal architecture: + +* **Inspect and route traffic**. **ebtables** is used to set up rules to inspect Ethernet frames between eth0 and eth1 and force the traffic to be routed instead of being just bridged. By this, the traffic will be routed through the Strongswan tunnel. + +* **Use the tunnel**. **Strongswan** passes all the traffic through the IPsec tunnel, except the 10.0.0.0/8 LAN. This LAN is needed to communicate with the NED directly. + +The next figure shows the internal communication with/from the PSA: + +![Corporate VPN PSA schema](images/psa_vpn_schema.png) + +### Rules + +Only the traffic going to the 10.0.0.0/8 stays in the local LAN, all the other traffic passes through the tunnel. + +### Certificates + +For the correct performance the certificates must be exchanged between the client and server of the Strongswan tunnel. For their generation you can follow this [tutorial](https://wiki.strongswan.org/projects/1/wiki/SimpleCA) from the Strongswan site. + +In our case, we used our own server (147.83.42.207) which already has the correct certificates for the PSA image. You can access to both the [server](strongswan/server) and [client](strongswan/psa) certificates. + + +## Virtual machine image creation + +The procedure to create a valid PSA image from scratch start with the prerequisite instructions defined in [PSA Developer guide](https://github.com/SECURED-FP7/secured-psa-develop-test) to obtain a valid base image for PSA. + +Install the software Strongswan 5.1.2: + + sudo apt-get install strongswan + +Copy the necessary [files](PSA) of this project in the folder: + +``` +$HOME/phytonScript/ +``` + +## Mobility Support +This PSA supports the mobility scenario. + +## Support, troubleshooting / known issues + +If there are any known issues, list them + +We totally supose both the server and the PSA have the correct certificates. For this reason, the only parameter the user must specify is the server ip address. + +## Files required + +No extra files required. + +### PSA application image + +PSA is based on a Virtual machine image in KVM- kernel module format ".qcow2". A [sample image has been included](https://vm-images.secured-fp7.eu/images/priv/corporate-vpnPSA.qcow2) in the project. + +### Manifest + + +* XML + +The PSA manifest in format XML is available at [Manifest](NED_files/TVDM/PSAManifest/corporate-vpnPSA.xml). +This file must be stored in the PSAR. And reflects the capabilities described below. + +* JSON +The PSA manifest is available at [Manifest](NED_files/TVDM/PSAManifest/corporate-vpnPSA). + + +### HSPL + +The HSPL format is defined as follows: + +* D4.1 format: + +``` +father;prot_conf_integr;Internet_traffic + +father;prot_conf;Internet_traffic + +father;prot_integr;Internet_traffic +``` + +* More friendly: + +``` +I enable the integrity protection on my internet traffic + +I enable the protection on my internet traffic + +I accept the IPSec protocol as my tunneling technology on my internet traffic +``` + +### MSPL + +An example of MSPL for this PSA are accesible at SPM project: [UPC_server_connection_MSPL](https://github.com/SECURED-FP7/secured-spm/blob/master/M2LService/code/M2LPluginStrongswan/test_conf.mspl.base64) + +### M2L Plug-in + +The M2l plug-in is available at [M2LPlugin](https://github.com/SECURED-FP7/secured-spm/blob/master/M2LService/code/M2LPluginStrongswan/src/eu/securedfp7/m2lservice/plugin/M2LPlugin.java) + +Current version of this plugin will generate a low level configuration like [this one](NED_files/TVDM/psaConfigs/corporate-vpnPSA/corporate-vpn_user8) + +This plugin do not need additional external information in this version that must be store in the PSAR. + + +## Features/Capabilities + +The capabilities extracted from manifest: + +* Confidentiality protection +* Integrity protection +* IPSec protocol + +The most important features are: +* Strongswan tunnels +* IPsec default encription protocol using certificates + +## Testing + +Testing scripts are available at [test folder](tests/) + + +# 3. License + +Please refer to project LICENSE file. + +This software incorporate only Strongswan which is an open source software licensed under the [GNU GPL](http://www.gnu.org/licenses/gpl-2.0.html). + +# Additional Information +## Partners involved + +* Application: UPC +* MSPL: POLITO,UPC +* M2L Plugin: UPC + +# Status (OK/No/Partial) -*OK*- + +# TODO: +* Tests + diff --git a/copy_psa_sw_to_vm.sh b/copy_psa_sw_to_vm.sh new file mode 100644 index 0000000..676dcc8 --- /dev/null +++ b/copy_psa_sw_to_vm.sh @@ -0,0 +1,31 @@ +# Uses libguestfs - Installation guide: http://www.libguestfs.org/ +# +# Run this as sudo and run this file from the folder that contains PSC folder. +# +# Make sure that: +# 1) SW_PATH directory exists in the target IMG. +# 2) Make sure that intefaces and boot_script_psa have executable permission (+x). +# +# WARNING: Using this on live virtual machines can be dangerous, potentially causing disk corruption! The virtual machine must be shut down before using this script! + +IMG="/var/lib/libvirt/images/corporate-vpnPSA.qcow2" +SW_PATH="/home/psa/pythonScript/" + +# Copy python files +echo -n "copy PSA SW... " +virt-copy-in -a $IMG PSA/* $SW_PATH +echo "done." + +# Copy interfaces file +echo -n "copy interfaces... " +virt-copy-in -a $IMG PSA/interfaces /etc/network/ +echo "done." + +# Copy boot script that is executed when interfaces are up +echo -n "copy boot_script_psa... " +virt-copy-in -a $IMG PSA/boot_script_psa /etc/network/if-up.d/ +echo "done." + +#Copy the hostname +virt-copy-in -a $IMG hostname /etc/ +virt-copy-in -a $IMG hosts /etc/ diff --git a/copy_psa_to_ned.sh b/copy_psa_to_ned.sh new file mode 100644 index 0000000..0780c71 --- /dev/null +++ b/copy_psa_to_ned.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# +# This script copies the PSA codes into the NED v0.6 implementation folders, namely: +# 1) PSCM/userList +# 2) TVDM/psaConfigs/[psaID] folder +# 3) TVDM/PSAManifest/[psaID] file +# 4) TVDM/userGraph/[psaID] file +# +# One parameter is required - the full path to your destination NED v0.5.1 dir, e.g., /home/ned/NED/. +# Note: This will overwrite existing configurations for the PSA_ID and the USER inside your NED! + +if [ $# -ne 1 ] ; then + echo "Usage: $0 [Full path to NED directory where the PSA files are to be copied (e.g., /home/ned/NED/)]" + exit 1 +fi + +NED_VERSION=v0.5.1 + +# Note: If you use this for other PSAs, please rename the PSA_ID as such (the config folders have to match in NED_files/TVDM/)! +PSA_ID="strongswan" +USER="user8" +PW=" secuser" +PSCM_PATH=$1PSCM/ +NED_PATH=$1 +USER_LIST=userList + +TEMPLATES=NED_files_template/TVDM/ + +if [ ! -f $PSCM_PATH$USER_LIST ]; then +echo "$PSCM_PATH$USER_LIST file does not exist." +echo "Usage: $0 [full path to NED directory where the PSA files are to be copied (e.g., /home/ned/NED/)]" + exit 1 +fi + +# 1 +################################################################################# +echo "Checking if PSA user exists in PSCM/userList" +user_pw=" secuser" +user_cred=$PSA_ID$user_pw + +if grep -q "$USER" $PSCM_PATH"$USER_LIST"; then + echo "User existed in PSCM/userList, skipping creation of new user." +else + echo "User not in PSCM/userList file, creating new user." + echo $USER$PW >> $PSCM_PATH$USER_LIST +fi + +# 2 +################################################################################# +echo "Copying PSA files into NED $NED_VERSION folders" +cp -avr NED_files/TVDM $NED_PATH diff --git a/hostname b/hostname new file mode 100644 index 0000000..7bf26d1 --- /dev/null +++ b/hostname @@ -0,0 +1 @@ +PSA-corporatevpn diff --git a/hosts b/hosts new file mode 100644 index 0000000..c6c3c79 --- /dev/null +++ b/hosts @@ -0,0 +1,7 @@ +127.0.0.1 localhost +127.0.1.1 PSA-corporatevpn + +# The following lines are desirable for IPv6 capable hosts +::1 localhost ip6-localhost ip6-loopback +ff02::1 ip6-allnodes +ff02::2 ip6-allrouters diff --git a/images/end_user_psa_vpn_schema.png b/images/end_user_psa_vpn_schema.png new file mode 100644 index 0000000000000000000000000000000000000000..89db0f52a3af921252ca97c1d785812240eb5b51 GIT binary patch literal 40653 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 BUh@C| literal 0 HcmV?d00001 diff --git a/strongswan.xml b/strongswan.xml new file mode 100644 index 0000000..467ec9f --- /dev/null +++ b/strongswan.xml @@ -0,0 +1,130 @@ + + + + + strongswan + Strongswan PSA + Most of the enterprises have more than one branch office and is very commun to communicate them using an VPN. Corporate VPN PSA main point is to offer the user the possibility of having access to a server in the same enterprise, using an encrypted VPN tunnel. The tunnelling technology used for this PSA is Strongswan. + https://www.secured-fp7.eu/ + 1.00 + UPC + Alicia Vila + + GPLv2 + + + + + + + Tunnelling + strongswan + encrypted tunnel + + + + + Protection_confidentiality + Protection_integrity + IPSec_protocol + + + + + corporate-vpn + Secured tunnel + This plugin converts MSPL to IPSec configuration + + corporate-vpn_M2L_plugin + Corporate VPN M2LPlugin + http://195.235.93.146:8080/v1/PSA/M2Lplugins/PSA-corporate-vpn + none + + corporate-vpn_M2L_plugin.jar + + + + + 10 + + + 10 + + 2 + 10 + + + + + + + 1 + x86_64 + Intel + + 1 + + + 2 + + + 10 + + + 2000 + + + + Debian + 7.0 + x86_64 + + + + + cold migration + stateless + + + + + + img + + corporate-vpn_M2L_plugin + corporate-vpn_M2L_plugin.jar + java + + 1 + + eth0 + 00:00:00:00:00:00 + 1.1.1.1 + 255.255.255.0 + + + eth1 + 00:00:00:00:00:01 + 192.168.1.1 + 255.255.255.0 + + + 1.1.1.250 + 8.8.8.8 + + + + + + 100 + + + 10 + + + 10 + + + diff --git a/strongswan/psa/ipsec.conf b/strongswan/psa/ipsec.conf new file mode 100644 index 0000000..0098b53 --- /dev/null +++ b/strongswan/psa/ipsec.conf @@ -0,0 +1,29 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=3 + keyexchange=ikev2 + +conn my_lan + left=%any + leftsubnet=192.168.2.0/24 #,10.2.0.0/16 + rightsubnet=192.168.2.0/24 #,10.2.0.0/16 + authby=never + type=passthrough + auto=route + +conn psa + left=%any + leftsourceip=%config + leftcert=ClientCert.pem + leftsubnet=10.2.2.0/16 + leftid=psa@secured.eu + leftfirewall=yes + right=147.83.42.191 + rightsubnet=0.0.0.0/0 + rightid=@vpn.secured.eu + auto=add diff --git a/strongswan/psa/ipsec.d/cacerts/strongswanCert.pem b/strongswan/psa/ipsec.d/cacerts/strongswanCert.pem new file mode 100644 index 0000000..00a71e8 --- /dev/null +++ b/strongswan/psa/ipsec.d/cacerts/strongswanCert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFNjCCAx6gAwIBAgIIEkPUG0+7tv8wDQYJKoZIhvcNAQEFBQAwOTELMAkGA1UE +BhMCRVMxEDAOBgNVBAoTB1NlY3VyZWQxGDAWBgNVBAMTD1NlY3VyZWQgUm9vdCBD +QTAeFw0xNjAyMjkxNjE5NTVaFw0yNjAyMjYxNjE5NTVaMDkxCzAJBgNVBAYTAkVT +MRAwDgYDVQQKEwdTZWN1cmVkMRgwFgYDVQQDEw9TZWN1cmVkIFJvb3QgQ0EwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ18n1g4CaO4UmNQBCvVA0hZjs +yY2I1PLhgnQS4tUu6zTb8CqL2qNr/lPh/PuDMhCDnyAdSvO+RrBRBbIKrDet1Ee6 +74lQnrhcN0b+8wQu3xBTwhMpg8iAPduJdDhdzDGrc0sKeUvJ2RnjQmyqNGTvCezl +7F/xb1UKKg1uZGTorqdxYg09v/6rg51/vd1xnjAaMI5aC4zSbmhDMgNowrNdVtW/ +qC7XQ+mgRNqDdf/ac63vnpNbKt8GhPjEVo/pJUSgNThpyBob5DTbSgFDPXxUjUyQ +QMwB4NW4CcxqhzoYdrhZXL/BAi7i/bLliX5ivOzKQHEbmfnwHwIyO39/OoEHFNnE +QC3YjDynFfdyx9LthxeaMFYXFmU/iWtL7qMfZb6IeiV1vkXchC8O/4aYzSqBskHX +XDwojlxMWYKfb9cJSfiOSbaaLvr1Gof2rjQSDEiJhdvynTE/Qf1wCmQDnLBfwZOv +d8mMdpi9g0Bqg3JF9/GuvebRJK8CKLcQM6wvn3W8YrxoFDDkH9ni+eLTRjZcazX1 +UuDL6lnWn2ZzO/ND54VSBwE/hPMcV63iSYWcNJP/Q69T2pXBrTigSBhlwaXuzRIF +/7YmrOwqag7d9lA3mej+jsaygA3L/f1CpI9HWjgZD16S2D83qWjcR66UqNDz8mFf +DPdLOQROZi5u+OLTHQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQUMPENpHawLXFavG47snr+cfvNR5QwDQYJKoZIhvcN +AQEFBQADggIBAFfyR9Q8h4poKaPu4Yh0uKVPDQSnX+xE/xw/acWu6NTck85TYP7G +s3U7+7IcEOaA/JJc5XhKEINQKj5qi28wzQzUg+bBoGAkd2PlfJZrLAULdNKBBjwH +7biMUIZFuPKhIJkgVK45o9gukQZW6azZ6uB0QYRD1usFrmqnERtNSYYv7QR1OuTp +MjHNdyyPok3vrcaYORg5bFmbPeyy/LPvyzHnv9eKR/ykj9Y7nCjBRq2sEa30bwzU +LFsEYmhrZs2Ja2YA9NjUWE6DOZdLrOjQDLHObGxVnLgzRrnwVqq33RJxCHGhcV7A +DjhVSH1qRs0OD/LhRpJIO/n7z/xPAH/T2/UhVpiuPQoHIfVWEgfUaZjl9jOBMhXm +iXJAA4RQLSH35cj8OntuEegE4ZdW1zTqmGmh3VjWf7hKv8FhpdQTCctVlTCDzOs7 +fWEVmupmgc2xBp6sW6e3ioo4CtPDoT+S23ve56gb7QV6UfMuZPeyFkFNgVCDwRBM +aVXd3WLey3c3D+j4cI+kbtwY8340grA8KALrh2VNbRDFh45vn2c3TAzcfIP2g+Bu +byNuWZ24IqIyhx1ottLo9KvtPM7S4vcNAuVtiVon3WLeRQlR3UZ316yenJy49NUJ +Q6PsMWHks5fqE9nVWU/AdflWYOJnX3ooi8EK3UD7sQC/21UyF/UFZdGd +-----END CERTIFICATE----- diff --git a/strongswan/psa/ipsec.d/certs/ClientCert.pem b/strongswan/psa/ipsec.d/certs/ClientCert.pem new file mode 100644 index 0000000..9a878a7 --- /dev/null +++ b/strongswan/psa/ipsec.d/certs/ClientCert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEMTCCAhmgAwIBAgIIPumjqfUrjEIwDQYJKoZIhvcNAQEFBQAwOTELMAkGA1UE +BhMCRVMxEDAOBgNVBAoTB1NlY3VyZWQxGDAWBgNVBAMTD1NlY3VyZWQgUm9vdCBD +QTAeFw0xNjAyMjkxNjIyMjlaFw0xODAyMjgxNjIyMjlaMDgxCzAJBgNVBAYTAkVT +MRAwDgYDVQQKEwdTZWN1cmVkMRcwFQYDVQQDFA5wc2FAc2VjdXJlZC5ldTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALUgqBMyLno3i4cBqByZJCpcZVsr +dvn9fSu2KsImPcl8Nh7m6fymspzyyV+G6ZwDCKL+HA7rzpC+SBf6I0/cj03FhCmo +eSxBCLn7ZICvb5OLCmXuOdrP5j7TMyH5fEGMOWoAQKL4OZlvDqWQO4FhSiGvY24m +e+cb81tkTRb34FxhzqA6fB/vVpusM7WygGhAI7AkA4UdmFG2cOa0UN+0d9XXUr7z +l8DZYxqvVd0UPCxN7Z4vs5+19ge9UMs9TEz1jmTLBtNfVdOKqflQf8PObWBkG2EC +ddWLQ57IrJeNaD4+t1VsXk+H45n/Zl0BIOgiWF9lW3qtEfZkqaMIvr7prt8CAwEA +AaM+MDwwHwYDVR0jBBgwFoAUMPENpHawLXFavG47snr+cfvNR5QwGQYDVR0RBBIw +EIEOcHNhQHNlY3VyZWQuZXUwDQYJKoZIhvcNAQEFBQADggIBALXhl4J3IvoMLuWT +X8ofgyg0M0D+Je0y/P1yjajQ6jTmszlH9E2j7vPCus598CCxDHn0URR60kaB6W0F +i4pSqsBo2ctW+8rZpyAhL/Uo3VwZCkSKSFTC5YeJR9zopPxZemQGqxbnD7pKWfTX +r+CMiA1IkoDUoLOh25MaHTn9OWeSouQJH41S1zuq+W7rpZPrABRrMNUqID32eQhK +rPtyTS96TIJTbng02PTk+0FLQLyVrDh4mVR8ZALzyBnAmZGsaIGGKw2ff7P+DgZZ +UlyXM+1F4HRCsGINoDTMkDUZkLUP2gZQfV/bysK3zvZy1UrFjQRbk/76SIwyE4+K +zdf32UcUpwoCy6FAAHSlUbiDVFX9VA70R1ibGnXSiy45CsYOvNGp6rX+zkE1hfva +6gFLiyCXt1ns8/GIw51RzGeCOYqzX0Xo4m6H2frfMBOGnJ3Mq6yaT5q/JUf95f31 +qXSgkmdV0NxYQugrFWAA3+mAtmCUj7qh5zejDDtbV6RGApEAr9w+MARbXd69UDRD +GHTpZLcfTti/czqUOfcUFcYxO6+2qxQXdloTOv0mIppoQ8Waawk7lNpT7cWqxUnU +isZCAEq6CvZ6VPiYoRPeYwrPfOl+/B5Mx6N0Grt5HNFSwzyBnXy8gq0hO873Hy4z +eG898qPQSAlOHnHeiGzPn104HHSh +-----END CERTIFICATE----- diff --git a/strongswan/psa/ipsec.d/certs/vpnHostCert.pem b/strongswan/psa/ipsec.d/certs/vpnHostCert.pem new file mode 100644 index 0000000..ebe9d7b --- /dev/null +++ b/strongswan/psa/ipsec.d/certs/vpnHostCert.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEUDCCAjigAwIBAgIILdCBejeojQcwDQYJKoZIhvcNAQEFBQAwOTELMAkGA1UE +BhMCRVMxEDAOBgNVBAoTB1NlY3VyZWQxGDAWBgNVBAMTD1NlY3VyZWQgUm9vdCBD +QTAeFw0xNjAyMjkxNzIwNTVaFw0xODAyMjgxNzIwNTVaMDgxCzAJBgNVBAYTAkVT +MRAwDgYDVQQKEwdTZWN1cmVkMRcwFQYDVQQDEw52cG4uc2VjdXJlZC5ldTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzmpbOKgqqq7ahSVDybx7PGNEdL +ltVQXTFZbMKqr3tEmOqdU+42PRbO02PtVxx+a+hIqIFcBMMK9meuMtFM+es2GdKN +k5UC/sPDTBxDAD3o3s2MQh5gLrHaEIgSYJSoy0jsuY2mfwLDQNsskvV/hcTROi6s +outxEYT/oUWrsXSt+hOO/J+Eh21ItHlPyNQXVBYalxWN+esTVNL/7PfFwSWQT6AG +bTJJ6W+mpX5pdEnl9dbJrduTpMRCRxN7t1UGf7WyZbY+1ZgLfjrcxdtEcJpH1Fsw +r6W+aCYKO/6e86f7orYsRAkf5+hZvtxt33hB8YCq3rN0cLuWeOlrM9Cn/YECAwEA +AaNdMFswHwYDVR0jBBgwFoAUMPENpHawLXFavG47snr+cfvNR5QwGQYDVR0RBBIw +EIIOdnBuLnNlY3VyZWQuZXUwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFCAIC +MA0GCSqGSIb3DQEBBQUAA4ICAQAI4vR/sjwFVocDqjf64DvXVsVmo+5iinc9dU96 +l/YCheC8ZXGyMbeuzTZ2b3xkYFQXGFJ1qcFse0VBGec4aj1d79vw7mIixcbsVSRZ +jHqSY6LaqGWnmguDUmYViG+qoKJAh6N3802kDJw9/ovv22B2WssktLMbYvx0no7c +wt8dmlK2OCyEAUN6SzlItwYSdPtI5MjpTvDAKYGHP5NfEsPh1zrOr3Ll+wEzLfzg +MvZRiBJIjsCe9YBOalndi9HTm6dTvTtPVT6ZJX9tPdXfeyQ2sqXqU6UF819LDnnp +/b5+dADiAGbj3+Kj0c3rjc/dS15WHp1qJoTPUpxngcJDqELAFaNB1H2tCg9a9BwM +9ke8YX8wV+DBMzlLGfPT2ijfJrlcuc/AszgnH7Y0npKAFFVMLOR8wfh5gaBduB9I +rYEPrN3mn+El/AFYOKohKNXY9BJ+W3ZruNFZMYmxbCdmzPhFt4AYgumFkBNFI3XV +tsTxARLhmwfeuwaqbq5bBfkFAfRO0RGlsfz1HeYPeyIlGoBu+VzeDEKPXqpJQ02b +s8hwgNhawqjwN6lba1T5XJnKxgXcY1Duf9ohQZ94wLcP8fvKO9OqOuyww5lF88LP +4DytW81hUAmoJug0nwmdUmLB5Pv1J8AgXW7OtpBauQaeeByckWSvh685DFSYa48l +IyFUKQ== +-----END CERTIFICATE----- diff --git a/strongswan/psa/ipsec.d/private/ClientKey.pem b/strongswan/psa/ipsec.d/private/ClientKey.pem new file mode 100644 index 0000000..a2ad836 --- /dev/null +++ b/strongswan/psa/ipsec.d/private/ClientKey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAtSCoEzIuejeLhwGoHJkkKlxlWyt2+f19K7YqwiY9yXw2Hubp +/KaynPLJX4bpnAMIov4cDuvOkL5IF/ojT9yPTcWEKah5LEEIuftkgK9vk4sKZe45 +2s/mPtMzIfl8QYw5agBAovg5mW8OpZA7gWFKIa9jbiZ75xvzW2RNFvfgXGHOoDp8 +H+9Wm6wztbKAaEAjsCQDhR2YUbZw5rRQ37R31ddSvvOXwNljGq9V3RQ8LE3tni+z +n7X2B71Qyz1MTPWOZMsG019V04qp+VB/w85tYGQbYQJ11YtDnsisl41oPj63VWxe +T4fjmf9mXQEg6CJYX2Vbeq0R9mSpowi+vumu3wIDAQABAoIBAHRaih3qupigXe1Y +Txov9l+QAzxR65gkEuilmUonLsHkHRA03lMC8vKHtHy9OgySllW+T1/2czfgRIfC +lDSVRyl6nK/2HgEjtetjZuiTymVJiGB6bIf3zbzGB67nib7ByZAioWMPelDqWspY +oSE30ltZQ9JavnV0Kxymji8XBPXSejT7hRRp3/eSGaXFxI0YOk4SDcEwnr1gA+bb +BpFIfFhvB80uMBk+iwHrY7fLoznXww7oZX4U9jGAixfYd6K7jtzR4Vj0toMkX1ww +FB2Hn1xlHNPjg2ldVom3p0MaWwJJ8DLaxWv7at4HO+kpwI9vDIgu41fBTps5d6L8 +O4dGGCECgYEA189T+qrb6fRyNEOkeZWf4GeUcgrxXwqOovAB9D7p2RHoSJ1h1cYa +VWgIohHC/J1uWiiKUgeLhBAl3pcG9wMWUKC4am4K4/yCvCjOOoZX5Zdw6+HVnHFv +PAk1fd4i9mkEzJxVtXJxu80LfKc2hH4Geq8Xs4EE3q+S58ATxb90H5UCgYEA1tvc +Dv5WT3sqOqYJZCIC/mL5NEzeai3wqA0II/pUkhAqe9DUP8gdLqDheyzVFclT6Frk +3Kdmw8Akilq34X3Of1BvuwzIDio/MzRoMb9fu/tq2PMGFR+DkspwnQ+nzIFayVjS +/cI0WOyiB6ADIJQkNX/hM92C5hJzj6geywGKh6MCgYBHFAkTyUxvDMzEe/bi+K3U +iijxOrtu0xpRff0WxdXdYbGAoR1E/F9V+9LEFleDPhLHbQzJoaSI1YyzeEiZ+JFT +8utqWl4J4vPoJwRtcCvo+Wz+s73YLeA2BM5ya0RWphYnkeIExfHBqfH7l1M0ZhGa +PKrwuzCwa2FWJQQeIEWN1QKBgCvw3OloFIi+vJ0v9b23wvr5jNOoYNhAOvZza9XH +zWHt0nJt++prZ6RwnIyPV6jT+sgLRsDlr3ubIR32faKtEv0wmxka/RMAitpS/ngm +FlMgkPJ7iSPqxQLRSgSk/gEx9zo0YzoobII/Ksf6bolMIreaRplP1QRug5m+nUWR +NB6XAoGAALSMJlqZq9UqIu22/vwIhP2PcZPjrFif+QkdU6f3cyJZ4mzPr7rQjCJP +fADhA5McGkTjh4kBTN7Bb0PTnxrUDYpAeWgkgwozZEKAp92g8UUtWkjK2KyIwQ9f +n+xD50NWlsgf/EGVPzh1RFGD/fl6Yfc6tCYK+wWctSLDbWtxgzc= +-----END RSA PRIVATE KEY----- diff --git a/strongswan/psa/ipsec.d/private/vpnHostKey.pem b/strongswan/psa/ipsec.d/private/vpnHostKey.pem new file mode 100644 index 0000000..e21fe7b --- /dev/null +++ b/strongswan/psa/ipsec.d/private/vpnHostKey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEArsN9VEcJOJ+kxHYUdQ/3ih9gwRg/NrfK9mgh7IAt73SizXR/ +XYTQxkfQnma1m6R4OiTcQSbseNYKRRpbHbNBp2pN2tZ3PmMGztKdpxPE+ORu+65T +eKnzUJcIbI4n0axC76YtbFzS2JiFIlyNz+o8GJcvsJ1WMo3yjjLLopzOnOqzpi50 +Dy8OPpb2orORAVWp7NA+ySYrxPSH26zbGEv4fSmGBT/Zr/CcSyTZo7i/MnMnCVnr +FglFQSqrY6Zrmf50XCCYVcc28SmJfKoh+S9kAzZIzP7dvUp9AFQQgFb48scxMSnC +42yE2EgQW5DoX1XmOa1E3nSVpa0Q8FhkxQT/SQIDAQABAoIBAGtO6/jAuX/EPtbH +Ll6G1FdCVxWZvs7pYb2VSSc/uXTr6D9Bhic1M0BcvlNU+7Q68/u3wGpc7RxxSNMP +kN0WwXx7wJxbzdJH2dlK57S8Zdjx4te1Pdm8eQOGCY0maHrNTDh8kC2WfXyzgiQM +MqN0zzvGMWk/4rqeAE6iD/mkURV13+IUfY1Wl9IxHHugCBQppu7vsEWCarBS1eQm +qXjrdyF+929OT6vJRWvambOofcuYSM3adL6d74F4wk2Zyy/RcUMtgld+aZzzJ1qo +196spOEs6iWxXDziFHk6rBZoBqfeFJIMtaeBL1pvzdGlNVVm5RNl2a6cUaGDL8SU +CHFJIhECgYEA05IUpCIR9FQASl8xRWbUql/hktRIpP/8HeA7A/ryAF+K8xsfuXjf +gcsz1fYhpk11c3K2f4l5+4GINTjcObQ/jImWPuoInV5gFqr5Bv7bZhs7OU1dyrvO +W7nR/k0688h51XFQ8EQVU0VcRzfU1dW3Yf/SLZ/aUmtepEsy5FRt4j0CgYEA03av +bab4DmPwGDkSktPbJ71kPYxm/pl459kTTun396OJ0PIKgK7QGuFz+OF0qJxG0Ew7 +CLUSp+LQHuh+hjEqatMJwE1CzrXKUXDLw4bq8cNM9WluGt+myGAehyvTGjD1Sn7n +viSRVxjyndOT0n9CzboVBqnAkI+xoGLVbV8xnf0CgYBe9z4uomBexGnQ+EOcfFjP +FPSivIjTD1gybNjwo26G/lvXXYBy9y+UCgr9alqWVER5Cq+iNao8W1sxUJGBkUfJ +JGT9Xn15bchBxIK9Qh5nCagew3xrKLuq7pC0ziO9E2qkhRWok1bjJsoR6ZyuUxza +b0L/WbZcsncr1dlFJiSgnQKBgQCq/sc+8QyXfmLRA52sU1pdWT3ss1M5v9rQCG4f +mvykKH8yEfdCNZockoDYGV/fVHfCylHWaa3LSpKm27QsSSeWuE6jTRaf1sribkan +NrtXgmkB7h5V5tKUJ32oKl0LSdmgQtycmi9Y8diGnWdkiPn5J+cGu0/21et52VHi +nl6d9QKBgQC6D13Jskxe8xiGYaTwCchJZgoyw+40Mdk6VwbfFbg76LZtFog3q3d3 +ECt14ymqueGs6sJG76cKK9XLk1oQY0z+R1XHp2E1QBlz+cezThLA5tW3IIqfYnyV +t5isXbUL14L7/r6EZ1mFf+vMs2Np8g4k1IhKf16aQuv/cMeN/ZBrxA== +-----END RSA PRIVATE KEY----- diff --git a/strongswan/server/ipsec.conf b/strongswan/server/ipsec.conf new file mode 100644 index 0000000..e17f711 --- /dev/null +++ b/strongswan/server/ipsec.conf @@ -0,0 +1,29 @@ + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=3 + keyexchange=ikev2 + + # various keepalive settings + dpdaction=clear + dpddelay=300s + + left=147.83.42.191 + leftfirewall=yes + leftsubnet=0.0.0.0/0 + leftcert=vpnHostCert.pem + leftid=@vpn.secured.eu + +conn psa + right=%any + #rightdns=10.31.0.1,8.8.8.8,8.8.4.4 + rightdns=8.8.8.8,8.8.4.4 + rightid=psa@secured.eu + rightsourceip=172.16.23.0/24 + rightsubnet=10.0.0.0/8 + auto=add + rightfirewall=yes diff --git a/strongswan/server/ipsec.d/Client.p12 b/strongswan/server/ipsec.d/Client.p12 new file mode 100644 index 0000000000000000000000000000000000000000..caf3137ea1517ed3bf77f197b95be84209a41aec GIT binary patch literal 4296 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 Xi@+;Yihd9dIIv!Xl}e`UpTPYO8I=QL literal 0 HcmV?d00001 diff --git a/strongswan/server/ipsec.d/ClientKey.pem b/strongswan/server/ipsec.d/ClientKey.pem new file mode 100644 index 0000000..a2ad836 --- /dev/null +++ b/strongswan/server/ipsec.d/ClientKey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAtSCoEzIuejeLhwGoHJkkKlxlWyt2+f19K7YqwiY9yXw2Hubp +/KaynPLJX4bpnAMIov4cDuvOkL5IF/ojT9yPTcWEKah5LEEIuftkgK9vk4sKZe45 +2s/mPtMzIfl8QYw5agBAovg5mW8OpZA7gWFKIa9jbiZ75xvzW2RNFvfgXGHOoDp8 +H+9Wm6wztbKAaEAjsCQDhR2YUbZw5rRQ37R31ddSvvOXwNljGq9V3RQ8LE3tni+z +n7X2B71Qyz1MTPWOZMsG019V04qp+VB/w85tYGQbYQJ11YtDnsisl41oPj63VWxe +T4fjmf9mXQEg6CJYX2Vbeq0R9mSpowi+vumu3wIDAQABAoIBAHRaih3qupigXe1Y +Txov9l+QAzxR65gkEuilmUonLsHkHRA03lMC8vKHtHy9OgySllW+T1/2czfgRIfC +lDSVRyl6nK/2HgEjtetjZuiTymVJiGB6bIf3zbzGB67nib7ByZAioWMPelDqWspY +oSE30ltZQ9JavnV0Kxymji8XBPXSejT7hRRp3/eSGaXFxI0YOk4SDcEwnr1gA+bb +BpFIfFhvB80uMBk+iwHrY7fLoznXww7oZX4U9jGAixfYd6K7jtzR4Vj0toMkX1ww +FB2Hn1xlHNPjg2ldVom3p0MaWwJJ8DLaxWv7at4HO+kpwI9vDIgu41fBTps5d6L8 +O4dGGCECgYEA189T+qrb6fRyNEOkeZWf4GeUcgrxXwqOovAB9D7p2RHoSJ1h1cYa +VWgIohHC/J1uWiiKUgeLhBAl3pcG9wMWUKC4am4K4/yCvCjOOoZX5Zdw6+HVnHFv +PAk1fd4i9mkEzJxVtXJxu80LfKc2hH4Geq8Xs4EE3q+S58ATxb90H5UCgYEA1tvc +Dv5WT3sqOqYJZCIC/mL5NEzeai3wqA0II/pUkhAqe9DUP8gdLqDheyzVFclT6Frk +3Kdmw8Akilq34X3Of1BvuwzIDio/MzRoMb9fu/tq2PMGFR+DkspwnQ+nzIFayVjS +/cI0WOyiB6ADIJQkNX/hM92C5hJzj6geywGKh6MCgYBHFAkTyUxvDMzEe/bi+K3U +iijxOrtu0xpRff0WxdXdYbGAoR1E/F9V+9LEFleDPhLHbQzJoaSI1YyzeEiZ+JFT +8utqWl4J4vPoJwRtcCvo+Wz+s73YLeA2BM5ya0RWphYnkeIExfHBqfH7l1M0ZhGa +PKrwuzCwa2FWJQQeIEWN1QKBgCvw3OloFIi+vJ0v9b23wvr5jNOoYNhAOvZza9XH +zWHt0nJt++prZ6RwnIyPV6jT+sgLRsDlr3ubIR32faKtEv0wmxka/RMAitpS/ngm +FlMgkPJ7iSPqxQLRSgSk/gEx9zo0YzoobII/Ksf6bolMIreaRplP1QRug5m+nUWR +NB6XAoGAALSMJlqZq9UqIu22/vwIhP2PcZPjrFif+QkdU6f3cyJZ4mzPr7rQjCJP +fADhA5McGkTjh4kBTN7Bb0PTnxrUDYpAeWgkgwozZEKAp92g8UUtWkjK2KyIwQ9f +n+xD50NWlsgf/EGVPzh1RFGD/fl6Yfc6tCYK+wWctSLDbWtxgzc= +-----END RSA PRIVATE KEY----- diff --git a/strongswan/server/ipsec.d/cacerts/strongswanCert.pem b/strongswan/server/ipsec.d/cacerts/strongswanCert.pem new file mode 100644 index 0000000..00a71e8 --- /dev/null +++ b/strongswan/server/ipsec.d/cacerts/strongswanCert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFNjCCAx6gAwIBAgIIEkPUG0+7tv8wDQYJKoZIhvcNAQEFBQAwOTELMAkGA1UE +BhMCRVMxEDAOBgNVBAoTB1NlY3VyZWQxGDAWBgNVBAMTD1NlY3VyZWQgUm9vdCBD +QTAeFw0xNjAyMjkxNjE5NTVaFw0yNjAyMjYxNjE5NTVaMDkxCzAJBgNVBAYTAkVT +MRAwDgYDVQQKEwdTZWN1cmVkMRgwFgYDVQQDEw9TZWN1cmVkIFJvb3QgQ0EwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ18n1g4CaO4UmNQBCvVA0hZjs +yY2I1PLhgnQS4tUu6zTb8CqL2qNr/lPh/PuDMhCDnyAdSvO+RrBRBbIKrDet1Ee6 +74lQnrhcN0b+8wQu3xBTwhMpg8iAPduJdDhdzDGrc0sKeUvJ2RnjQmyqNGTvCezl +7F/xb1UKKg1uZGTorqdxYg09v/6rg51/vd1xnjAaMI5aC4zSbmhDMgNowrNdVtW/ +qC7XQ+mgRNqDdf/ac63vnpNbKt8GhPjEVo/pJUSgNThpyBob5DTbSgFDPXxUjUyQ +QMwB4NW4CcxqhzoYdrhZXL/BAi7i/bLliX5ivOzKQHEbmfnwHwIyO39/OoEHFNnE +QC3YjDynFfdyx9LthxeaMFYXFmU/iWtL7qMfZb6IeiV1vkXchC8O/4aYzSqBskHX +XDwojlxMWYKfb9cJSfiOSbaaLvr1Gof2rjQSDEiJhdvynTE/Qf1wCmQDnLBfwZOv +d8mMdpi9g0Bqg3JF9/GuvebRJK8CKLcQM6wvn3W8YrxoFDDkH9ni+eLTRjZcazX1 +UuDL6lnWn2ZzO/ND54VSBwE/hPMcV63iSYWcNJP/Q69T2pXBrTigSBhlwaXuzRIF +/7YmrOwqag7d9lA3mej+jsaygA3L/f1CpI9HWjgZD16S2D83qWjcR66UqNDz8mFf +DPdLOQROZi5u+OLTHQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQUMPENpHawLXFavG47snr+cfvNR5QwDQYJKoZIhvcN +AQEFBQADggIBAFfyR9Q8h4poKaPu4Yh0uKVPDQSnX+xE/xw/acWu6NTck85TYP7G +s3U7+7IcEOaA/JJc5XhKEINQKj5qi28wzQzUg+bBoGAkd2PlfJZrLAULdNKBBjwH +7biMUIZFuPKhIJkgVK45o9gukQZW6azZ6uB0QYRD1usFrmqnERtNSYYv7QR1OuTp +MjHNdyyPok3vrcaYORg5bFmbPeyy/LPvyzHnv9eKR/ykj9Y7nCjBRq2sEa30bwzU +LFsEYmhrZs2Ja2YA9NjUWE6DOZdLrOjQDLHObGxVnLgzRrnwVqq33RJxCHGhcV7A +DjhVSH1qRs0OD/LhRpJIO/n7z/xPAH/T2/UhVpiuPQoHIfVWEgfUaZjl9jOBMhXm +iXJAA4RQLSH35cj8OntuEegE4ZdW1zTqmGmh3VjWf7hKv8FhpdQTCctVlTCDzOs7 +fWEVmupmgc2xBp6sW6e3ioo4CtPDoT+S23ve56gb7QV6UfMuZPeyFkFNgVCDwRBM +aVXd3WLey3c3D+j4cI+kbtwY8340grA8KALrh2VNbRDFh45vn2c3TAzcfIP2g+Bu +byNuWZ24IqIyhx1ottLo9KvtPM7S4vcNAuVtiVon3WLeRQlR3UZ316yenJy49NUJ +Q6PsMWHks5fqE9nVWU/AdflWYOJnX3ooi8EK3UD7sQC/21UyF/UFZdGd +-----END CERTIFICATE----- diff --git a/strongswan/server/ipsec.d/certs/ClientCert.pem b/strongswan/server/ipsec.d/certs/ClientCert.pem new file mode 100644 index 0000000..9a878a7 --- /dev/null +++ b/strongswan/server/ipsec.d/certs/ClientCert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEMTCCAhmgAwIBAgIIPumjqfUrjEIwDQYJKoZIhvcNAQEFBQAwOTELMAkGA1UE +BhMCRVMxEDAOBgNVBAoTB1NlY3VyZWQxGDAWBgNVBAMTD1NlY3VyZWQgUm9vdCBD +QTAeFw0xNjAyMjkxNjIyMjlaFw0xODAyMjgxNjIyMjlaMDgxCzAJBgNVBAYTAkVT +MRAwDgYDVQQKEwdTZWN1cmVkMRcwFQYDVQQDFA5wc2FAc2VjdXJlZC5ldTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALUgqBMyLno3i4cBqByZJCpcZVsr +dvn9fSu2KsImPcl8Nh7m6fymspzyyV+G6ZwDCKL+HA7rzpC+SBf6I0/cj03FhCmo +eSxBCLn7ZICvb5OLCmXuOdrP5j7TMyH5fEGMOWoAQKL4OZlvDqWQO4FhSiGvY24m +e+cb81tkTRb34FxhzqA6fB/vVpusM7WygGhAI7AkA4UdmFG2cOa0UN+0d9XXUr7z +l8DZYxqvVd0UPCxN7Z4vs5+19ge9UMs9TEz1jmTLBtNfVdOKqflQf8PObWBkG2EC +ddWLQ57IrJeNaD4+t1VsXk+H45n/Zl0BIOgiWF9lW3qtEfZkqaMIvr7prt8CAwEA +AaM+MDwwHwYDVR0jBBgwFoAUMPENpHawLXFavG47snr+cfvNR5QwGQYDVR0RBBIw +EIEOcHNhQHNlY3VyZWQuZXUwDQYJKoZIhvcNAQEFBQADggIBALXhl4J3IvoMLuWT +X8ofgyg0M0D+Je0y/P1yjajQ6jTmszlH9E2j7vPCus598CCxDHn0URR60kaB6W0F +i4pSqsBo2ctW+8rZpyAhL/Uo3VwZCkSKSFTC5YeJR9zopPxZemQGqxbnD7pKWfTX +r+CMiA1IkoDUoLOh25MaHTn9OWeSouQJH41S1zuq+W7rpZPrABRrMNUqID32eQhK +rPtyTS96TIJTbng02PTk+0FLQLyVrDh4mVR8ZALzyBnAmZGsaIGGKw2ff7P+DgZZ +UlyXM+1F4HRCsGINoDTMkDUZkLUP2gZQfV/bysK3zvZy1UrFjQRbk/76SIwyE4+K +zdf32UcUpwoCy6FAAHSlUbiDVFX9VA70R1ibGnXSiy45CsYOvNGp6rX+zkE1hfva +6gFLiyCXt1ns8/GIw51RzGeCOYqzX0Xo4m6H2frfMBOGnJ3Mq6yaT5q/JUf95f31 +qXSgkmdV0NxYQugrFWAA3+mAtmCUj7qh5zejDDtbV6RGApEAr9w+MARbXd69UDRD +GHTpZLcfTti/czqUOfcUFcYxO6+2qxQXdloTOv0mIppoQ8Waawk7lNpT7cWqxUnU +isZCAEq6CvZ6VPiYoRPeYwrPfOl+/B5Mx6N0Grt5HNFSwzyBnXy8gq0hO873Hy4z +eG898qPQSAlOHnHeiGzPn104HHSh +-----END CERTIFICATE----- diff --git a/strongswan/server/ipsec.d/certs/vpnHostCert.pem b/strongswan/server/ipsec.d/certs/vpnHostCert.pem new file mode 100644 index 0000000..ebe9d7b --- /dev/null +++ b/strongswan/server/ipsec.d/certs/vpnHostCert.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEUDCCAjigAwIBAgIILdCBejeojQcwDQYJKoZIhvcNAQEFBQAwOTELMAkGA1UE +BhMCRVMxEDAOBgNVBAoTB1NlY3VyZWQxGDAWBgNVBAMTD1NlY3VyZWQgUm9vdCBD +QTAeFw0xNjAyMjkxNzIwNTVaFw0xODAyMjgxNzIwNTVaMDgxCzAJBgNVBAYTAkVT +MRAwDgYDVQQKEwdTZWN1cmVkMRcwFQYDVQQDEw52cG4uc2VjdXJlZC5ldTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzmpbOKgqqq7ahSVDybx7PGNEdL +ltVQXTFZbMKqr3tEmOqdU+42PRbO02PtVxx+a+hIqIFcBMMK9meuMtFM+es2GdKN +k5UC/sPDTBxDAD3o3s2MQh5gLrHaEIgSYJSoy0jsuY2mfwLDQNsskvV/hcTROi6s +outxEYT/oUWrsXSt+hOO/J+Eh21ItHlPyNQXVBYalxWN+esTVNL/7PfFwSWQT6AG +bTJJ6W+mpX5pdEnl9dbJrduTpMRCRxN7t1UGf7WyZbY+1ZgLfjrcxdtEcJpH1Fsw +r6W+aCYKO/6e86f7orYsRAkf5+hZvtxt33hB8YCq3rN0cLuWeOlrM9Cn/YECAwEA +AaNdMFswHwYDVR0jBBgwFoAUMPENpHawLXFavG47snr+cfvNR5QwGQYDVR0RBBIw +EIIOdnBuLnNlY3VyZWQuZXUwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFCAIC +MA0GCSqGSIb3DQEBBQUAA4ICAQAI4vR/sjwFVocDqjf64DvXVsVmo+5iinc9dU96 +l/YCheC8ZXGyMbeuzTZ2b3xkYFQXGFJ1qcFse0VBGec4aj1d79vw7mIixcbsVSRZ +jHqSY6LaqGWnmguDUmYViG+qoKJAh6N3802kDJw9/ovv22B2WssktLMbYvx0no7c +wt8dmlK2OCyEAUN6SzlItwYSdPtI5MjpTvDAKYGHP5NfEsPh1zrOr3Ll+wEzLfzg +MvZRiBJIjsCe9YBOalndi9HTm6dTvTtPVT6ZJX9tPdXfeyQ2sqXqU6UF819LDnnp +/b5+dADiAGbj3+Kj0c3rjc/dS15WHp1qJoTPUpxngcJDqELAFaNB1H2tCg9a9BwM +9ke8YX8wV+DBMzlLGfPT2ijfJrlcuc/AszgnH7Y0npKAFFVMLOR8wfh5gaBduB9I +rYEPrN3mn+El/AFYOKohKNXY9BJ+W3ZruNFZMYmxbCdmzPhFt4AYgumFkBNFI3XV +tsTxARLhmwfeuwaqbq5bBfkFAfRO0RGlsfz1HeYPeyIlGoBu+VzeDEKPXqpJQ02b +s8hwgNhawqjwN6lba1T5XJnKxgXcY1Duf9ohQZ94wLcP8fvKO9OqOuyww5lF88LP +4DytW81hUAmoJug0nwmdUmLB5Pv1J8AgXW7OtpBauQaeeByckWSvh685DFSYa48l +IyFUKQ== +-----END CERTIFICATE----- diff --git a/strongswan/server/ipsec.d/client.conf b/strongswan/server/ipsec.d/client.conf new file mode 100644 index 0000000..e7f5331 --- /dev/null +++ b/strongswan/server/ipsec.d/client.conf @@ -0,0 +1,30 @@ + +config setup + charondebug="cfg 2, dmn 2, ike 2, net 2" + +conn %default + auto=add + + # key and renewal settings + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + keyexchange=ikev2 + + # various keepalive settings + dpdaction=clear + dpddelay=300s + +conn remote + auto=start # starts on boot. Change to "add" to manually connect + + # local config + leftcert=ClientCert.pem + leftfirewall=yes + leftsourceip=10.31.0.1 # change this as needed. you might even not need this line + + # remote server + rightcert=vpnHostCert.pem + right=147.83.42.191/24 # THE IP OF YOUR SERVER + rightsubnet=10.1.0.0/8 # SAME AS SERVER CONFIG diff --git a/strongswan/server/ipsec.d/private/ClientKey.pem b/strongswan/server/ipsec.d/private/ClientKey.pem new file mode 100644 index 0000000..a2ad836 --- /dev/null +++ b/strongswan/server/ipsec.d/private/ClientKey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAtSCoEzIuejeLhwGoHJkkKlxlWyt2+f19K7YqwiY9yXw2Hubp +/KaynPLJX4bpnAMIov4cDuvOkL5IF/ojT9yPTcWEKah5LEEIuftkgK9vk4sKZe45 +2s/mPtMzIfl8QYw5agBAovg5mW8OpZA7gWFKIa9jbiZ75xvzW2RNFvfgXGHOoDp8 +H+9Wm6wztbKAaEAjsCQDhR2YUbZw5rRQ37R31ddSvvOXwNljGq9V3RQ8LE3tni+z +n7X2B71Qyz1MTPWOZMsG019V04qp+VB/w85tYGQbYQJ11YtDnsisl41oPj63VWxe +T4fjmf9mXQEg6CJYX2Vbeq0R9mSpowi+vumu3wIDAQABAoIBAHRaih3qupigXe1Y +Txov9l+QAzxR65gkEuilmUonLsHkHRA03lMC8vKHtHy9OgySllW+T1/2czfgRIfC +lDSVRyl6nK/2HgEjtetjZuiTymVJiGB6bIf3zbzGB67nib7ByZAioWMPelDqWspY +oSE30ltZQ9JavnV0Kxymji8XBPXSejT7hRRp3/eSGaXFxI0YOk4SDcEwnr1gA+bb +BpFIfFhvB80uMBk+iwHrY7fLoznXww7oZX4U9jGAixfYd6K7jtzR4Vj0toMkX1ww +FB2Hn1xlHNPjg2ldVom3p0MaWwJJ8DLaxWv7at4HO+kpwI9vDIgu41fBTps5d6L8 +O4dGGCECgYEA189T+qrb6fRyNEOkeZWf4GeUcgrxXwqOovAB9D7p2RHoSJ1h1cYa +VWgIohHC/J1uWiiKUgeLhBAl3pcG9wMWUKC4am4K4/yCvCjOOoZX5Zdw6+HVnHFv +PAk1fd4i9mkEzJxVtXJxu80LfKc2hH4Geq8Xs4EE3q+S58ATxb90H5UCgYEA1tvc +Dv5WT3sqOqYJZCIC/mL5NEzeai3wqA0II/pUkhAqe9DUP8gdLqDheyzVFclT6Frk +3Kdmw8Akilq34X3Of1BvuwzIDio/MzRoMb9fu/tq2PMGFR+DkspwnQ+nzIFayVjS +/cI0WOyiB6ADIJQkNX/hM92C5hJzj6geywGKh6MCgYBHFAkTyUxvDMzEe/bi+K3U +iijxOrtu0xpRff0WxdXdYbGAoR1E/F9V+9LEFleDPhLHbQzJoaSI1YyzeEiZ+JFT +8utqWl4J4vPoJwRtcCvo+Wz+s73YLeA2BM5ya0RWphYnkeIExfHBqfH7l1M0ZhGa +PKrwuzCwa2FWJQQeIEWN1QKBgCvw3OloFIi+vJ0v9b23wvr5jNOoYNhAOvZza9XH +zWHt0nJt++prZ6RwnIyPV6jT+sgLRsDlr3ubIR32faKtEv0wmxka/RMAitpS/ngm +FlMgkPJ7iSPqxQLRSgSk/gEx9zo0YzoobII/Ksf6bolMIreaRplP1QRug5m+nUWR +NB6XAoGAALSMJlqZq9UqIu22/vwIhP2PcZPjrFif+QkdU6f3cyJZ4mzPr7rQjCJP +fADhA5McGkTjh4kBTN7Bb0PTnxrUDYpAeWgkgwozZEKAp92g8UUtWkjK2KyIwQ9f +n+xD50NWlsgf/EGVPzh1RFGD/fl6Yfc6tCYK+wWctSLDbWtxgzc= +-----END RSA PRIVATE KEY----- diff --git a/strongswan/server/ipsec.d/private/strongswanKey.pem b/strongswan/server/ipsec.d/private/strongswanKey.pem new file mode 100644 index 0000000..d830ddc --- /dev/null +++ b/strongswan/server/ipsec.d/private/strongswanKey.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/strongswan/server/ipsec.d/private/vpnHostKey.pem b/strongswan/server/ipsec.d/private/vpnHostKey.pem new file mode 100644 index 0000000..60376cf --- /dev/null +++ b/strongswan/server/ipsec.d/private/vpnHostKey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAzOals4qCqqrtqFJUPJvHs8Y0R0uW1VBdMVlswqqve0SY6p1T +7jY9Fs7TY+1XHH5r6EiogVwEwwr2Z64y0Uz56zYZ0o2TlQL+w8NMHEMAPejezYxC +HmAusdoQiBJglKjLSOy5jaZ/AsNA2yyS9X+FxNE6Lqyi63ERhP+hRauxdK36E478 +n4SHbUi0eU/I1BdUFhqXFY356xNU0v/s98XBJZBPoAZtMknpb6alfml0SeX11smt +25OkxEJHE3u3VQZ/tbJltj7VmAt+OtzF20RwmkfUWzCvpb5oJgo7/p7zp/uitixE +CR/n6Fm+3G3feEHxgKres3Rwu5Z46Wsz0Kf9gQIDAQABAoIBAQC6f5kEwSbLtsaY +NU9s1xG+5MICvp9sSzh3yYA/82HD3h0dNtQE9yNFVj18L7PhQIRxKWkOTTYcXScI +GbsxXFIBr/1y8uAIenBuuJzXAHkA4KujBW3M/hiiUuxHHgsAZF6bYrRFZO0oE5Y9 +p+WfRFH6YcIGZ6XmjMS4fIBKzMq5elsloziDndpA0MBqktOsP2WqsFSSpSuf33+h +a36ES9oxkvPv4uMIajajvXG4EMjc2fhT6/IWo9AQgQBJ6xAbEn89DAinsZgJMIsE +okYBEIYlBGWDBYLimaKVR2Z/yWmp6KCBZ8zNSuA2B6DGfMVxlx824f1rGKihutny +fYseo4z9AoGBAPY/HorZwtynlow60GyGfKAz8wHMJmyRtsHIPdynHurfope5wnyk +Pftc+BHS+MUGzXxwBGs+zyD+CabQRaPxIxJWlS5DYf3cbeOPsvM0SqHRL7RLhh18 +XQ6KVPd16YjiRDfePMQz5Mus/lx2S3Sv1rY7HCs/5H/GB8QSQ+EFxoJrAoGBANUE +SyqrYgQLOph/t2MAhr4fdM48nLy2GDlGPDd21S87hmmPjcO+oqfzxfSmVdPxt+ud +dAy2FrNiYEtg9ZS+XcEnmclmS7Nxm7k6gcBWol1qXxFhzKWaLMkNu4I5szwo2Pr8 +f7kUHhaPCS3Vp4W2GHFKWa02BRzL6eepE5nK3XLDAoGASBLlb6KZJFhw+g5k3r7m +T0WlRMtqG7DymvlvEdxgckKOpnYadLzl32KwwRbM9W4uStPGl1VE7tUsIgUnvPph +V4a8F1RlpBYWm9p9eC3AAmjl25GXaZ3y5sSaZeu/NO91JB0KECtYNj0mHg4Ju9eT +srbOYeJLkRJ3R25JK92y4o0CgYArk8BIz1yy1RU8o0wB3dayyD6d/jpWRK458Uta +UV4VVokNyUdIFxSxZSZHAUwnmph0g/Z8rzd/pQ1r/j7fGf6WCCLm151+7SvdPcHT +diWrFJq0NJyn+B1XWA7W+jTXhse0uLjsRNhO5x4KmV229eSUMJCd5aIwHOsR8D3J +RX39/wKBgQD0BxwVWqA6707fFOKXknsmzFrcDQpJeBYnjt+cXNhPNGmLtTVhxrBA +KQsrV5l+OZDoflp11/0sjhvCkRr1wtzKZdOMJeZik36/xKvIdPuZmmM6Q7XYEQBD +rVAAqBIfyP5gnSDRisVLbfvLVHEQ85zaoQY/BoAbfWoFPq4JYcVTsA== +-----END RSA PRIVATE KEY----- diff --git a/tests/I_TC_1.py b/tests/I_TC_1.py new file mode 100644 index 0000000..c7d3639 --- /dev/null +++ b/tests/I_TC_1.py @@ -0,0 +1,193 @@ +''' +I_TC_1: Network Edge Device (NED) Functional operation + +1. User authentication and Attestation: + a) Test connection to NED (class test_01) + b) Test logging in with invalid username (class test_01) + c) Test logging in with invalid password (class test_01) + d) Test logging in with valid username and password (class test_01) + e) Test Internet connection (class test_03) +2. Setup of New user (specs from D6.1) + a) Handle new user (class test_02) + b) Retrieve user specific data, e.g. profile (class test_04) + c) Setup new user trusted virtual domain (TVD), which includes his/hers PSC (class test_02) + d) Request user policies (class test_03) + e) Setup and configure user PSAs (class test_02) + +Preconditions to this test set: +- One user terminal is powered on and Selenium server is started +- NED is powered on + +Before tests (setup_module), script opens web browser Firefox on User terminal +After tests (teardown_module), script closes the browser on User terminal + +PSA specific settings: +- in program code, these codelines are hilighted with #PSA SPECIFIC SETTING +- From variable list 'User terminal settings', set appropriate username and password +- Check whole class test_03_connection + +''' + +import NEDtest +import selenium, time +from selenium import webdriver +from selenium.webdriver.common.desired_capabilities import DesiredCapabilities +from nose.plugins.skip import Skip, SkipTest +from nose.tools import with_setup + +#NED settings +login_address = 'http://10.2.2.253:8080/login' +manager_address = 'http://10.2.2.251:8080/psc' +PSAlog_prefix = 'http://10.2.2.251:8080/psa/dump-log-psa-ctrl/' +PSClog_address = 'http://10.2.2.251:8080/psc/dump-log-psc' + +#User terminal settings +user_ip = '192.168.184.129' +user_address = 'http://' + user_ip + ':4444/wd/hub' +username = 'test' #PSA SPECIFIC SETTING +password = 'secuser' #PSA SPECIFIC SETTING +logPath_prefix = '/var/lib/jenkins/logs/builds/I_TC_1/' + +#Variables +driver = 0 +website_timeout = 60 #seconds +PSXload_time = 0.000 +startTime = 0.000 + + + +def setup_module(module): + #Setup WebDriver + global driver + driver = webdriver.Remote(command_executor=user_address, desired_capabilities=webdriver.DesiredCapabilities.FIREFOX) + driver.set_page_load_timeout(website_timeout) + +def teardown_module(module): + #Write PSA logfile + PSAlog_address = NEDtest.getLogAddress(driver, manager_address, PSAlog_prefix) + if PSAlog_address: + for x in range(len(PSAlog_address)): + NEDtest.writeLog(driver, logPath_prefix + 'PSA' + str(x) + '.log', PSAlog_address[x]) + else: + NEDtest.emptyList('PSAlog_address') + #Write PSC logfile + NEDtest.writeLog(driver, logPath_prefix + 'PSC.log', PSClog_address) + #Write parameters logfile + paraFile = open(logPath_prefix + 'parameters.log', 'w+') + paraFile.write('File created: %s\n' % time.ctime()) + paraFile.write('Test I_TC_1 with username %s\n' % username) + paraFile.write('User terminal IP: %s\n' % user_ip) + paraFile.write('NED login address: %s\n' % login_address) + paraFile.write('PSC manager address: %s\n' % manager_address) + paraFile.write('PSA log address: %s\n' % PSAlog_address) + paraFile.write('PSC log address: %s\n' % PSClog_address) + paraFile.write('Timeout of website connection: %s seconds\n' % website_timeout) + paraFile.write('Loading time from login to "SECURED: OK": %.3f seconds\n' % PSXload_time) + paraFile.close() + #Close WebDrive + driver.close() + driver.quit() + + + +class test_01_login: + #This test set tests logging in with invalid credentials and + #valid credentials. No need to modify for PSA testing + + def setup(self): + try: + driver.get(login_address) + except: + assert 0, NEDtest.timeout(login_address) + + def test_01_connectionNED(self): + assert "NED" in driver.title, NEDtest.titleNotfound('NED', driver.title) + + def test_02_noconnectionInternet(self): + timeout = NEDtest.connectionNotloggedin(driver) + assert timeout == 'TRUE', "User had access to Internet before logging in to NED" + + def test_03_invalidUsername(self): + NEDtest.login(driver, 'invalidusername', password, login_address) + assert "Login failed" in driver.find_element_by_id("error-msg").text, "No Login failed -message in element error-msg" + + def test_04_invalidPassword(self): + NEDtest.login(driver, username, 'invalidpassword', login_address) + assert "Login failed" in driver.find_element_by_id("error-msg").text, "No Login failed -message in element error-msg" + + def test_05_successfulLogin(self): + NEDtest.login(driver, username, password, login_address) + assert "Congratulations" in driver.find_element_by_id("login_ok").text, "No Congratulations-message in element login_ok" + +class test_02_checkSECUREDok: + #This test set tests that are PSC and PSA(s) started correctly and + #are they visible in PSC GUI. No need to modify for PSA testing + + def setup(self): + global startTime + driver.get('about:blank') + startTime = time.time() + + def teardown(self): + global PSXload_time + PSXload_time = time.time() - startTime + + def test_01_checkSECUREDok(self): + NEDtest.checkSECUREDok(driver, manager_address) + + +class test_03_connection: + #This test set tests user's connection to Internet and websites. + #This set have to be modified to match PSA policies + + ############################################ + #PSA SPECIFIC SETTINGS (ALMOST WHOLE CLASS)# + ############################################ + + def setup(self): + driver.get('about:blank') + + def test_01_internetConnection(self): + NEDtest.allowedWebpage(driver, 'http://www.secured-fp7.eu') + assert "SECURED" in driver.title, NEDtest.titleNotfound('SECURED', driver.title) + + def test_02_blockedConnectionPolito(self): #PSA SPECIFIC SETTINGS + #With this test case you can check that blocked webpage from PSA is really blocked. + #Copy-paste this function to create new tests cases to different blocked pages. + #See example test_03_allowedConnectionUPC and test_04_allowedConnectionGoogle + website_address = 'http://www.polito.it' + website_title = 'Politecnico di Torino' + timeout = NEDtest.blockedWebpage(driver, website_address, website_title) + assert timeout == 'TRUE', "Connecting to blocked site %s did not timeout. Could be general error in Internet connection." % website_address + + def test_03_allowedConnectionUPC(self): #PSA SPECIFIC SETTINGS + #With this function you can test pages where user has access + #Copy-paste this function to create new tests cases to different blocked pages. + #See example test_03_allowedConnectionUPC and test_04_allowedConnectionGoogle + website_address = 'http://www.upc.edu' + website_title = 'UPC' + NEDtest.allowedWebpage(driver, website_address) + assert website_title in driver.title, NEDtest.titleNotfound(website_title, driver.title) + + def test_04_allowedConnectionGoogle(self): #PSA SPECIFIC SETTINGS + website_address = 'http://www.google.com' + website_title = 'Google' + NEDtest.allowedWebpage(driver, website_address) + assert website_title in driver.title, NEDtest.titleNotfound(website_title, driver.title) + +class test_04_userData: + #This test set checks user's data from PSC GUI. Right now, it only + #checks that user's IP is in correct form + + def test_01_checkuserIP(self): + userIP = NEDtest.getUserIPfromPSC(driver, manager_address) + assert "10.2.2." in userIP, "Could not retrieve user IP from PSC" + +class test_05_logout: + #This test is the last test of this set. It logs user out and also + #works as a teardown for whole test set. + + def test_01_logout(self): + #First have to go through login-screen to get to the logout-page + NEDtest.login(driver, username, password, login_address) + NEDtest.logout(driver) diff --git a/tests/NEDtest.py b/tests/NEDtest.py new file mode 100644 index 0000000..26a2532 --- /dev/null +++ b/tests/NEDtest.py @@ -0,0 +1,187 @@ +''' +This library contains functions to execute NED test scripts +''' + +import time +import selenium, re +from selenium import webdriver + +#################################### +#TEST CONNECTION AND LOGGING IN/OUT# +#################################### + +def login(driver, username, password, login_address): + try: + driver.get(login_address) + except: + print "In webpage title: %s" % driver.title + assert 0, timeout(login_address) + driver.find_element_by_id("input-username").clear() + driver.find_element_by_id("input-username").send_keys(username) + driver.find_element_by_id("input-password").clear() + driver.find_element_by_id("input-password").send_keys(password) + driver.find_element_by_id("submit").click() + +def logout(driver): + try: + driver.find_element_by_id("logout").click() + except: + assert 0, 'Could not found element \"logout\" from %s' % login_address + assert driver.find_element_by_id("login-form"), "Could not found login-form" + +def allowedWebpage(driver, webpage): + try: + driver.get(webpage) + except: + print "In webpage title: %s" % driver.title + assert 0, timeout(webpage) + +def blockedWebpage(driver, webpage, title): + timeout = 'FALSE' + try: + driver.get(webpage) + except: + timeout = 'TRUE' + print 'Website title: %s' % driver.title + print 'Expected website title: %s' % title + print 'Timeout occured: %s' % timeout + if 'Problem loading page' in driver.title: + timeout = 'TRUE' + elif title in driver.title: + assert 0, notBlocked(webpage) + elif timeout == 'FALSE': + assert 0, problemLoading(driver.title, webpage) + return timeout + +def connectionNotloggedin(driver): + webpage = 'http://www.secured-fp7.eu' + title = 'SECURED' + noConnection = 'FALSE' + try: + driver.get(webpage) + except: + noConnection = 'TRUE' + if 'Problem loading page' in driver.title: + noConnection = 'TRUE' + elif title in driver.title: + assert 0, 'User had access to SECURED website before logging in to NED' + elif noConnection == 'FALSE': + assert 0, problemLoading(driver.title, webpage) + return noConnection + +def getUserIPfromPSC(driver, manager_address): + userIP = '' + + try: + driver.get(manager_address) + except: + print "In webpage title: %s" % driver.title + assert 0, timeout(manager_address) + try: + userIP = driver.find_element_by_id("user-ip").text + except: + print "In webpage title: %s" % driver.title + assert 0, 'Could not found element \"user-ip\" from %s' % manager_address + print "User\'s IP is: %s" % userIP + return userIP + +def checkSECUREDok(driver, manager_address): + manager_counter = 0 + time_counter = 0 + counter_timeout = 60 + interval = 5 + status_text = '' + start_time = 0.000 + + start_time = time.time() + while True: + try: + driver.get(manager_address) + if manager_counter >= counter_timeout: + print "Try: In webpage title: %s" % driver.title + assert 0, timeout(manager_address) + if driver.title != 'Problem loading page': + break + else: + time.sleep(interval) + manager_counter += interval + except: + if manager_counter >= counter_timeout: + print "Except: In webpage title: %s" % driver.title + assert 0, timeout(manager_address) + time.sleep(interval) + manager_counter += interval + while True: + try: + status_text = driver.find_element_by_id("status").text + except: + if time_counter >= counter_timeout: + assert 0, 'Could not found status text. Status text: %s' % (status_text) + time.sleep(interval) + time_counter += interval + + if 'wait' in status_text: + if time_counter >= counter_timeout: + assert 0, 'It took over %d seconds to load PSA(s)\n %s' % (counter_timeout, status_text) + time.sleep(interval) + time_counter += interval + elif 'OK' in status_text: + print 'It took %.3f seconds to load PSA(s)' % (time.time() - start_time) + break + elif 'Error' in status_text: + print driver.find_element_by_id('page').text + assert 0, 'Text error in status text' + +######### +#LOGGING# +######### + +def getLogAddress(driver, manager_address, log_address): + ip = [] + ip_list = [] + try: + driver.get(manager_address) + except: + print "In webpage title: %s" % driver.title + timeout(manager_address) + try: + ip = re.findall(r'[0-9]+(?:\.[0-9]+){3}', driver.find_element_by_id("psa-list-online").text) + except: + print "Not able to found IP number from element psa-list-online" + print "In webpage title: %s" % driver.title + for x in ip: + ip_list.append(log_address + x) + return ip_list + +def writeLog(driver, logPath, logAddress): + logFile = open(logPath, 'w+') + try: + driver.get(logAddress) + except: + print "In webpage title: %s" % driver.title + timeout(logAddress) + try: + logFile.write(driver.find_element_by_xpath('/html/body/pre').text + '\n') + except: + print "Not able to write dump log file from %s to %s" % (logAddress, logPath) + logFile.close() + + +################ +#ERROR HANDLING# +################ + +def timeout(url): + print "Could not load page %s. Connection timed out." % url + +def notBlocked(url): + print "User had access to blocked page %s." % url + +def problemLoading(title, webpage): + print '\"%s\" reported in %s title. Connection should timeout.' % (title, webpage) + +def emptyList(listname): + print "IP address list %s is empty." % listname + +def titleNotfound(expected, found): + print 'Text \"%s\" not found in webpage title. Found \"%s\" instead' % (expected, found) diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..d454c6b --- /dev/null +++ b/tests/README.md @@ -0,0 +1,11 @@ +# Tests to be executed in Jenkins. +In this folder, there is a test set for PSA testing. Only these two files are needed to execute quick smoketest for PSA. For PSA specific tests some modifications have to be done to test scripts. + +## Needed modifications for PSA testing +Modifications are needed to I_TC_1.py file. DO NOT MODIFY NEDtest.py. These modifications are also explained in I_TC_1.py comments. The specific lines or functions that need modification are hilighted with comment #PSA SPECIFIC SETTING + +### Variables: username and password +Change correct username and password from the beginning of the code in variable list under title 'User terminal settings' + +### class test_03_connection +This test class tests connectivity to the Internet and some websites. This class have to be modified according to PSA's policies. Right now there are functions to test that has user access to blocked or allowed website. See I_TC_1.py for detailed instructions. \ No newline at end of file