complete implementation of the StrongSwan VPN feature

This commit is contained in:
Will Stevens 2017-01-16 13:34:11 -05:00
parent 17787a194a
commit f045d65b90
15 changed files with 200 additions and 114 deletions

View File

@ -190,10 +190,10 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
String ipsecPsk = cmd.getIpsecPsk();
String ikePolicy = cmd.getIkePolicy();
String espPolicy = cmd.getEspPolicy();
if (!NetUtils.isValidS2SVpnPolicy(ikePolicy)) {
throw new InvalidParameterValueException("The customer gateway IKE policy " + ikePolicy + " is invalid!");
if (!NetUtils.isValidS2SVpnPolicy("ike", ikePolicy)) {
throw new InvalidParameterValueException("The customer gateway IKE policy " + ikePolicy + " is invalid! Verify the required Diffie Hellman (DH) group is specified.");
}
if (!NetUtils.isValidS2SVpnPolicy(espPolicy)) {
if (!NetUtils.isValidS2SVpnPolicy("esp", espPolicy)) {
throw new InvalidParameterValueException("The customer gateway ESP policy " + espPolicy + " is invalid!");
}
Long ikeLifetime = cmd.getIkeLifetime();
@ -444,10 +444,10 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
String ipsecPsk = cmd.getIpsecPsk();
String ikePolicy = cmd.getIkePolicy();
String espPolicy = cmd.getEspPolicy();
if (!NetUtils.isValidS2SVpnPolicy(ikePolicy)) {
throw new InvalidParameterValueException("The customer gateway IKE policy" + ikePolicy + " is invalid!");
if (!NetUtils.isValidS2SVpnPolicy("ike", ikePolicy)) {
throw new InvalidParameterValueException("The customer gateway IKE policy" + ikePolicy + " is invalid! Verify the required Diffie Hellman (DH) group is specified.");
}
if (!NetUtils.isValidS2SVpnPolicy(espPolicy)) {
if (!NetUtils.isValidS2SVpnPolicy("esp", espPolicy)) {
throw new InvalidParameterValueException("The customer gateway ESP policy" + espPolicy + " is invalid!");
}
Long ikeLifetime = cmd.getIkeLifetime();
@ -517,7 +517,7 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
_accountMgr.checkAccess(caller, null, false, conn);
if (conn.getState() == State.Connected) {
if (conn.getState() != State.Pending) {
stopVpnConnection(id);
}
_vpnConnectionDao.remove(id);
@ -531,8 +531,8 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn
throw new CloudRuntimeException("Unable to acquire lock on " + conn);
}
try {
if (conn.getState() != State.Connected && conn.getState() != State.Error) {
throw new InvalidParameterValueException("Site to site VPN connection with specified id is not in correct state(connected) to process disconnect!");
if (conn.getState() == State.Pending) {
throw new InvalidParameterValueException("Site to site VPN connection with specified id is currently Pending, unable to Disconnect!");
}
conn.setState(State.Disconnected);

View File

@ -1,13 +1,14 @@
# rotate log files daily
daily
# keep 5 days worth
rotate 5
# keep 3 days worth
rotate 3
# create new (empty) log files after rotating old ones
create
# use date as a suffix of the rotated file
#dateext
# max size 50M
size 50M
compress
# RPM packages drop log rotation information into this directory
include /etc/logrotate.d
# no packages own wtmp and btmp -- we'll rotate them here

View File

@ -22,18 +22,9 @@ then
exit 1
fi
ipsec auto --status | grep vpn-$1 > /tmp/vpn-$1.status
ipsec status vpn-$1 > /tmp/vpn-$1.status
cat /tmp/vpn-$1.status | grep "ISAKMP SA established" > /dev/null
isakmpok=$?
if [ $isakmpok -ne 0 ]
then
echo -n "ISAKMP SA NOT found but checking IPsec;"
else
echo -n "ISAKMP SA found;"
fi
cat /tmp/vpn-$1.status | grep "IPsec SA established" > /dev/null
cat /tmp/vpn-$1.status | grep "ESTABLISHED" > /dev/null
ipsecok=$?
if [ $ipsecok -ne 0 ]
then

View File

@ -471,13 +471,13 @@ class CsSite2SiteVpn(CsDataBag):
def deletevpn(self, ip):
logging.info("Removing VPN configuration for %s", ip)
CsHelper.execute("ipsec auto --down vpn-%s" % ip)
CsHelper.execute("ipsec auto --delete vpn-%s" % ip)
CsHelper.execute("ipsec down vpn-%s" % ip)
CsHelper.execute("ipsec down vpn-%s" % ip)
vpnconffile = "%s/ipsec.vpn-%s.conf" % (self.VPNCONFDIR, ip)
vpnsecretsfile = "%s/ipsec.vpn-%s.secrets" % (self.VPNCONFDIR, ip)
os.remove(vpnconffile)
os.remove(vpnsecretsfile)
CsHelper.execute("ipsec auto --rereadall")
CsHelper.execute("ipsec reload")
def configure_iptables(self, dev, obj):
self.fw.append(["", "front", "-A INPUT -i %s -p udp -m udp --dport 500 -s %s -d %s -j ACCEPT" % (dev, obj['peer_gateway_ip'], obj['local_public_ip'])])
@ -497,49 +497,56 @@ class CsSite2SiteVpn(CsDataBag):
def configure_ipsec(self, obj):
leftpeer = obj['local_public_ip']
rightpeer = obj['peer_gateway_ip']
peerlist = obj['peer_guest_cidr_list'].lstrip().rstrip().replace(',', ' ')
peerlist = obj['peer_guest_cidr_list'].replace(' ', '')
vpnconffile = "%s/ipsec.vpn-%s.conf" % (self.VPNCONFDIR, rightpeer)
vpnsecretsfile = "%s/ipsec.vpn-%s.secrets" % (self.VPNCONFDIR, rightpeer)
ikepolicy=obj['ike_policy'].replace(';','-')
esppolicy=obj['esp_policy'].replace(';','-')
pfs='no'
if 'modp' in esppolicy:
pfs='yes'
if rightpeer in self.confips:
self.confips.remove(rightpeer)
file = CsFile(vpnconffile)
file.add("#conn for vpn-%s" % rightpeer, 0)
file.search("conn ", "conn vpn-%s" % rightpeer)
file.addeq(" left=%s" % leftpeer)
file.addeq(" leftsubnet=%s" % obj['local_guest_cidr'])
file.addeq(" leftnexthop=%s" % obj['local_public_gateway'])
file.addeq(" right=%s" % rightpeer)
file.addeq(" rightsubnets={%s}" % peerlist)
file.addeq(" rightsubnet=%s" % peerlist)
file.addeq(" type=tunnel")
file.addeq(" authby=secret")
file.addeq(" keyexchange=ike")
file.addeq(" ike=%s" % obj['ike_policy'])
file.addeq(" ike=%s" % ikepolicy)
file.addeq(" ikelifetime=%s" % self.convert_sec_to_h(obj['ike_lifetime']))
file.addeq(" esp=%s" % obj['esp_policy'])
file.addeq(" salifetime=%s" % self.convert_sec_to_h(obj['esp_lifetime']))
if "modp" in obj['esp_policy']:
file.addeq(" pfs=yes")
else:
file.addeq(" pfs=no")
file.addeq(" esp=%s" % esppolicy)
file.addeq(" lifetime=%s" % self.convert_sec_to_h(obj['esp_lifetime']))
file.addeq(" pfs=%s" % pfs)
file.addeq(" keyingtries=2")
file.addeq(" auto=start")
if 'encap' not in obj:
obj['encap']=False
file.addeq(" forceencaps=%s" % CsHelper.bool_to_yn(obj['encap']))
if obj['dpd']:
file.addeq(" dpddelay=30")
file.addeq(" dpdtimeout=120")
file.addeq(" dpdaction=restart")
file.addeq(" dpddelay=30")
file.addeq(" dpdtimeout=120")
file.addeq(" dpdaction=restart")
secret = CsFile(vpnsecretsfile)
secret.search("%s " % leftpeer, "%s %s: PSK \"%s\"" % (leftpeer, rightpeer, obj['ipsec_psk']))
secret.search("%s " % leftpeer, "%s %s : PSK \"%s\"" % (leftpeer, rightpeer, obj['ipsec_psk']))
if secret.is_changed() or file.is_changed():
secret.commit()
file.commit()
logging.info("Configured vpn %s %s", leftpeer, rightpeer)
CsHelper.execute("ipsec auto --rereadall")
CsHelper.execute("ipsec auto --add vpn-%s" % rightpeer)
if not obj['passive']:
CsHelper.execute("ipsec auto --up vpn-%s" % rightpeer)
os.chmod(vpnsecretsfile, 0o400)
CsHelper.execute("ipsec rereadsecrets")
CsHelper.execute("ipsec reload")
if not obj['passive']:
CsHelper.execute("sudo nohup ipsec down vpn-%s" % rightpeer)
CsHelper.execute("sudo nohup ipsec up vpn-%s &" % rightpeer)
os.chmod(vpnsecretsfile, 0400)
def convert_sec_to_h(self, val):
hrs = int(val) / 3600
@ -628,25 +635,25 @@ class CsRemoteAccessVpn(CsDataBag):
logging.debug("Remote accessvpn data bag %s", self.dbag)
self.remoteaccessvpn_iptables(public_ip, self.dbag[public_ip])
CsHelper.execute("ipsec auto --rereadall")
CsHelper.execute("ipsec down L2TP-PSK")
CsHelper.execute("ipsec update")
CsHelper.execute("service xl2tpd stop")
CsHelper.execute("service xl2tpd start")
CsHelper.execute("ipsec auto --rereadsecrets")
CsHelper.execute("ipsec auto --replace L2TP-PSK")
CsHelper.execute("ipsec rereadsecrets")
else:
logging.debug("Disabling remote access vpn .....")
#disable remote access vpn
CsHelper.execute("ipsec auto --down L2TP-PSK")
CsHelper.execute("ipsec down L2TP-PSK")
CsHelper.execute("service xl2tpd stop")
def configure_l2tpIpsec(self, left, obj):
vpnconffile="%s/l2tp.conf" % (self.VPNCONFDIR)
l2tpconffile="%s/l2tp.conf" % (self.VPNCONFDIR)
vpnsecretfilte="%s/ipsec.any.secrets" % (self.VPNCONFDIR)
xl2tpdconffile="/etc/xl2tpd/xl2tpd.conf"
xl2tpoptionsfile='/etc/ppp/options.xl2tpd'
file = CsFile(vpnconffile)
file = CsFile(l2tpconffile)
localip=obj['local_ip']
localcidr=obj['local_cidr']
publicIface=obj['public_interface']

View File

@ -15,21 +15,20 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
from pprint import pprint
from netaddr import *
def merge(dbag, ip):
added = False
nic_dev_id = None
index = -1 # a non-valid array index
for dev in dbag:
if dev == "id":
continue
for address in dbag[dev]:
for i, address in enumerate(dbag[dev]):
if address['public_ip'] == ip['public_ip']:
if 'nic_dev_id' in address:
nic_dev_id = address['nic_dev_id']
dbag[dev].remove(address)
index = i
ipo = IPNetwork(ip['public_ip'] + '/' + ip['netmask'])
if 'nic_dev_id' in ip:
@ -44,8 +43,11 @@ def merge(dbag, ip):
else:
ip['nw_type'] = ip['nw_type'].lower()
if ip['nw_type'] == 'control':
dbag['eth' + str(nic_dev_id)] = [ip]
dbag[ip['device']] = [ip]
else:
dbag.setdefault('eth' + str(nic_dev_id), []).append(ip)
if index != -1:
dbag[ip['device']][index] = ip
else:
dbag.setdefault(ip['device'], []).append(ip)
return dbag

View File

@ -1,9 +1,5 @@
# Manual: ipsec.conf.5
version 2.0
# ipsec.conf - strongSwan IPsec configuration file
config setup
nat_traversal=yes
virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12
protostack=auto
include /etc/ipsec.d/*.conf

View File

@ -1,8 +1,14 @@
#ipsec remote access vpn configuration
conn L2TP-PSK
authby=secret
authby=psk
pfs=no
rekey=no
keyingtries=3
keyexchange=ikev1
forceencaps=yes
leftfirewall=yes
leftnexthop=%defaultroute
type=transport
#
# ----------------------------------------------------------
# The VPN server.
@ -30,4 +36,5 @@ conn L2TP-PSK
# ----------------------------------------------------------
# Change 'ignore' to 'add' to enable this configuration.
#
rightsubnetwithin=0.0.0.0/0
auto=add

View File

@ -1,2 +1,2 @@
include /var/lib/openswan/ipsec.secrets.inc
include /var/lib/strongswan/ipsec.conf.inc
include /etc/ipsec.d/ipsec.*.secrets

View File

@ -177,7 +177,7 @@ class Services:
},
"vpn_customer_gw": {
"ipsecpsk": "s2svpn",
"ikepolicy": "3des-md5",
"ikepolicy": "3des-md5;modp1536",
"ikelifetime": "86400",
"esppolicy": "3des-md5",
"esplifetime": "3600",

View File

@ -186,7 +186,7 @@ class Services:
},
"vpn_customer_gw": {
"ipsecpsk": "s2svpn",
"ikepolicy": "3des-md5",
"ikepolicy": "3des-md5;modp1536",
"ikelifetime": "86400",
"esppolicy": "3des-md5",
"esplifetime": "3600",

View File

@ -54,6 +54,11 @@ function do_signature() {
echo "Cloudstack Release $CLOUDSTACK_RELEASE $(date)" > /etc/cloudstack-release
}
function configure_strongswan() {
# change the charon stroke timeout from 3 minutes to 30 seconds
sed -i "s/# timeout = 0/timeout = 30000/" /etc/strongswan.d/charon/stroke.conf
}
function configure_services() {
mkdir -p /var/www/html
mkdir -p /opt/cloud/bin
@ -81,6 +86,7 @@ function configure_services() {
chkconfig radvd off
configure_apache2
configure_strongswan
}
return 2>/dev/null || configure_services

View File

@ -64,7 +64,6 @@ function install_packages() {
nfs-common \
samba-common cifs-utils \
xl2tpd bcrelay ppp ipsec-tools tdb-tools \
openswan=1:2.6.37-3+deb7u1 \
xenstore-utils libxenstore3.0 \
conntrackd ipvsadm libnetfilter-conntrack3 libnl-3-200 libnl-genl-3-200 \
ipcalc \
@ -76,9 +75,8 @@ function install_packages() {
sharutils
${apt_get} -t wheezy-backports install keepalived irqbalance open-vm-tools qemu-guest-agent
${apt_get} -t wheezy-backports install strongswan libcharon-extra-plugins libstrongswan-extra-plugins
# hold on installed openswan version, upgrade rest of the packages (if any)
apt-mark hold openswan
apt-get update
apt-get -y --force-yes upgrade

View File

@ -5992,10 +5992,6 @@
docID: 'helpVPNGatewayIKEEncryption',
select: function(args) {
var items = [];
items.push({
id: '3des',
description: '3des'
});
items.push({
id: 'aes128',
description: 'aes128'
@ -6008,6 +6004,10 @@
id: 'aes256',
description: 'aes256'
});
items.push({
id: '3des',
description: '3des'
});
args.response.success({
data: items
});
@ -6018,14 +6018,26 @@
docID: 'helpVPNGatewayIKEHash',
select: function(args) {
var items = [];
items.push({
id: 'md5',
description: 'md5'
});
items.push({
id: 'sha1',
description: 'sha1'
});
items.push({
id: 'sha256',
description: 'sha256'
});
items.push({
id: 'sha384',
description: 'sha384'
});
items.push({
id: 'sha512',
description: 'sha512'
});
items.push({
id: 'md5',
description: 'md5'
});
args.response.success({
data: items
});
@ -6036,18 +6048,39 @@
docID: 'helpVPNGatewayIKEDH',
select: function(args) {
var items = [];
// StrongSwan now requires a DH group to be specified...
//items.push({
// id: '',
// description: _l('label.none')
//});
items.push({
id: '',
description: _l('label.none')
id: 'modp1536',
description: 'Group 5(modp1536)'
});
items.push({
id: 'modp2048',
description: 'Group 14(modp2048)'
});
items.push({
id: 'modp3072',
description: 'Group 15(modp3072)'
});
items.push({
id: 'modp4096',
description: 'Group 16(modp4096)'
});
items.push({
id: 'modp6144',
description: 'Group 17(modp6144)'
});
items.push({
id: 'modp8192',
description: 'Group 18(modp8192)'
});
items.push({
id: 'modp1024',
description: 'Group 2(modp1024)'
});
items.push({
id: 'modp1536',
description: 'Group 5(modp1536)'
});
args.response.success({
data: items
});
@ -6060,10 +6093,6 @@
docID: 'helpVPNGatewayESPLifetime',
select: function(args) {
var items = [];
items.push({
id: '3des',
description: '3des'
});
items.push({
id: 'aes128',
description: 'aes128'
@ -6076,6 +6105,10 @@
id: 'aes256',
description: 'aes256'
});
items.push({
id: '3des',
description: '3des'
});
args.response.success({
data: items
});
@ -6086,14 +6119,26 @@
docID: 'helpVPNGatewayESPHash',
select: function(args) {
var items = [];
items.push({
id: 'md5',
description: 'md5'
});
items.push({
id: 'sha1',
description: 'sha1'
});
items.push({
id: 'sha256',
description: 'sha256'
});
items.push({
id: 'sha384',
description: 'sha384'
});
items.push({
id: 'sha512',
description: 'sha512'
});
items.push({
id: 'md5',
description: 'md5'
});
args.response.success({
data: items
});
@ -6108,14 +6153,34 @@
id: '',
description: _l('label.none')
});
items.push({
id: 'modp1024',
description: 'Group 2(modp1024)'
});
items.push({
id: 'modp1536',
description: 'Group 5(modp1536)'
});
items.push({
id: 'modp2048',
description: 'Group 14(modp2048)'
});
items.push({
id: 'modp3072',
description: 'Group 15(modp3072)'
});
items.push({
id: 'modp4096',
description: 'Group 16(modp4096)'
});
items.push({
id: 'modp6144',
description: 'Group 17(modp6144)'
});
items.push({
id: 'modp8192',
description: 'Group 18(modp8192)'
});
items.push({
id: 'modp1024',
description: 'Group 2(modp1024)'
});
args.response.success({
data: items
});
@ -6522,8 +6587,8 @@
success: function(json) {
var item = json.listvpncustomergatewaysresponse.vpncustomergateway[0];
//IKE POlicy
var a1 = item.ikepolicy.split('-'); //e.g. item.ikepolicy == '3des-md5' or '3des-md5;modp1024'
//IKE Policy
var a1 = item.ikepolicy.split('-'); //e.g. item.ikepolicy == '3des-md5;modp1024'
item.ikeEncryption = a1[0];
if (a1[1].indexOf(';') == -1) {
item.ikeHash = a1[1];

View File

@ -1187,7 +1187,10 @@ public class NetUtils {
return false;
}
public static boolean isValidS2SVpnPolicy(final String policys) {
public static boolean isValidS2SVpnPolicy(final String policyType, final String policys) {
if (policyType == null || policyType.isEmpty()) {
return false;
}
if (policys == null || policys.isEmpty()) {
return false;
}
@ -1208,14 +1211,17 @@ public class NetUtils {
if (!cipher.matches("3des|aes128|aes192|aes256")) {
return false;
}
if (!hash.matches("md5|sha1")) {
if (!hash.matches("md5|sha1|sha256|sha384|sha512")) {
return false;
}
String pfsGroup = null;
String group = null;
if (!policy.equals(cipherHash)) {
pfsGroup = policy.split(";")[1];
group = policy.split(";")[1];
}
if (pfsGroup != null && !pfsGroup.matches("modp1024|modp1536")) {
if (group == null && policyType.toLowerCase().matches("ike")) {
return false; // StrongSwan requires a DH group for the IKE policy
}
if (group != null && !group.matches("modp1024|modp1536|modp2048|modp3072|modp4096|modp6144|modp8192")) {
return false;
}
}

View File

@ -105,18 +105,25 @@ public class NetUtilsTest {
@Test
public void testIsValidS2SVpnPolicy() {
assertTrue(NetUtils.isValidS2SVpnPolicy("aes128-sha1"));
assertTrue(NetUtils.isValidS2SVpnPolicy("3des-sha1"));
assertTrue(NetUtils.isValidS2SVpnPolicy("3des-sha1,aes256-sha1"));
assertTrue(NetUtils.isValidS2SVpnPolicy("3des-md5;modp1024"));
assertTrue(NetUtils.isValidS2SVpnPolicy("3des-sha1,aes128-sha1;modp1536"));
assertFalse(NetUtils.isValidS2SVpnPolicy("des-md5;modp1024,aes128-sha1;modp1536"));
assertFalse(NetUtils.isValidS2SVpnPolicy("des-sha1"));
assertFalse(NetUtils.isValidS2SVpnPolicy("abc-123,ase-sha1"));
assertFalse(NetUtils.isValidS2SVpnPolicy("de-sh,aes-sha1"));
assertFalse(NetUtils.isValidS2SVpnPolicy(""));
assertFalse(NetUtils.isValidS2SVpnPolicy(";modp1536"));
assertFalse(NetUtils.isValidS2SVpnPolicy(",aes;modp1536,,,"));
assertTrue(NetUtils.isValidS2SVpnPolicy("esp", "aes128-sha1"));
assertTrue(NetUtils.isValidS2SVpnPolicy("esp", "3des-sha1"));
assertTrue(NetUtils.isValidS2SVpnPolicy("esp", "3des-sha1,aes256-sha1"));
assertTrue(NetUtils.isValidS2SVpnPolicy("esp", "3des-md5;modp1024"));
assertTrue(NetUtils.isValidS2SVpnPolicy("esp", "3des-sha256,aes128-sha512;modp1536"));
assertTrue(NetUtils.isValidS2SVpnPolicy("ike", "3des-sha1;modp3072,aes128-sha1;modp1536"));
assertTrue(NetUtils.isValidS2SVpnPolicy("ike", "3des-md5;modp1024"));
assertTrue(NetUtils.isValidS2SVpnPolicy("ike", "3des-sha1;modp3072,aes128-sha1;modp1536"));
assertTrue(NetUtils.isValidS2SVpnPolicy("ike", "3des-sha256;modp3072,aes128-sha512;modp1536"));
assertFalse(NetUtils.isValidS2SVpnPolicy("ike", "aes128-sha1"));
assertFalse(NetUtils.isValidS2SVpnPolicy("ike", "3des-sha1"));
assertFalse(NetUtils.isValidS2SVpnPolicy("ike", "3des-sha1,aes256-sha1"));
assertFalse(NetUtils.isValidS2SVpnPolicy("esp", "des-md5;modp1024,aes128-sha1;modp1536"));
assertFalse(NetUtils.isValidS2SVpnPolicy("esp", "des-sha1"));
assertFalse(NetUtils.isValidS2SVpnPolicy("esp", "abc-123,ase-sha1"));
assertFalse(NetUtils.isValidS2SVpnPolicy("esp", "de-sh,aes-sha1"));
assertFalse(NetUtils.isValidS2SVpnPolicy("esp", ""));
assertFalse(NetUtils.isValidS2SVpnPolicy("esp", ";modp1536"));
assertFalse(NetUtils.isValidS2SVpnPolicy("esp", ",aes;modp1536,,,"));
}
@Test