diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in
index 3afde1e317b..505921f5c36 100755
--- a/client/tomcatconf/commands.properties.in
+++ b/client/tomcatconf/commands.properties.in
@@ -215,4 +215,9 @@ listInstanceGroups=com.cloud.api.commands.ListVMGroupsCmd;15
uploadCustomCertificate=com.cloud.api.commands.UploadCustomCertificateCmd;15
### other commands
-listHypervisors=com.cloud.api.commands.ListHypervisorsCmd;15
\ No newline at end of file
+listHypervisors=com.cloud.api.commands.ListHypervisorsCmd;15
+
+### VPN
+createRemoteAccessVpn=com.cloud.api.commands.CreateRemoteAccessVpnCmd;15
+deleteRemoteAccessVpn=com.cloud.api.commands.DeleteRemoteAccessVpnCmd;15
+listRemoteAccessVpns=com.cloud.api.commands.ListRemoteAccessVpnsCmd;15
diff --git a/client/tomcatconf/components.xml.in b/client/tomcatconf/components.xml.in
index 28a78a0ce79..0f9f44cd2df 100755
--- a/client/tomcatconf/components.xml.in
+++ b/client/tomcatconf/components.xml.in
@@ -114,7 +114,8 @@
-
+
+
diff --git a/core/src/com/cloud/agent/api/routing/RemoteAccessVpnCfgCommand.java b/core/src/com/cloud/agent/api/routing/RemoteAccessVpnCfgCommand.java
new file mode 100644
index 00000000000..669db7a954a
--- /dev/null
+++ b/core/src/com/cloud/agent/api/routing/RemoteAccessVpnCfgCommand.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package com.cloud.agent.api.routing;
+
+
+public class RemoteAccessVpnCfgCommand extends RoutingCommand {
+
+ String vpnAppliancePrivateIpAddress; //router private ip address typically
+ boolean create;
+ String vpnServerIp;
+ String ipRange;
+ String presharedKey;
+ String localIp;
+
+ protected RemoteAccessVpnCfgCommand() {
+ this.create = false;
+ }
+
+ public boolean isCreate() {
+ return create;
+ }
+
+ @Override
+ public boolean executeInSequence() {
+ return true;
+ }
+
+
+ public RemoteAccessVpnCfgCommand(boolean create, String routerPrivateIp, String vpnServerAddress, String localIp, String ipRange, String ipsecPresharedKey) {
+ this.vpnAppliancePrivateIpAddress = routerPrivateIp;
+ this.vpnServerIp = vpnServerAddress;
+ this.ipRange = ipRange;
+ this.presharedKey = ipsecPresharedKey;
+ this.localIp = localIp;
+ this.create = create;
+ }
+
+ public String getVpnServerIp() {
+ return vpnServerIp;
+ }
+
+ public void setVpnServerIp(String vpnServerIp) {
+ this.vpnServerIp = vpnServerIp;
+ }
+
+ public String getIpRange() {
+ return ipRange;
+ }
+
+ public void setIpRange(String ipRange) {
+ this.ipRange = ipRange;
+ }
+
+ public String getPresharedKey() {
+ return presharedKey;
+ }
+
+ public void setPresharedKey(String presharedKey) {
+ this.presharedKey = presharedKey;
+ }
+
+ public String getLocalIp() {
+ return localIp;
+ }
+
+ public String getVpnAppliancePrivateIpAddress() {
+ return vpnAppliancePrivateIpAddress;
+ }
+
+ public String getRouterPrivateIpAddress() {
+ return vpnAppliancePrivateIpAddress;
+ }
+
+}
diff --git a/core/src/com/cloud/event/EventTypes.java b/core/src/com/cloud/event/EventTypes.java
index a0776c8f23d..c8c9cd1af54 100755
--- a/core/src/com/cloud/event/EventTypes.java
+++ b/core/src/com/cloud/event/EventTypes.java
@@ -162,4 +162,8 @@ public class EventTypes {
public static final String EVENT_MAINTENANCE_CANCEL_PRIMARY_STORAGE = "MAINT.CANCEL.PS";
public static final String EVENT_MAINTENANCE_PREPARE = "MAINT.PREPARE";
public static final String EVENT_MAINTENANCE_PREPARE_PRIMARY_STORAGE = "MAINT.PREPARE.PS";
+
+ //VPN
+ public static final String EVENT_REMOTE_ACCESS_VPN_CREATE = "VPN.REMOTE.ACCESS.CREATE";
+ public static final String EVENT_REMOTE_ACCESS_VPN_DESTROY = "VPN.REMOTE.ACCESS.DESTROY";
}
diff --git a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
index 7a40bbf7f75..a59f305159d 100644
--- a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
+++ b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
@@ -131,6 +131,7 @@ import com.cloud.agent.api.routing.IPAssocCommand;
import com.cloud.agent.api.routing.LoadBalancerCfgCommand;
import com.cloud.agent.api.routing.SavePasswordCommand;
import com.cloud.agent.api.routing.SetFirewallRuleCommand;
+import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand;
import com.cloud.agent.api.routing.VmDataCommand;
import com.cloud.agent.api.storage.CopyVolumeAnswer;
import com.cloud.agent.api.storage.CopyVolumeCommand;
@@ -642,6 +643,8 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
return execute((PoolEjectCommand) cmd);
} else if (cmd instanceof Start2Command) {
return execute((Start2Command)cmd);
+ } else if (cmd instanceof RemoteAccessVpnCfgCommand) {
+ return execute((RemoteAccessVpnCfgCommand)cmd);
} else {
return Answer.createUnsupportedCommandAnswer(cmd);
}
@@ -1204,6 +1207,25 @@ public abstract class CitrixResourceBase implements StoragePoolResource, ServerR
}
return new Answer(cmd);
}
+
+ protected synchronized Answer execute(final RemoteAccessVpnCfgCommand cmd) {
+ String args = cmd.getRouterPrivateIpAddress();
+ if (cmd.isCreate()) {
+ args += " -r " + cmd.getIpRange();
+ args += " -p " + cmd.getPresharedKey();
+ args += " -s " + cmd.getVpnServerIp();
+ args += " -l " + cmd.getLocalIp();
+ args += " -c";
+
+ } else {
+ args += " -d";
+ }
+ String result = callHostPlugin("vmops", "lt2p_vpn", "args", args);
+ if (result == null || result.isEmpty()) {
+ return new Answer(cmd, false, "Configure VPN failed");
+ }
+ return new Answer(cmd);
+ }
protected Answer execute(final VmDataCommand cmd) {
String routerPrivateIpAddress = cmd.getRouterPrivateIpAddress();
diff --git a/core/src/com/cloud/network/RemoteAccessVpnVO.java b/core/src/com/cloud/network/RemoteAccessVpnVO.java
new file mode 100644
index 00000000000..40fd4b01b02
--- /dev/null
+++ b/core/src/com/cloud/network/RemoteAccessVpnVO.java
@@ -0,0 +1,135 @@
+/**
+ * Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package com.cloud.network;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.PrimaryKeyJoinColumn;
+import javax.persistence.SecondaryTable;
+import javax.persistence.Table;
+
+@Entity
+@Table(name=("remote_access_vpn"))
+@SecondaryTable(name="account",
+ pkJoinColumns={@PrimaryKeyJoinColumn(name="account_id", referencedColumnName="id")})
+public class RemoteAccessVpnVO {
+ @Id
+ @GeneratedValue(strategy=GenerationType.IDENTITY)
+ @Column(name="id")
+ private Long id;
+
+ @Column(name="account_id")
+ private long accountId;
+
+ @Column(name="zone_id")
+ private long zoneId;
+
+ @Column(name="account_name", table="account", insertable=false, updatable=false)
+ private String accountName = null;
+
+ @Column(name="domain_id", table="account", insertable=false, updatable=false)
+ private long domainId;
+
+ @Column(name="vpn_server_addr")
+ private String vpnServerAddress;
+
+ @Column(name="local_ip")
+ private String localIp;
+
+ @Column(name="ip_range")
+ private String ipRange;
+
+ @Column(name="ipsec_psk")
+ private String ipsecPresharedKey;
+
+ public RemoteAccessVpnVO() { }
+
+ public RemoteAccessVpnVO(long accountId, long zoneId, String publicIp, String localIp, String ipRange, String presharedKey) {
+ this.accountId = accountId;
+ this.vpnServerAddress = publicIp;
+ this.ipRange = ipRange;
+ this.ipsecPresharedKey = presharedKey;
+ this.zoneId = zoneId;
+ this.localIp = localIp;
+
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+
+
+ public long getAccountId() {
+ return accountId;
+ }
+
+ public String getAccountName() {
+ return accountName;
+ }
+
+ public String getVpnServerAddress() {
+ return vpnServerAddress;
+ }
+
+ public void setVpnServerAddress(String vpnServerAddress) {
+ this.vpnServerAddress = vpnServerAddress;
+ }
+
+ public String getIpRange() {
+ return ipRange;
+ }
+
+ public void setIpRange(String ipRange) {
+ this.ipRange = ipRange;
+ }
+
+ public String getIpsecPresharedKey() {
+ return ipsecPresharedKey;
+ }
+
+ public void setIpsecPresharedKey(String ipsecPresharedKey) {
+ this.ipsecPresharedKey = ipsecPresharedKey;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public void setZoneId(long zoneId) {
+ this.zoneId = zoneId;
+ }
+
+ public long getZoneId() {
+ return zoneId;
+ }
+
+ public String getLocalIp() {
+ return localIp;
+ }
+
+ public long getDomainId() {
+ return domainId;
+ }
+
+
+}
diff --git a/core/src/com/cloud/network/dao/RemoteAccessVpnDao.java b/core/src/com/cloud/network/dao/RemoteAccessVpnDao.java
new file mode 100644
index 00000000000..fa8fd5165f6
--- /dev/null
+++ b/core/src/com/cloud/network/dao/RemoteAccessVpnDao.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package com.cloud.network.dao;
+
+import com.cloud.network.RemoteAccessVpnVO;
+import com.cloud.utils.db.GenericDao;
+
+public interface RemoteAccessVpnDao extends GenericDao {
+ RemoteAccessVpnVO findByPublicIpAddress(String ipAddress);
+ RemoteAccessVpnVO findByAccountAndZone(Long accountId, Long zoneId);
+}
diff --git a/core/src/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java b/core/src/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java
new file mode 100644
index 00000000000..1860d048e6e
--- /dev/null
+++ b/core/src/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package com.cloud.network.dao;
+
+import javax.ejb.Local;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.network.RemoteAccessVpnVO;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+
+@Local(value={RemoteAccessVpnDao.class})
+public class RemoteAccessVpnDaoImpl extends GenericDaoBase implements RemoteAccessVpnDao {
+ private static final Logger s_logger = Logger.getLogger(RemoteAccessVpnDaoImpl.class);
+
+ private final SearchBuilder ListByIp;
+ private final SearchBuilder AccountAndZoneSearch;
+
+ protected RemoteAccessVpnDaoImpl() {
+ ListByIp = createSearchBuilder();
+ ListByIp.and("ipAddress", ListByIp.entity().getVpnServerAddress(), SearchCriteria.Op.EQ);
+ ListByIp.done();
+
+ AccountAndZoneSearch = createSearchBuilder();
+ AccountAndZoneSearch.and("accountId", AccountAndZoneSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
+ AccountAndZoneSearch.and("zoneId", AccountAndZoneSearch.entity().getZoneId(), SearchCriteria.Op.EQ);
+ AccountAndZoneSearch.done();
+ }
+
+ @Override
+ public RemoteAccessVpnVO findByPublicIpAddress(String ipAddress) {
+ SearchCriteria sc = ListByIp.create();
+ sc.setParameters("ipAddress", ipAddress);
+ return findOneBy(sc);
+ }
+
+ @Override
+ public RemoteAccessVpnVO findByAccountAndZone(Long accountId, Long zoneId) {
+ SearchCriteria sc = AccountAndZoneSearch.create();
+ sc.setParameters("accountId", accountId);
+ sc.setParameters("zoneId", zoneId);
+ return findOneBy(sc);
+ }
+}
diff --git a/patches/systemvm/debian/buildsystemvm.sh b/patches/systemvm/debian/buildsystemvm.sh
index e999b8b649d..329da8062fd 100755
--- a/patches/systemvm/debian/buildsystemvm.sh
+++ b/patches/systemvm/debian/buildsystemvm.sh
@@ -335,10 +335,14 @@ xenstore_utils() {
for f in $(find ${scriptdir}/xe/ -name xe-*)
do
cp $f ./usr/sbin/
- chmod a+x /usr/sbin/xe-*
+ chmod a+x ./usr/sbin/xe-*
done
}
+vpn_config() {
+ cp -r ${scriptdir}/vpn/* ./
+}
+
packages() {
DEBIAN_FRONTEND=noninteractive
DEBIAN_PRIORITY=critical
@@ -346,7 +350,7 @@ packages() {
export DEBIAN_FRONTEND DEBIAN_PRIORITY DEBCONF_DB_OVERRIDE
#basic stuff
- chroot . apt-get --no-install-recommends -q -y --force-yes install rsyslog logrotate cron chkconfig insserv net-tools ifupdown vim-tiny netbase iptables openssh-server grub e2fsprogs dhcp3-client dnsmasq tcpdump socat wget python bzip2 sed gawk diff grep gzip less tar telnet traceroute psmisc procps monit inetutils-ping iputils-arping httping dnsutils zip unzip ethtool uuid file iproute acpid iptables-persistent sysstat
+ chroot . apt-get --no-install-recommends -q -y --force-yes install rsyslog logrotate cron chkconfig insserv net-tools ifupdown vim-tiny netbase iptables openssh-server grub e2fsprogs dhcp3-client dnsmasq tcpdump socat wget python bzip2 sed gawk diff grep gzip less tar telnet traceroute psmisc lsof procps monit inetutils-ping iputils-arping httping dnsutils zip unzip ethtool uuid file iproute acpid iptables-persistent sysstat
#apache
chroot . apt-get --no-install-recommends -q -y --force-yes install apache2 ssl-cert
#haproxy
@@ -422,6 +426,7 @@ cleanup() {
signature() {
(cd ${scriptdir}/config; tar czf ${MOUNTPOINT}/usr/share/cloud/cloud-scripts.tgz *)
md5sum ${MOUNTPOINT}/usr/share/cloud/cloud-scripts.tgz |awk '{print $1}' > ${MOUNTPOINT}/var/cache/cloud/cloud-scripts-signature
+ echo "Cloudstack Release 2.2 $(date)" > ${MOUNTPOUNT}/etc/cloudstack-release
}
mkdir -p $IMAGENAME
@@ -487,6 +492,9 @@ services
echo "*************CONFIGURING APACHE********************"
apache2
+echo "*************CONFIGURING VPN********************"
+vpn_config
+
echo "*************CLEANING UP********************"
cleanup
diff --git a/patches/systemvm/debian/config/etc/profile.d/cloud.sh b/patches/systemvm/debian/config/etc/profile.d/cloud.sh
new file mode 100755
index 00000000000..6d8ef23a399
--- /dev/null
+++ b/patches/systemvm/debian/config/etc/profile.d/cloud.sh
@@ -0,0 +1,4 @@
+if [ "`id -u`" -eq 0 ]; then
+ PATH=${PATH}:/opt/cloud/bin
+fi
+export PATH
diff --git a/patches/systemvm/debian/vpn/etc/ipsec.conf b/patches/systemvm/debian/vpn/etc/ipsec.conf
new file mode 100644
index 00000000000..a1c4bfb52b8
--- /dev/null
+++ b/patches/systemvm/debian/vpn/etc/ipsec.conf
@@ -0,0 +1,50 @@
+# /etc/ipsec.conf - Openswan IPsec configuration file
+
+# This file: /usr/share/doc/openswan/ipsec.conf-sample
+#
+# Manual: ipsec.conf.5
+
+
+version 2.0 # conforms to second version of ipsec.conf specification
+
+# basic configuration
+config setup
+ # Do not set debug options to debug configuration issues!
+ # plutodebug / klipsdebug = "all", "none" or a combation from below:
+ # "raw crypt parsing emitting control klips pfkey natt x509 dpd private"
+ # eg:
+ # plutodebug="control parsing"
+ #
+ # enable to get logs per-peer
+ # plutoopts="--perpeerlog"
+ #
+ # Again: only enable plutodebug or klipsdebug when asked by a developer
+ #
+ # NAT-TRAVERSAL support, see README.NAT-Traversal
+ nat_traversal=yes
+ # exclude networks used on server side by adding %v4:!a.b.c.0/24
+ virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12
+ # OE is now off by default. Uncomment and change to on, to enable.
+ oe=off
+ # which IPsec stack to use. auto will try netkey, then klips then mast
+ protostack=auto
+
+
+# Add connections here
+
+# sample VPN connection
+# for more examples, see /etc/ipsec.d/examples/
+#conn sample
+# # Left security gateway, subnet behind it, nexthop toward right.
+# left=10.0.0.1
+# leftsubnet=172.16.0.0/24
+# leftnexthop=10.22.33.44
+# # Right security gateway, subnet behind it, nexthop toward left.
+# right=10.12.12.1
+# rightsubnet=192.168.0.0/24
+# rightnexthop=10.101.102.103
+# # To authorize this connection, but not actually start it,
+# # at startup, uncomment this.
+# #auto=add
+
+include /etc/ipsec.d/*.conf
diff --git a/patches/systemvm/debian/vpn/etc/ipsec.conf.orig b/patches/systemvm/debian/vpn/etc/ipsec.conf.orig
new file mode 100644
index 00000000000..d185e6cd502
--- /dev/null
+++ b/patches/systemvm/debian/vpn/etc/ipsec.conf.orig
@@ -0,0 +1,48 @@
+# /etc/ipsec.conf - Openswan IPsec configuration file
+
+# This file: /usr/share/doc/openswan/ipsec.conf-sample
+#
+# Manual: ipsec.conf.5
+
+
+version 2.0 # conforms to second version of ipsec.conf specification
+
+# basic configuration
+config setup
+ # Do not set debug options to debug configuration issues!
+ # plutodebug / klipsdebug = "all", "none" or a combation from below:
+ # "raw crypt parsing emitting control klips pfkey natt x509 dpd private"
+ # eg:
+ # plutodebug="control parsing"
+ #
+ # enable to get logs per-peer
+ # plutoopts="--perpeerlog"
+ #
+ # Again: only enable plutodebug or klipsdebug when asked by a developer
+ #
+ # NAT-TRAVERSAL support, see README.NAT-Traversal
+ nat_traversal=yes
+ # exclude networks used on server side by adding %v4:!a.b.c.0/24
+ virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12
+ # OE is now off by default. Uncomment and change to on, to enable.
+ oe=off
+ # which IPsec stack to use. auto will try netkey, then klips then mast
+ protostack=auto
+
+
+# Add connections here
+
+# sample VPN connection
+# for more examples, see /etc/ipsec.d/examples/
+#conn sample
+# # Left security gateway, subnet behind it, nexthop toward right.
+# left=10.0.0.1
+# leftsubnet=172.16.0.0/24
+# leftnexthop=10.22.33.44
+# # Right security gateway, subnet behind it, nexthop toward left.
+# right=10.12.12.1
+# rightsubnet=192.168.0.0/24
+# rightnexthop=10.101.102.103
+# # To authorize this connection, but not actually start it,
+# # at startup, uncomment this.
+# #auto=add
diff --git a/patches/systemvm/debian/vpn/etc/ipsec.d/l2tp.conf b/patches/systemvm/debian/vpn/etc/ipsec.d/l2tp.conf
new file mode 100644
index 00000000000..7459e259a4e
--- /dev/null
+++ b/patches/systemvm/debian/vpn/etc/ipsec.d/l2tp.conf
@@ -0,0 +1,33 @@
+conn L2TP-PSK
+ authby=secret
+ pfs=no
+ rekey=no
+ keyingtries=3
+ #
+ # ----------------------------------------------------------
+ # The VPN server.
+ #
+ # Allow incoming connections on the external network interface.
+ # If you want to use a different interface or if there is no
+ # defaultroute, you can use: left=your.ip.addr.ess
+ #
+ left=172.26.0.151
+ #
+ leftprotoport=17/1701
+ # If you insist on supporting non-updated Windows clients,
+ # you can use: leftprotoport=17/%any
+ #
+ # ----------------------------------------------------------
+ # The remote user(s).
+ #
+ # Allow incoming connections only from this IP address.
+ right=%any
+ # If you want to allow multiple connections from any IP address,
+ # you can use: right=%any
+ #
+ rightprotoport=17/%any
+ #
+ # ----------------------------------------------------------
+ # Change 'ignore' to 'add' to enable this configuration.
+ #
+ auto=add
diff --git a/patches/systemvm/debian/vpn/etc/ipsec.secrets b/patches/systemvm/debian/vpn/etc/ipsec.secrets
new file mode 100644
index 00000000000..67ae69886cb
--- /dev/null
+++ b/patches/systemvm/debian/vpn/etc/ipsec.secrets
@@ -0,0 +1,12 @@
+# RCSID $Id: ipsec.secrets.proto,v 1.3.6.1 2005/09/28 13:59:14 paul Exp $
+# This file holds shared secrets or RSA private keys for inter-Pluto
+# authentication. See ipsec_pluto(8) manpage, and HTML documentation.
+
+# RSA private key for this host, authenticating it to any other host
+# which knows the public part. Suitable public keys, for ipsec.conf, DNS,
+# or configuration of other implementations, can be extracted conveniently
+# with "ipsec showhostkey".
+
+# this file is managed with debconf and will contain the automatically created RSA keys
+include /var/lib/openswan/ipsec.secrets.inc
+include /etc/ipsec.d/ipsec.*.secrets
diff --git a/patches/systemvm/debian/vpn/etc/ipsec.secrets.orig b/patches/systemvm/debian/vpn/etc/ipsec.secrets.orig
new file mode 100644
index 00000000000..6885545e8e8
--- /dev/null
+++ b/patches/systemvm/debian/vpn/etc/ipsec.secrets.orig
@@ -0,0 +1,11 @@
+# RCSID $Id: ipsec.secrets.proto,v 1.3.6.1 2005/09/28 13:59:14 paul Exp $
+# This file holds shared secrets or RSA private keys for inter-Pluto
+# authentication. See ipsec_pluto(8) manpage, and HTML documentation.
+
+# RSA private key for this host, authenticating it to any other host
+# which knows the public part. Suitable public keys, for ipsec.conf, DNS,
+# or configuration of other implementations, can be extracted conveniently
+# with "ipsec showhostkey".
+
+# this file is managed with debconf and will contain the automatically created RSA keys
+include /var/lib/openswan/ipsec.secrets.inc
diff --git a/patches/systemvm/debian/vpn/etc/ppp/options.xl2tpd b/patches/systemvm/debian/vpn/etc/ppp/options.xl2tpd
new file mode 100644
index 00000000000..08c301b098f
--- /dev/null
+++ b/patches/systemvm/debian/vpn/etc/ppp/options.xl2tpd
@@ -0,0 +1,14 @@
+proxyarp
+ipcp-accept-local
+ipcp-accept-remote
+noccp
+idle 1800
+auth
+crtscts
+mtu 1410
+mru 1410
+nodefaultroute
+debug
+lock
+connect-delay 5000
+ms-dns 10.1.1.1
diff --git a/patches/systemvm/debian/vpn/etc/xl2tpd/xl2tpd.conf b/patches/systemvm/debian/vpn/etc/xl2tpd/xl2tpd.conf
new file mode 100644
index 00000000000..574eab1461e
--- /dev/null
+++ b/patches/systemvm/debian/vpn/etc/xl2tpd/xl2tpd.conf
@@ -0,0 +1,6 @@
+[lns default]
+ip range = 10.1.9.2-10.1.9.8
+local ip = 10.1.9.1
+require chap = yes
+refuse pap = yes
+pppoptfile = /etc/ppp/options.xl2tpd
diff --git a/patches/systemvm/debian/vpn/etc/xl2tpd/xl2tpd.conf.orig b/patches/systemvm/debian/vpn/etc/xl2tpd/xl2tpd.conf.orig
new file mode 100644
index 00000000000..9f2f03a5048
--- /dev/null
+++ b/patches/systemvm/debian/vpn/etc/xl2tpd/xl2tpd.conf.orig
@@ -0,0 +1,76 @@
+;
+; Sample l2tpd configuration file
+;
+; This example file should give you some idea of how the options for l2tpd
+; should work. The best place to look for a list of all options is in
+; the source code itself, until I have the time to write better documetation :)
+; Specifically, the file "file.c" contains a list of commands at the end.
+;
+; You most definitely don't have to spell out everything as it is done here
+;
+; [global] ; Global parameters:
+; port = 1701 ; * Bind to port 1701
+; auth file = /etc/l2tpd/l2tp-secrets ; * Where our challenge secrets are
+; access control = yes ; * Refuse connections without IP match
+; rand source = dev ; Source for entropy for random
+; ; numbers, options are:
+; ; dev - reads of /dev/urandom
+; ; sys - uses rand()
+; ; egd - reads from egd socket
+; ; egd is not yet implemented
+;
+; [lns default] ; Our fallthrough LNS definition
+; exclusive = no ; * Only permit one tunnel per host
+; ip range = 192.168.0.1-192.168.0.20 ; * Allocate from this IP range
+; no ip range = 192.168.0.3-192.168.0.9 ; * Except these hosts
+; ip range = 192.168.0.5 ; * But this one is okay
+; ip range = lac1-lac2 ; * And anything from lac1 to lac2's IP
+; lac = 192.168.1.4 - 192.168.1.8 ; * These can connect as LAC's
+; no lac = untrusted.marko.net ; * This guy can't connect
+; hidden bit = no ; * Use hidden AVP's?
+; local ip = 192.168.1.2 ; * Our local IP to use
+; length bit = yes ; * Use length bit in payload?
+; require chap = yes ; * Require CHAP auth. by peer
+; refuse pap = yes ; * Refuse PAP authentication
+; refuse chap = no ; * Refuse CHAP authentication
+; refuse authentication = no ; * Refuse authentication altogether
+; require authentication = yes ; * Require peer to authenticate
+; unix authentication = no ; * Use /etc/passwd for auth.
+; name = myhostname ; * Report this as our hostname
+; ppp debug = no ; * Turn on PPP debugging
+; pppoptfile = /etc/ppp/options.l2tpd.lns ; * ppp options file
+; call rws = 10 ; * RWS for call (-1 is valid)
+; tunnel rws = 4 ; * RWS for tunnel (must be > 0)
+; flow bit = yes ; * Include sequence numbers
+; challenge = yes ; * Challenge authenticate peer ;
+; rx bps = 10000000 ; Receive tunnel speed
+; tx bps = 10000000 ; Transmit tunnel speed
+; bps = 100000 ; Define both receive and transmit speed in one option
+
+; [lac marko] ; Example VPN LAC definition
+; lns = lns.marko.net ; * Who is our LNS?
+; lns = lns2.marko.net ; * A backup LNS (not yet used)
+; redial = yes ; * Redial if disconnected?
+; redial timeout = 15 ; * Wait n seconds between redials
+; max redials = 5 ; * Give up after n consecutive failures
+; hidden bit = yes ; * User hidden AVP's?
+; local ip = 192.168.1.1 ; * Force peer to use this IP for us
+; remote ip = 192.168.1.2 ; * Force peer to use this as their IP
+; length bit = no ; * Use length bit in payload?
+; require pap = no ; * Require PAP auth. by peer
+; require chap = yes ; * Require CHAP auth. by peer
+; refuse pap = yes ; * Refuse PAP authentication
+; refuse chap = no ; * Refuse CHAP authentication
+; refuse authentication = no ; * Refuse authentication altogether
+; require authentication = yes ; * Require peer to authenticate
+; name = marko ; * Report this as our hostname
+; ppp debug = no ; * Turn on PPP debugging
+; pppoptfile = /etc/ppp/options.l2tpd.marko ; * ppp options file for this lac
+; call rws = 10 ; * RWS for call (-1 is valid)
+; tunnel rws = 4 ; * RWS for tunnel (must be > 0)
+; flow bit = yes ; * Include sequence numbers
+; challenge = yes ; * Challenge authenticate peer
+;
+; [lac cisco] ; Another quick LAC
+; lns = cisco.marko.net ; * Required, but can take from default
+; require authentication = yes
diff --git a/patches/systemvm/debian/vpn/opt/cloud/bin/vpn_l2tp.sh b/patches/systemvm/debian/vpn/opt/cloud/bin/vpn_l2tp.sh
new file mode 100755
index 00000000000..fbb6659ae40
--- /dev/null
+++ b/patches/systemvm/debian/vpn/opt/cloud/bin/vpn_l2tp.sh
@@ -0,0 +1,151 @@
+#!/bin/bash
+
+set -x
+usage() {
+ printf "Usage: %s: \n" $(basename $0)
+}
+
+get_intf_ip() {
+ ip addr show $1 | grep -w inet | awk '{print $2}' | awk -F'/' '{print $1}'
+}
+
+
+iptables_() {
+ local op=$1
+ local public_if="eth2"
+ local subnet_if="eth0"
+ local subnet_ip=$(get_intf_ip $subnet_if)
+
+ iptables $op INPUT -i $public_if -p udp -m udp --dport 1701 -j ACCEPT
+ iptables $op INPUT -i $public_if -p udp -m udp --dport 500 -j ACCEPT
+ iptables $op INPUT -i $public_if -p udp -m udp --dport 4500 -j ACCEPT
+ iptables $op INPUT -i eth2 -p ah -j ACCEPT
+ iptables $op INPUT -i eth2 -p esp -j ACCEPT
+ iptables $op FORWARD -i ppp+ -o $subnet_if -j ACCEPT
+ iptables $op FORWARD -i $subnet_if -o ppp+ -j ACCEPT
+ iptables $op FORWARD -i ppp+ -o ppp+ -j ACCEPT
+ iptables $op INPUT -i ppp+ -m udp -p udp --dport 53 -j ACCEPT
+ iptables -t nat $op PREROUTING -i ppp+ -p udp -m udp --dport 53 -j DNAT --to-destination $subnet_ip
+
+}
+
+ipsec_server() {
+ local op=$1
+ if [ "$op" == "restart" ]; then
+ service ipsec stop
+ service xl2tpd stop
+ service ipsec start
+ service xl2tpd start
+ return $?
+ fi
+ service ipsec $op
+ service xl2tpd $op
+}
+
+create_l2tp_ipsec_vpn_server() {
+ local ipsec_psk=$1
+ local server_ip=$2
+ local client_range=$3
+ local local_ip=$4
+
+ sed -i -e "s/left=.*$/left=$server_ip/" /etc/ipsec.d/l2tp.conf
+ echo ": PSK \"$ipsec_psk\"" > /etc/ipsec.d/ipsec.any.secrets
+ sed -i -e "s/^ip range = .*$/ip range = $client_range/" /etc/xl2tpd/xl2tpd.conf
+ sed -i -e "s/^local ip = .*$/local ip = $local_ip/" /etc/xl2tpd/xl2tpd.conf
+
+ sed -i -e "s/^ms-dns.*$/ms-dns $local_ip/" /etc/ppp/options.xl2tpd
+
+ iptables_ "-D"
+ iptables_ "-I"
+
+ ipsec_server "restart"
+
+ ipsec auto --rereadsecrets
+ ipsec auto --replace L2TP-PSK
+}
+
+destroy_l2tp_ipsec_vpn_server() {
+
+ ipsec auto --down L2TP-PSK
+
+ iptables_ "-D"
+
+ ipsec_server "stop"
+}
+
+remove_l2tp_ipsec_user() {
+ local u=$1
+ sed -i -e "/^$u .*$/d" /etc/ppp/chap-secrets
+}
+
+add_l2tp_ipsec_user() {
+ local u=$1
+ local passwd=$2
+
+ remove_l2tp_ipsec_user $u
+ echo "$u * $passwd *" >> /etc/ppp/chap-secrets
+}
+
+rflag=
+pflag=
+lflag=
+sflag=
+create=
+destroy=
+useradd=
+userdel=
+
+while getopts 'cdl:p:r:s:u:U:' OPTION
+do
+ case $OPTION in
+ c) create=1
+ ;;
+ d) destroy=1
+ ;;
+ u) useradd=1
+ user_pwd="$OPTARG"
+ ;;
+ U) userdel=1
+ user="$OPTARG"
+ ;;
+ r) rflag=1
+ client_range="$OPTARG"
+ ;;
+ p) pflag=1
+ ipsec_psk="$OPTARG"
+ ;;
+ l) lflag=1
+ local_ip="$OPTARG"
+ ;;
+ s) sflag=1
+ server_ip="$OPTARG"
+ ;;
+ ?) usage
+ exit 2
+ ;;
+ esac
+done
+
+[ "$create$destroy" == "11" ] || [ "$create$destroy$useradd$userdel" == "" ] && usage && exit 2
+[ "$create" == "1" ] && [ "$lflag$pflag$rflag$sflag" != "1111" ] && usage && exit 2
+
+if [ "$create" == "1" ]; then
+ create_l2tp_ipsec_vpn_server $ipsec_psk $server_ip $client_range $local_ip
+ exit $?
+fi
+
+if [ "$destroy" == "1" ]; then
+ destroy_l2tp_ipsec_vpn_server
+ exit $?
+fi
+
+if [ "$useradd" == "1" ]; then
+ u=$(echo $user_pwd | awk -F',' '{print $1}')
+ pwd=$(echo $user_pwd | awk -F',' '{print $2}')
+ add_l2tp_ipsec_user $u $pwd
+ exit $?
+fi
+if [ "$userdel" == "1" ]; then
+ remove_l2tp_ipsec_user $user
+ exit $?
+fi
diff --git a/scripts/network/domr/l2tp_vpn.sh b/scripts/network/domr/l2tp_vpn.sh
new file mode 100755
index 00000000000..d562dd61c93
--- /dev/null
+++ b/scripts/network/domr/l2tp_vpn.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+#
+# @VERSION@
+
+cert="/root/.ssh/id_rsa.cloud"
+domr=$1
+shift
+ssh -p 3922 -o StrictHostKeyChecking=no -i $cert root@$domr "/opt/cloud/bin/vpn_l2tp.sh $*" >/dev/null
+
+exit $?
diff --git a/scripts/vm/hypervisor/xenserver/vmops b/scripts/vm/hypervisor/xenserver/vmops
index 2c45006d2f7..52aceb78250 100755
--- a/scripts/vm/hypervisor/xenserver/vmops
+++ b/scripts/vm/hypervisor/xenserver/vmops
@@ -271,6 +271,21 @@ def saveDhcpEntry(session, args):
txt = ''
return txt
+
+@echo
+def lt2p_vpn(session, args):
+ sargs = args['args']
+ cmd = sargs.split(' ')
+ cmd.insert(0, "/opt/xensource/bin/l2tp_vpn.sh")
+ cmd.insert(0, "/bin/bash")
+ try:
+ txt = util.pread2(cmd)
+ txt = 'success'
+ except:
+ util.SMlog("l2tp vpn failed " )
+ txt = ''
+
+ return txt
@echo
def setLinkLocalIP(session, args):
@@ -1088,5 +1103,5 @@ def network_rules(session, args):
if __name__ == "__main__":
- XenAPIPlugin.dispatch({"pingtest": pingtest, "setup_iscsi":setup_iscsi, "gethostvmstats": gethostvmstats, "getvncport": getvncport, "getgateway": getgateway, "getnetwork": getnetwork, "preparemigration": preparemigration, "setIptables": setIptables, "patchdomr": patchdomr, "pingdomr": pingdomr, "pingxenserver": pingxenserver, "ipassoc": ipassoc, "vm_data": vm_data, "savePassword": savePassword, "saveDhcpEntry": saveDhcpEntry, "setFirewallRule": setFirewallRule, "setLoadBalancerRule": setLoadBalancerRule, "createFile": createFile, "deleteFile": deleteFile, "checkMount": checkMount, "checkIscsi": checkIscsi, "networkUsage": networkUsage, "network_rules":network_rules, "can_bridge_firewall":can_bridge_firewall, "default_network_rules":default_network_rules, "destroy_network_rules_for_vm":destroy_network_rules_for_vm, "default_network_rules_systemvm":default_network_rules_systemvm, "get_rule_logs_for_vms":get_rule_logs_for_vms, "setLinkLocalIP":setLinkLocalIP})
+ XenAPIPlugin.dispatch({"pingtest": pingtest, "setup_iscsi":setup_iscsi, "gethostvmstats": gethostvmstats, "getvncport": getvncport, "getgateway": getgateway, "getnetwork": getnetwork, "preparemigration": preparemigration, "setIptables": setIptables, "patchdomr": patchdomr, "pingdomr": pingdomr, "pingxenserver": pingxenserver, "ipassoc": ipassoc, "vm_data": vm_data, "savePassword": savePassword, "saveDhcpEntry": saveDhcpEntry, "setFirewallRule": setFirewallRule, "setLoadBalancerRule": setLoadBalancerRule, "createFile": createFile, "deleteFile": deleteFile, "checkMount": checkMount, "checkIscsi": checkIscsi, "networkUsage": networkUsage, "network_rules":network_rules, "can_bridge_firewall":can_bridge_firewall, "default_network_rules":default_network_rules, "destroy_network_rules_for_vm":destroy_network_rules_for_vm, "default_network_rules_systemvm":default_network_rules_systemvm, "get_rule_logs_for_vms":get_rule_logs_for_vms, "setLinkLocalIP":setLinkLocalIP, "lt2p_vpn":lt2p_vpn})
diff --git a/scripts/vm/hypervisor/xenserver/xenserver56/patch b/scripts/vm/hypervisor/xenserver/xenserver56/patch
index 29cbcd95d05..0fb3ed9cfd2 100644
--- a/scripts/vm/hypervisor/xenserver/xenserver56/patch
+++ b/scripts/vm/hypervisor/xenserver/xenserver56/patch
@@ -36,3 +36,4 @@ save_password_to_domr.sh=../../../../network/domr/,0755,/opt/xensource/bin
networkUsage.sh=../../../../network/domr/,0755,/opt/xensource/bin
call_firewall.sh=../../../../network/domr/,0755,/opt/xensource/bin
call_loadbalancer.sh=../../../../network/domr/,0755,/opt/xensource/bin
+l2tp_vpn.sh=../../../../network/domr/,0755,/opt/xensource/bin
diff --git a/server/src/com/cloud/api/commands/CreateRemoteAccessVpnCmd.java b/server/src/com/cloud/api/commands/CreateRemoteAccessVpnCmd.java
new file mode 100644
index 00000000000..4f0f86a69ea
--- /dev/null
+++ b/server/src/com/cloud/api/commands/CreateRemoteAccessVpnCmd.java
@@ -0,0 +1,148 @@
+/**
+ * Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package com.cloud.api.commands;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.api.ApiDBUtils;
+import com.cloud.api.BaseAsyncCreateCmd;
+import com.cloud.api.Implementation;
+import com.cloud.api.Parameter;
+import com.cloud.api.response.RemoteAccessVpnResponse;
+import com.cloud.event.EventTypes;
+import com.cloud.network.NetworkManager;
+import com.cloud.network.RemoteAccessVpnVO;
+import com.cloud.user.Account;
+import com.cloud.user.UserContext;
+
+@Implementation(createMethod="createRemoteAccessVpn", method="startRemoteAccessVpn", manager=NetworkManager.class, description="Creates a l2tp/ipsec remote access vpn")
+public class CreateRemoteAccessVpnCmd extends BaseAsyncCreateCmd {
+ public static final Logger s_logger = Logger.getLogger(CreateRemoteAccessVpnCmd.class.getName());
+
+ private static final String s_name = "createremoteaccessvpnresponse";
+
+ /////////////////////////////////////////////////////
+ //////////////// API parameters /////////////////////
+ /////////////////////////////////////////////////////
+ @Parameter(name="zoneid", type=CommandType.LONG, required=true, description="zone id where the vpn server needs to be created")
+ private Long zoneId;
+
+ @Parameter(name="publicip", type=CommandType.STRING, required=false, description="public ip address of the vpn server")
+ private String publicIp;
+
+ @Parameter(name="iprange", type=CommandType.STRING, required=false, description="the range of ip addresses to allocate to vpn clients. The first ip in the range will be taken by the vpn server")
+ private String ipRange;
+
+ @Parameter(name="account", type=CommandType.STRING, description="an optional account for the virtual machine. Must be used with domainId.")
+ private String accountName;
+
+ @Parameter(name="domainid", type=CommandType.LONG, description="an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used.")
+ private Long domainId;
+
+ /////////////////////////////////////////////////////
+ /////////////////// Accessors ///////////////////////
+ /////////////////////////////////////////////////////
+ public String getPublicIp() {
+ return publicIp;
+ }
+
+ public String getAccountName() {
+ return accountName;
+ }
+
+ public Long getDomainId() {
+ return domainId;
+ }
+
+ public void setPublicIp(String publicIp) {
+ this.publicIp = publicIp;
+ }
+
+ public String getIpRange() {
+ return ipRange;
+ }
+
+ public void setIpRange(String ipRange) {
+ this.ipRange = ipRange;
+ }
+
+ public void setZoneId(Long zoneId) {
+ this.zoneId = zoneId;
+ }
+
+ public Long getZoneId() {
+ return zoneId;
+ }
+ /////////////////////////////////////////////////////
+ /////////////// API Implementation///////////////////
+ /////////////////////////////////////////////////////
+
+
+
+ public String getName() {
+ return s_name;
+ }
+
+ @Override @SuppressWarnings("unchecked")
+ public RemoteAccessVpnResponse getResponse() {
+ RemoteAccessVpnVO responseObj = (RemoteAccessVpnVO)getResponseObject();
+
+ RemoteAccessVpnResponse response = new RemoteAccessVpnResponse();
+ response.setId(responseObj.getId());
+ response.setPublicIp(responseObj.getVpnServerAddress());
+ response.setIpRange(responseObj.getIpRange());
+ response.setAccountName(responseObj.getAccountName());
+ response.setDomainId(responseObj.getDomainId());
+ response.setDomainName(ApiDBUtils.findDomainById(responseObj.getDomainId()).getName());
+ response.setResponseName(getName());
+ return response;
+ }
+
+ @Override
+ public long getAccountId() {
+ Account account = (Account)UserContext.current().getAccount();
+ if ((account == null) || isAdmin(account.getType())) {
+ if ((domainId != null) && (accountName != null)) {
+ Account userAccount = ApiDBUtils.findAccountByNameDomain(accountName, domainId);
+ if (userAccount != null) {
+ return userAccount.getId();
+ }
+ }
+ }
+
+ if (account != null) {
+ return account.getId();
+ }
+
+ return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked
+ }
+
+ @Override
+ public String getEventDescription() {
+ return "Create Remote Access VPN for account " + getAccountId() + " in zone " + getZoneId();
+ }
+
+ @Override
+ public String getEventType() {
+ return EventTypes.EVENT_REMOTE_ACCESS_VPN_CREATE;
+ }
+
+
+
+}
diff --git a/server/src/com/cloud/api/commands/DeleteRemoteAccessVpnCmd.java b/server/src/com/cloud/api/commands/DeleteRemoteAccessVpnCmd.java
new file mode 100644
index 00000000000..9730191384c
--- /dev/null
+++ b/server/src/com/cloud/api/commands/DeleteRemoteAccessVpnCmd.java
@@ -0,0 +1,120 @@
+/**
+ * Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package com.cloud.api.commands;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.api.ApiDBUtils;
+import com.cloud.api.BaseAsyncCmd;
+import com.cloud.api.Implementation;
+import com.cloud.api.Parameter;
+import com.cloud.api.response.SuccessResponse;
+import com.cloud.event.EventTypes;
+import com.cloud.network.NetworkManager;
+import com.cloud.user.Account;
+import com.cloud.user.UserContext;
+
+@Implementation(method="destroyRemoteAccessVpn", manager=NetworkManager.class, description="Destroys a l2tp/ipsec remote access vpn")
+public class DeleteRemoteAccessVpnCmd extends BaseAsyncCmd {
+ public static final Logger s_logger = Logger.getLogger(DeleteRemoteAccessVpnCmd.class.getName());
+
+ private static final String s_name = "deleteremoteaccessvpnresponse";
+
+ /////////////////////////////////////////////////////
+ //////////////// API parameters /////////////////////
+ /////////////////////////////////////////////////////
+ @Parameter(name="zoneid", type=CommandType.LONG, required=true, description="zone id where the vpn server needs to be created")
+ private Long zoneId;
+
+ @Parameter(name="account", type=CommandType.STRING, description="an optional account for the virtual machine. Must be used with domainId.")
+ private String accountName;
+
+ @Parameter(name="domainid", type=CommandType.LONG, description="an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used.")
+ private Long domainId;
+
+ /////////////////////////////////////////////////////
+ /////////////////// Accessors ///////////////////////
+ /////////////////////////////////////////////////////
+
+ public void setZoneId(Long zoneId) {
+ this.zoneId = zoneId;
+ }
+
+ public Long getZoneId() {
+ return zoneId;
+ }
+
+
+ public String getAccountName() {
+ return accountName;
+ }
+
+ public Long getDomainId() {
+ return domainId;
+ }
+ /////////////////////////////////////////////////////
+ /////////////// API Implementation///////////////////
+ /////////////////////////////////////////////////////
+
+
+ public String getName() {
+ return s_name;
+ }
+
+ @Override @SuppressWarnings("unchecked")
+ public SuccessResponse getResponse() {
+ Boolean success = (Boolean)getResponseObject();
+ SuccessResponse response = new SuccessResponse();
+ response.setSuccess(success);
+ response.setResponseName(getName());
+ return response;
+ }
+
+ @Override
+ public long getAccountId() {
+ Account account = (Account)UserContext.current().getAccount();
+ if ((account == null) || isAdmin(account.getType())) {
+ if ((domainId != null) && (accountName != null)) {
+ Account userAccount = ApiDBUtils.findAccountByNameDomain(accountName, domainId);
+ if (userAccount != null) {
+ return userAccount.getId();
+ }
+ }
+ }
+
+ if (account != null) {
+ return account.getId();
+ }
+
+ return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked
+ }
+
+ @Override
+ public String getEventDescription() {
+ return "Delete Remote Access VPN for account " + getAccountId() + " in zone " + getZoneId();
+ }
+
+ @Override
+ public String getEventType() {
+ return EventTypes.EVENT_REMOTE_ACCESS_VPN_DESTROY;
+ }
+
+
+
+}
diff --git a/server/src/com/cloud/api/commands/ListRemoteAccessVpnsCmd.java b/server/src/com/cloud/api/commands/ListRemoteAccessVpnsCmd.java
new file mode 100644
index 00000000000..fe01cdf098b
--- /dev/null
+++ b/server/src/com/cloud/api/commands/ListRemoteAccessVpnsCmd.java
@@ -0,0 +1,129 @@
+/**
+ * Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package com.cloud.api.commands;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.api.ApiDBUtils;
+import com.cloud.api.BaseListCmd;
+import com.cloud.api.Implementation;
+import com.cloud.api.Parameter;
+import com.cloud.api.response.RemoteAccessVpnResponse;
+import com.cloud.api.response.ListResponse;
+import com.cloud.api.response.LoadBalancerResponse;
+import com.cloud.network.LoadBalancerVO;
+import com.cloud.network.RemoteAccessVpnVO;
+import com.cloud.user.Account;
+
+@Implementation(method="searchForRemoteAccessVpns", description="Lists remote access vpns")
+public class ListRemoteAccessVpnsCmd extends BaseListCmd {
+ public static final Logger s_logger = Logger.getLogger (ListRemoteAccessVpnsCmd.class.getName());
+
+ private static final String s_name = "listremoteaccessvpnsresponse";
+
+ /////////////////////////////////////////////////////
+ //////////////// API parameters /////////////////////
+ /////////////////////////////////////////////////////
+
+ @Parameter(name="account", type=CommandType.STRING, description="the account of the remote access vpn. Must be used with the domainId parameter.")
+ private String accountName;
+
+ @Parameter(name="domainid", type=CommandType.LONG, description="the domain ID of the remote access vpn rule. If used with the account parameter, lists remote access vpns for the account in the specified domain.")
+ private Long domainId;
+
+ @Parameter(name="id", type=CommandType.LONG, description="the ID of the remote access vpn")
+ private Long id;
+
+ @Parameter(name="zoneid", type=CommandType.LONG, description="the zone ID of the remote access vpn rule")
+ private Long zoneId;
+
+ @Parameter(name="publicip", type=CommandType.STRING, description="the public IP address of the remote access vpn ")
+ private String publicIp;
+
+
+ /////////////////////////////////////////////////////
+ /////////////////// Accessors ///////////////////////
+ /////////////////////////////////////////////////////
+
+ public String getAccountName() {
+ return accountName;
+ }
+
+ public Long getDomainId() {
+ return domainId;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setZoneId(Long zoneId) {
+ this.zoneId = zoneId;
+ }
+
+ public Long getZoneId() {
+ return zoneId;
+ }
+
+ public String getPublicIp() {
+ return publicIp;
+ }
+
+
+ /////////////////////////////////////////////////////
+ /////////////// API Implementation///////////////////
+ /////////////////////////////////////////////////////
+
+ @Override
+ public String getName() {
+ return s_name;
+ }
+
+ @Override @SuppressWarnings("unchecked")
+ public ListResponse getResponse() {
+ List vpns = (List)getResponseObject();
+
+ ListResponse response = new ListResponse();
+ List vpnResponses = new ArrayList();
+ for (RemoteAccessVpnVO vpn : vpns) {
+ RemoteAccessVpnResponse vpnResponse = new RemoteAccessVpnResponse();
+ vpnResponse.setId(vpn.getId());
+ vpnResponse.setPublicIp(vpn.getVpnServerAddress());
+ vpnResponse.setIpRange(vpn.getIpRange());
+ vpnResponse.setPresharedKey(vpn.getIpsecPresharedKey());
+ vpnResponse.setAccountName(vpn.getAccountName());
+
+ Account accountTemp = ApiDBUtils.findAccountById(vpn.getAccountId());
+ if (accountTemp != null) {
+ vpnResponse.setDomainId(accountTemp.getDomainId());
+ vpnResponse.setDomainName(ApiDBUtils.findDomainById(accountTemp.getDomainId()).getName());
+ }
+
+ vpnResponse.setResponseName("remoteaccessvpn");
+ vpnResponses.add(vpnResponse);
+ }
+
+ response.setResponses(vpnResponses);
+ response.setResponseName(getName());
+ return response;
+ }
+}
diff --git a/server/src/com/cloud/api/response/RemoteAccessVpnResponse.java b/server/src/com/cloud/api/response/RemoteAccessVpnResponse.java
new file mode 100644
index 00000000000..41de0de2e5d
--- /dev/null
+++ b/server/src/com/cloud/api/response/RemoteAccessVpnResponse.java
@@ -0,0 +1,104 @@
+/**
+ * Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+package com.cloud.api.response;
+
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+
+public class RemoteAccessVpnResponse extends BaseResponse {
+ @SerializedName("id") @Param(description="the vpn ID")
+ private Long id;
+
+ @SerializedName("publicip") @Param(description="the public ip address of the vpn server")
+ private String publicIp;
+
+ @SerializedName("ipRange") @Param(description="the range of ips to allocate to the clients")
+ private String ipRange;
+
+ @SerializedName("presharedkey") @Param(description="the ipsec preshared key")
+ private String presharedKey;
+
+ @SerializedName("account") @Param(description="the account of the remote access vpn")
+ private String accountName;
+
+ @SerializedName("domainid") @Param(description="the domain id of the account of the remote access vpn")
+ private long domainId;
+
+ @SerializedName("domainname") @Param(description="the domain name of the account of the remote access vpn")
+ private String domainName;
+
+ public String getAccountName() {
+ return accountName;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getPublicIp() {
+ return publicIp;
+ }
+
+ public void setPublicIp(String publicIp) {
+ this.publicIp = publicIp;
+ }
+
+ public String getIpRange() {
+ return ipRange;
+ }
+
+ public void setIpRange(String ipRange) {
+ this.ipRange = ipRange;
+ }
+
+ public String getPresharedKey() {
+ return presharedKey;
+ }
+
+ public void setPresharedKey(String presharedKey) {
+ this.presharedKey = presharedKey;
+ }
+
+ public void setAccountName(String accountName) {
+ this.accountName = accountName;
+
+ }
+
+ public void setDomainId(long domainId) {
+ this.domainId = domainId;
+
+ }
+
+ public void setDomainName(String name) {
+ this.domainName = name;
+ }
+
+ public long getDomainId() {
+ return domainId;
+ }
+
+ public String getDomainName() {
+ return domainName;
+ }
+
+
+}
diff --git a/server/src/com/cloud/network/NetworkManager.java b/server/src/com/cloud/network/NetworkManager.java
index 937dd020fa3..d44c60d483e 100644
--- a/server/src/com/cloud/network/NetworkManager.java
+++ b/server/src/com/cloud/network/NetworkManager.java
@@ -25,8 +25,11 @@ import com.cloud.api.commands.AssignToLoadBalancerRuleCmd;
import com.cloud.api.commands.AssociateIPAddrCmd;
import com.cloud.api.commands.CreateIPForwardingRuleCmd;
import com.cloud.api.commands.CreateLoadBalancerRuleCmd;
+import com.cloud.api.commands.CreateRemoteAccessVpnCmd;
import com.cloud.api.commands.DeleteIPForwardingRuleCmd;
import com.cloud.api.commands.DeleteLoadBalancerRuleCmd;
+import com.cloud.api.commands.DeletePortForwardingServiceRuleCmd;
+import com.cloud.api.commands.DeleteRemoteAccessVpnCmd;
import com.cloud.api.commands.DisassociateIPAddrCmd;
import com.cloud.api.commands.ListPortForwardingRulesCmd;
import com.cloud.api.commands.RebootRouterCmd;
@@ -318,4 +321,29 @@ public interface NetworkManager extends Manager {
List setupNetworkConfiguration(Account owner, ServiceOfferingVO offering, DeploymentPlan plan);
String assignSourceNatIpAddress(Account account, DataCenter dc) throws InsufficientAddressCapacityException;
+ /**
+ * Create a remote access vpn from the given public ip address and client ip range
+ * @param cmd the command specifying the ip address, ip range
+ * @return the newly created RemoteAccessVpnVO if successful, null otherwise
+ * @throws InvalidParameterValueException
+ * @throws PermissionDeniedException
+ * @throws ConcurrentOperationException
+ */
+ public RemoteAccessVpnVO createRemoteAccessVpn(CreateRemoteAccessVpnCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, ConcurrentOperationException;
+
+ /**
+ * Start a remote access vpn for the given public ip address and client ip range
+ * @param cmd the command specifying the ip address, ip range
+ * @return the RemoteAccessVpnVO if successful, null otherwise
+ * @throws ConcurrentOperationException
+ */
+ public RemoteAccessVpnVO startRemoteAccessVpn(CreateRemoteAccessVpnCmd cmd) throws ConcurrentOperationException;
+
+ /**
+ * Destroy a previously created remote access VPN
+ * @param cmd the command specifying the account and zone
+ * @return success if successful, false otherwise
+ * @throws ConcurrentOperationException
+ */
+ public boolean destroyRemoteAccessVpn(DeleteRemoteAccessVpnCmd cmd) throws ConcurrentOperationException;
}
diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java
index 05487302d82..9116bb0fc3f 100755
--- a/server/src/com/cloud/network/NetworkManagerImpl.java
+++ b/server/src/com/cloud/network/NetworkManagerImpl.java
@@ -47,8 +47,11 @@ import com.cloud.api.commands.AssignToLoadBalancerRuleCmd;
import com.cloud.api.commands.AssociateIPAddrCmd;
import com.cloud.api.commands.CreateIPForwardingRuleCmd;
import com.cloud.api.commands.CreateLoadBalancerRuleCmd;
+import com.cloud.api.commands.CreateRemoteAccessVpnCmd;
import com.cloud.api.commands.DeleteIPForwardingRuleCmd;
import com.cloud.api.commands.DeleteLoadBalancerRuleCmd;
+import com.cloud.api.commands.DeletePortForwardingServiceRuleCmd;
+import com.cloud.api.commands.DeleteRemoteAccessVpnCmd;
import com.cloud.api.commands.DisassociateIPAddrCmd;
import com.cloud.api.commands.ListPortForwardingRulesCmd;
import com.cloud.api.commands.RebootRouterCmd;
@@ -66,8 +69,8 @@ import com.cloud.configuration.dao.ResourceLimitDao;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.HostPodVO;
-import com.cloud.dc.Vlan.VlanType;
import com.cloud.dc.VlanVO;
+import com.cloud.dc.Vlan.VlanType;
import com.cloud.dc.dao.AccountVlanMapDao;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.dc.dao.HostPodDao;
@@ -103,6 +106,9 @@ import com.cloud.network.dao.LoadBalancerDao;
import com.cloud.network.dao.LoadBalancerVMMapDao;
import com.cloud.network.dao.NetworkConfigurationDao;
import com.cloud.network.dao.NetworkRuleConfigDao;
+import com.cloud.network.dao.RemoteAccessVpnDao;
+import com.cloud.network.dao.SecurityGroupDao;
+import com.cloud.network.dao.SecurityGroupVMMapDao;
import com.cloud.network.element.NetworkElement;
import com.cloud.network.router.DomainRouterManager;
import com.cloud.offering.NetworkOffering;
@@ -131,16 +137,17 @@ import com.cloud.user.dao.UserDao;
import com.cloud.user.dao.UserStatisticsDao;
import com.cloud.uservm.UserVm;
import com.cloud.utils.Pair;
+import com.cloud.utils.PasswordGenerator;
import com.cloud.utils.StringUtils;
import com.cloud.utils.Ternary;
import com.cloud.utils.component.Adapters;
import com.cloud.utils.component.Inject;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.JoinBuilder;
-import com.cloud.utils.db.JoinBuilder.JoinType;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.JoinBuilder.JoinType;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.DomainRouter;
@@ -200,6 +207,7 @@ public class NetworkManagerImpl implements NetworkManager, DomainRouterService {
@Inject NetworkConfigurationDao _networkConfigDao = null;
@Inject NicDao _nicDao;
@Inject GuestOSDao _guestOSDao = null;
+ @Inject RemoteAccessVpnDao _remoteAccessVpnDao = null;
@Inject DomainRouterManager _routerMgr;
@Inject(adapter=NetworkGuru.class)
@@ -588,6 +596,37 @@ public class NetworkManagerImpl implements NetworkManager, DomainRouterService {
return true;
}
+ /** Returns the target account for an api command
+ * @param accountName - non-null if the account name was passed in in the command
+ * @param domainId - non-null if the domainId was passed in in the command.
+ * @return
+ */
+ protected Account getAccountForApiCommand(String accountName, Long domainId) throws InvalidParameterValueException, PermissionDeniedException{
+ Account account = UserContext.current().getAccount();
+
+ if ((account == null) || isAdmin(account.getType())) {
+ //The admin is making the call, determine if it is for someone else or for himself
+ if (domainId != null) {
+ if ((account != null) && !_domainDao.isChildDomain(account.getDomainId(), domainId)) {
+ throw new PermissionDeniedException("Invalid domain id (" + domainId + ") given, , permission denied");
+ }
+ if (accountName != null) {
+ Account userAccount = _accountDao.findActiveAccount(accountName, domainId);
+ if (userAccount != null) {
+ account = userAccount;
+ } else {
+ throw new PermissionDeniedException("Unable to find account " + accountName + " in domain " + domainId + ", permission denied");
+ }
+ }
+ } else if (account != null) {
+ // the admin is calling the api on his own behalf
+ return account;
+ } else {
+ throw new InvalidParameterValueException("Account information is not specified.");
+ }
+ }
+ return account;
+ }
@Override @DB
public IPAddressVO associateIP(AssociateIPAddrCmd cmd) throws ResourceAllocationException, InsufficientAddressCapacityException, InvalidParameterValueException, InternalErrorException, PermissionDeniedException {
@@ -2547,5 +2586,187 @@ public class NetworkManagerImpl implements NetworkManager, DomainRouterService {
NetworkOfferingVO networkOffering = _networkOfferingDao.findByServiceOffering(offering);
return setupNetworkConfiguration(owner, networkOffering, plan);
}
+
+ @Override
+ @DB
+ public RemoteAccessVpnVO createRemoteAccessVpn(CreateRemoteAccessVpnCmd cmd)
+ throws InvalidParameterValueException, PermissionDeniedException, ConcurrentOperationException {
+ String publicIp = cmd.getPublicIp();
+ IPAddressVO ipAddr = null;
+ Account account = getAccountForApiCommand(cmd.getAccountName(), cmd.getDomainId());
+ if (publicIp == null) {
+ List accountAddrs = _ipAddressDao.listByAccount(account.getId());
+ for (IPAddressVO addr: accountAddrs){
+ if (addr.getSourceNat() && addr.getDataCenterId() == cmd.getZoneId()){
+ ipAddr = addr;
+ publicIp = ipAddr.getAddress();
+ break;
+ }
+ }
+ if (ipAddr == null) {
+ throw new InvalidParameterValueException("Account " + account.getAccountName() + " does not have any public ip addresses in zone " + cmd.getZoneId());
+ }
+ }
+
+ // make sure ip address exists
+ ipAddr = _ipAddressDao.findById(publicIp);
+ if (ipAddr == null) {
+ throw new InvalidParameterValueException("Unable to create remote access vpn, invalid public IP address " + publicIp);
+ }
+
+ VlanVO vlan = _vlanDao.findById(ipAddr.getVlanDbId());
+ if (vlan != null) {
+ if (!VlanType.VirtualNetwork.equals(vlan.getVlanType())) {
+ throw new InvalidParameterValueException("Unable to create VPN for IP address " + publicIp + ", only VirtualNetwork type IP addresses can be used for VPN.");
+ }
+ }
+ assert vlan != null:"Inconsistent DB state -- ip address does not belong to any vlan?";
+
+ if ((ipAddr.getAccountId() == null) || (ipAddr.getAllocated() == null)) {
+ throw new PermissionDeniedException("Unable to create VPN, permission denied for ip " + publicIp);
+ }
+
+ if (account != null) {
+ if ((account.getType() == Account.ACCOUNT_TYPE_ADMIN) || (account.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN)) {
+ if (!_domainDao.isChildDomain(account.getDomainId(), ipAddr.getDomainId())) {
+ throw new PermissionDeniedException("Unable to create VPN with public IP address " + publicIp + ", permission denied.");
+ }
+ } else if (account.getId() != ipAddr.getAccountId().longValue()) {
+ throw new PermissionDeniedException("Unable to create VPN for account " + account.getAccountName() + " doesn't own ip address " + publicIp);
+ }
+ }
+
+ RemoteAccessVpnVO vpnVO = _remoteAccessVpnDao.findByPublicIpAddress(publicIp);
+ if (vpnVO != null) {
+ throw new InvalidParameterValueException("A Remote Access VPN already exists for this public Ip address");
+ }
+ //TODO: assumes one virtual network / domr per account per zone
+ vpnVO = _remoteAccessVpnDao.findByAccountAndZone(account.getId(), cmd.getZoneId());
+ if (vpnVO != null) {
+ throw new InvalidParameterValueException("A Remote Access VPN already exists for this account");
+ }
+ String ipRange = cmd.getIpRange();
+ if (ipRange == null) {
+ //TODO: get default range from database
+ ipRange = "10.1.2.1-10.1.2.8";
+ }
+ String [] range = ipRange.split("-");
+ if (range.length != 2) {
+ throw new InvalidParameterValueException("Invalid ip range");
+ }
+ if (!NetUtils.isValidIp(range[0]) || !NetUtils.isValidIp(range[1])){
+ throw new InvalidParameterValueException("Invalid ip range");
+ }
+ if (!NetUtils.validIpRange(range[0], range[1])){
+ throw new InvalidParameterValueException("Invalid ip range");
+ }
+ if (NetUtils.ipRangesOverlap(range[0], range[1], "10.1.1.1", "10.1.1.255")) {
+ throw new InvalidParameterValueException("Invalid ip range --- overlaps with guest ip range");
+ //TODO: get actual guest ip range from config db
+ }
+ //TODO: check sufficient range
+ //TODO: check overlap with private and public ip ranges in datacenter
+ //TODO: check overlap with port forwarding rules on this ip (udp ports 500, 4500, 1701)
+ long startIp = NetUtils.ip2Long(range[0]);
+ String newIpRange = NetUtils.long2Ip(++startIp) + "-" + range[1];
+ String sharedSecret = PasswordGenerator.generateRandomPassword(24);
+ //TODO: use SecureRandom in password generator
+ Transaction txn = Transaction.currentTxn();
+ txn.start();
+ boolean locked = false;
+ try {
+ ipAddr = _ipAddressDao.acquire(publicIp);
+ if (ipAddr == null) {
+ throw new ConcurrentOperationException("Another operation active, unable to create vpn");
+ }
+ locked = true;
+ vpnVO = new RemoteAccessVpnVO(account.getId(), cmd.getZoneId(), publicIp, range[0], newIpRange, sharedSecret);
+ _remoteAccessVpnDao.persist(vpnVO);
+ txn.commit();
+ return vpnVO;
+ } finally {
+ if (locked) {
+ _ipAddressDao.release(publicIp);
+ }
+ }
+ }
+
+ @Override
+ @DB
+ public RemoteAccessVpnVO startRemoteAccessVpn(CreateRemoteAccessVpnCmd cmd) throws ConcurrentOperationException {
+ Long userId = UserContext.current().getUserId();
+ Account account = getAccountForApiCommand(cmd.getAccountName(), cmd.getDomainId());
+ EventUtils.saveStartedEvent(userId, account.getId(), EventTypes.EVENT_REMOTE_ACCESS_VPN_CREATE, "Creating a Remote Access VPN for account: " + account.getAccountName() + " in zone " + cmd.getZoneId(), cmd.getStartEventId());
+ RemoteAccessVpnVO vpnVO = _remoteAccessVpnDao.findById(cmd.getId());
+ String publicIp = vpnVO.getVpnServerAddress();
+ Long vpnId = vpnVO.getId();
+ Transaction txn = Transaction.currentTxn();
+ txn.start();
+ boolean locked = false;
+ boolean created = false;
+ try {
+ IPAddressVO ipAddr = _ipAddressDao.acquire(publicIp);
+ if (ipAddr == null) {
+ throw new ConcurrentOperationException("Another operation active, unable to create vpn");
+ }
+ locked = true;
+
+ vpnVO = _routerMgr.startRemoteAccessVpn(vpnVO);
+ created = (vpnVO != null);
+
+ return vpnVO;
+ } finally {
+ if (created) {
+ EventUtils.saveEvent(userId, account.getId(), EventTypes.EVENT_REMOTE_ACCESS_VPN_CREATE, "Created a Remote Access VPN for account: " + account.getAccountName() + " in zone " + cmd.getZoneId());
+ } else {
+ EventUtils.saveEvent(userId, account.getId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_REMOTE_ACCESS_VPN_CREATE, "Unable to create Remote Access VPN ", account.getAccountName() + " in zone " + cmd.getZoneId());
+ _remoteAccessVpnDao.remove(vpnId);
+ }
+ txn.commit();
+ if (locked) {
+ _ipAddressDao.release(publicIp);
+ }
+ }
+ }
+
+ @Override
+ @DB
+ public boolean destroyRemoteAccessVpn(DeleteRemoteAccessVpnCmd cmd) throws ConcurrentOperationException {
+ Long userId = UserContext.current().getUserId();
+ Account account = getAccountForApiCommand(cmd.getAccountName(), cmd.getDomainId());
+ //TODO: assumes one virtual network / domr per account per zone
+ RemoteAccessVpnVO vpnVO = _remoteAccessVpnDao.findByAccountAndZone(account.getId(), cmd.getZoneId());
+ if (vpnVO == null) {
+ throw new InvalidParameterValueException("No VPN found for account " + account.getAccountName() + " in zone " + cmd.getZoneId());
+ }
+ EventUtils.saveStartedEvent(userId, account.getId(), EventTypes.EVENT_REMOTE_ACCESS_VPN_DESTROY, "Deleting Remote Access VPN for account: " + account.getAccountName() + " in zone " + cmd.getZoneId(), cmd.getStartEventId());
+ String publicIp = vpnVO.getVpnServerAddress();
+ Long vpnId = vpnVO.getId();
+ Transaction txn = Transaction.currentTxn();
+ txn.start();
+ boolean locked = false;
+ boolean deleted = false;
+ try {
+ IPAddressVO ipAddr = _ipAddressDao.acquire(publicIp);
+ if (ipAddr == null) {
+ throw new ConcurrentOperationException("Another operation active, unable to create vpn");
+ }
+ locked = true;
+
+ deleted = _routerMgr.deleteRemoteAccessVpn(vpnVO);
+ return deleted;
+ } finally {
+ if (deleted) {
+ _remoteAccessVpnDao.remove(vpnId);
+ EventUtils.saveEvent(userId, account.getId(), EventTypes.EVENT_REMOTE_ACCESS_VPN_DESTROY, "Deleted Remote Access VPN for account: " + account.getAccountName() + " in zone " + cmd.getZoneId());
+ } else {
+ EventUtils.saveEvent(userId, account.getId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_REMOTE_ACCESS_VPN_DESTROY, "Unable to delete Remote Access VPN ", account.getAccountName() + " in zone " + cmd.getZoneId());
+ }
+ txn.commit();
+ if (locked) {
+ _ipAddressDao.release(publicIp);
+ }
+ }
+ }
}
diff --git a/server/src/com/cloud/network/router/DomainRouterManager.java b/server/src/com/cloud/network/router/DomainRouterManager.java
index 299122356fd..5581d9741f0 100644
--- a/server/src/com/cloud/network/router/DomainRouterManager.java
+++ b/server/src/com/cloud/network/router/DomainRouterManager.java
@@ -34,6 +34,7 @@ import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.NetworkConfiguration;
+import com.cloud.network.RemoteAccessVpnVO;
import com.cloud.offering.NetworkOffering;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.user.Account;
@@ -169,4 +170,8 @@ public interface DomainRouterManager extends Manager {
DomainRouterVO getRouter(String publicIpAddress);
DomainRouterVO deploy(NetworkConfiguration guestConfig, NetworkOffering offering, DeployDestination dest, Account owner) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException;
+
+ RemoteAccessVpnVO startRemoteAccessVpn(RemoteAccessVpnVO vpnVO);
+
+ boolean deleteRemoteAccessVpn(RemoteAccessVpnVO vpnVO);
}
diff --git a/server/src/com/cloud/network/router/DomainRouterManagerImpl.java b/server/src/com/cloud/network/router/DomainRouterManagerImpl.java
index 02465beac3c..91b02fbdaa9 100644
--- a/server/src/com/cloud/network/router/DomainRouterManagerImpl.java
+++ b/server/src/com/cloud/network/router/DomainRouterManagerImpl.java
@@ -52,6 +52,7 @@ import com.cloud.agent.api.StartRouterCommand;
import com.cloud.agent.api.StopCommand;
import com.cloud.agent.api.routing.DhcpEntryCommand;
import com.cloud.agent.api.routing.SavePasswordCommand;
+import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand;
import com.cloud.agent.api.routing.VmDataCommand;
import com.cloud.agent.api.to.NicTO;
import com.cloud.agent.api.to.VirtualMachineTO;
@@ -109,6 +110,7 @@ import com.cloud.network.Network.TrafficType;
import com.cloud.network.NetworkConfiguration;
import com.cloud.network.NetworkConfigurationVO;
import com.cloud.network.NetworkManager;
+import com.cloud.network.RemoteAccessVpnVO;
import com.cloud.network.SshKeysDistriMonitor;
import com.cloud.network.dao.FirewallRulesDao;
import com.cloud.network.dao.IPAddressDao;
@@ -2105,4 +2107,60 @@ public class DomainRouterManagerImpl implements DomainRouterManager, VirtualMach
return true;
}
+ @Override
+ public RemoteAccessVpnVO startRemoteAccessVpn(RemoteAccessVpnVO vpnVO) {
+ DomainRouterVO router = getRouter(vpnVO.getAccountId(), vpnVO.getZoneId());
+ if (router == null) {
+ s_logger.warn("Failed to start remote access VPN: no router found for account and zone");
+ return null;
+ }
+ if (router.getState() != State.Running) {
+ s_logger.warn("Failed to start remote access VPN: router not in running state");
+ return null;
+ }
+ try {
+ Answer answer = _agentMgr.send(router.getHostId(), new RemoteAccessVpnCfgCommand(true, router.getPrivateIpAddress(), vpnVO.getVpnServerAddress(), vpnVO.getLocalIp(), vpnVO.getIpRange(), vpnVO.getIpsecPresharedKey()));
+ if (answer != null && answer.getResult()) {
+ return vpnVO;
+ } else {
+ s_logger.debug("Failed to start remote access VPN: " + answer.getDetails());
+ return null;
+ }
+ } catch (AgentUnavailableException e) {
+ s_logger.debug("Failed to start remote access VPN: ", e);
+ return null;
+ } catch (OperationTimedoutException e) {
+ s_logger.debug("Failed to start remote access VPN: ", e);
+ return null;
+ }
+ }
+
+ @Override
+ public boolean deleteRemoteAccessVpn(RemoteAccessVpnVO vpnVO) {
+ DomainRouterVO router = getRouter(vpnVO.getAccountId(), vpnVO.getZoneId());
+ if (router == null) {
+ s_logger.warn("Failed to delete remote access VPN: no router found for account and zone");
+ return false;
+ }
+ if (router.getState() != State.Running) {
+ s_logger.warn("Failed to delete remote access VPN: router not in running state");
+ return false;
+ }
+ try {
+ Answer answer = _agentMgr.send(router.getHostId(), new RemoteAccessVpnCfgCommand(false, router.getPrivateIpAddress(), vpnVO.getVpnServerAddress(), vpnVO.getLocalIp(), vpnVO.getIpRange(), vpnVO.getIpsecPresharedKey()));
+ if (answer != null && answer.getResult()) {
+ return true;
+ } else {
+ s_logger.debug("Failed to delete remote access VPN: " + answer.getDetails());
+ return false;
+ }
+ } catch (AgentUnavailableException e) {
+ s_logger.debug("Failed to delete remote access VPN: ", e);
+ return false;
+ } catch (OperationTimedoutException e) {
+ s_logger.debug("Failed to delete remote access VPN: ", e);
+ return false;
+ }
+ }
+
}
diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java
index 072dca0ef09..26ad4d94628 100644
--- a/server/src/com/cloud/server/ConfigurationServerImpl.java
+++ b/server/src/com/cloud/server/ConfigurationServerImpl.java
@@ -499,7 +499,7 @@ public class ConfigurationServerImpl implements ConfigurationServer {
if (already == null) {
s_logger.info("Need to store secondary storage vm copy password in the database");
- String password = PasswordGenerator.generateRandomPassword();
+ String password = PasswordGenerator.generateRandomPassword(12);
String insertSql1 = "INSERT INTO `cloud`.`configuration` (category, instance, component, name, value, description) " +
"VALUES ('Hidden','DEFAULT', 'management-server','secstorage.copy.password', '" + password + "','Password used to authenticate zone-to-zone template copy requests')";
diff --git a/server/src/com/cloud/server/ManagementServer.java b/server/src/com/cloud/server/ManagementServer.java
index 63b2bfbcbd1..a4ab586dadb 100755
--- a/server/src/com/cloud/server/ManagementServer.java
+++ b/server/src/com/cloud/server/ManagementServer.java
@@ -58,6 +58,7 @@ import com.cloud.api.commands.ListLoadBalancerRulesCmd;
import com.cloud.api.commands.ListPodsByCmd;
import com.cloud.api.commands.ListPreallocatedLunsCmd;
import com.cloud.api.commands.ListPublicIpAddressesCmd;
+import com.cloud.api.commands.ListRemoteAccessVpnsCmd;
import com.cloud.api.commands.ListRoutersCmd;
import com.cloud.api.commands.ListServiceOfferingsCmd;
import com.cloud.api.commands.ListSnapshotsCmd;
@@ -113,6 +114,9 @@ import com.cloud.info.ConsoleProxyInfo;
import com.cloud.network.FirewallRuleVO;
import com.cloud.network.IPAddressVO;
import com.cloud.network.LoadBalancerVO;
+import com.cloud.network.NetworkRuleConfigVO;
+import com.cloud.network.RemoteAccessVpnVO;
+import com.cloud.network.SecurityGroupVO;
import com.cloud.network.security.NetworkGroupVO;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.storage.DiskOfferingVO;
@@ -1125,4 +1129,6 @@ public interface ManagementServer {
* @throws ServerApiException -- even if one of the console proxy patching fails, we throw back this exception
*/
String uploadCertificate(UploadCustomCertificateCmd cmd) throws ServerApiException;
+
+ public List searchForRemoteAccessVpns(ListRemoteAccessVpnsCmd cmd) throws InvalidParameterValueException, PermissionDeniedException;
}
diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java
index c80846e5c6f..ad91d66e8ee 100755
--- a/server/src/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/com/cloud/server/ManagementServerImpl.java
@@ -104,6 +104,7 @@ import com.cloud.api.commands.ListLoadBalancerRulesCmd;
import com.cloud.api.commands.ListPodsByCmd;
import com.cloud.api.commands.ListPreallocatedLunsCmd;
import com.cloud.api.commands.ListPublicIpAddressesCmd;
+import com.cloud.api.commands.ListRemoteAccessVpnsCmd;
import com.cloud.api.commands.ListRoutersCmd;
import com.cloud.api.commands.ListServiceOfferingsCmd;
import com.cloud.api.commands.ListSnapshotsCmd;
@@ -202,10 +203,18 @@ import com.cloud.network.IPAddressVO;
import com.cloud.network.LoadBalancerVMMapVO;
import com.cloud.network.LoadBalancerVO;
import com.cloud.network.NetworkManager;
+import com.cloud.network.NetworkRuleConfigVO;
+import com.cloud.network.RemoteAccessVpnVO;
+import com.cloud.network.SecurityGroupVMMapVO;
+import com.cloud.network.SecurityGroupVO;
import com.cloud.network.dao.FirewallRulesDao;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.LoadBalancerDao;
import com.cloud.network.dao.LoadBalancerVMMapDao;
+import com.cloud.network.dao.NetworkRuleConfigDao;
+import com.cloud.network.dao.RemoteAccessVpnDao;
+import com.cloud.network.dao.SecurityGroupDao;
+import com.cloud.network.dao.SecurityGroupVMMapDao;
import com.cloud.network.security.NetworkGroupManager;
import com.cloud.network.security.NetworkGroupVO;
import com.cloud.network.security.dao.NetworkGroupDao;
@@ -375,6 +384,7 @@ public class ManagementServerImpl implements ManagementServer {
private final UploadMonitor _uploadMonitor;
private final UploadDao _uploadDao;
private final CertificateDao _certDao;
+ private final RemoteAccessVpnDao _remoteAccessVpnDao;
private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("AccountChecker"));
private final ScheduledExecutorService _eventExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("EventChecker"));
@@ -449,6 +459,7 @@ public class ManagementServerImpl implements ManagementServer {
_groupVMMapDao = locator.getDao(InstanceGroupVMMapDao.class);
_uploadDao = locator.getDao(UploadDao.class);
_certDao = locator.getDao(CertificateDao.class);
+ _remoteAccessVpnDao = locator.getDao(RemoteAccessVpnDao.class);
_configs = _configDao.getConfiguration();
_userStatsDao = locator.getDao(UserStatisticsDao.class);
_vmInstanceDao = locator.getDao(VMInstanceDao.class);
@@ -1432,7 +1443,7 @@ public class ManagementServerImpl implements ManagementServer {
@Override
public String generateRandomPassword() {
- return PasswordGenerator.generateRandomPassword();
+ return PasswordGenerator.generateRandomPassword(6);
}
@Override
@@ -6048,4 +6059,96 @@ public class ManagementServerImpl implements ManagementServer {
return accountId;
}
+
+ @Override
+ public List searchForRemoteAccessVpns(ListRemoteAccessVpnsCmd cmd) throws InvalidParameterValueException,
+ PermissionDeniedException {
+ // do some parameter validation
+ Account account = UserContext.current().getAccount();
+ String accountName = cmd.getAccountName();
+ Long domainId = cmd.getDomainId();
+ Long accountId = null;
+ Account ipAddressOwner = null;
+ String ipAddress = cmd.getPublicIp();
+
+ if (ipAddress != null) {
+ IPAddressVO ipAddressVO = _publicIpAddressDao.findById(ipAddress);
+ if (ipAddressVO == null) {
+ throw new InvalidParameterValueException("Unable to list remote access vpns, IP address " + ipAddress + " not found.");
+ } else {
+ Long ipAddrAcctId = ipAddressVO.getAccountId();
+ if (ipAddrAcctId == null) {
+ throw new InvalidParameterValueException("Unable to list remote access vpns, IP address " + ipAddress + " is not associated with an account.");
+ }
+ ipAddressOwner = _accountDao.findById(ipAddrAcctId);
+ }
+ }
+
+ if ((account == null) || isAdmin(account.getType())) {
+ // validate domainId before proceeding
+ if (domainId != null) {
+ if ((account != null) && !_domainDao.isChildDomain(account.getDomainId(), domainId)) {
+ throw new PermissionDeniedException("Unable to list remote access vpns for domain id " + domainId + ", permission denied.");
+ }
+ if (accountName != null) {
+ Account userAccount = _accountDao.findActiveAccount(accountName, domainId);
+ if (userAccount != null) {
+ accountId = userAccount.getId();
+ } else {
+ throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
+ }
+ }
+ } else if (ipAddressOwner != null) {
+ if ((account != null) && !_domainDao.isChildDomain(account.getDomainId(), ipAddressOwner.getDomainId())) {
+ throw new PermissionDeniedException("Unable to list remote access vpn for IP address " + ipAddress + ", permission denied.");
+ }
+ } else {
+ domainId = ((account == null) ? DomainVO.ROOT_DOMAIN : account.getDomainId());
+ }
+ } else {
+ accountId = account.getId();
+ }
+
+ Filter searchFilter = new Filter(RemoteAccessVpnVO.class, "vpnServerAddress", true, cmd.getStartIndex(), cmd.getPageSizeVal());
+
+ Object id = cmd.getId();
+ Object zoneId = cmd.getZoneId();
+
+
+ SearchBuilder sb = _remoteAccessVpnDao.createSearchBuilder();
+ sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
+ sb.and("zoneId", sb.entity().getZoneId(), SearchCriteria.Op.EQ);
+ sb.and("accountId", sb.entity().getAccountId(), SearchCriteria.Op.EQ);
+ sb.and("ipAddress", sb.entity().getVpnServerAddress(), SearchCriteria.Op.EQ);
+
+ if ((accountId == null) && (domainId != null)) {
+ // if accountId isn't specified, we can do a domain match for the admin case
+ SearchBuilder domainSearch = _domainDao.createSearchBuilder();
+ domainSearch.and("path", domainSearch.entity().getPath(), SearchCriteria.Op.LIKE);
+ sb.join("domainSearch", domainSearch, sb.entity().getDomainId(), domainSearch.entity().getId(), JoinBuilder.JoinType.INNER);
+ }
+
+ SearchCriteria sc = sb.create();
+
+ if (id != null) {
+ sc.setParameters("id", id);
+ }
+
+ if (ipAddress != null) {
+ sc.setParameters("ipAddress", ipAddress);
+ }
+
+ if (zoneId != null) {
+ sc.setParameters("zoneId", zoneId);
+ }
+
+ if (accountId != null) {
+ sc.setParameters("accountId", accountId);
+ } else if (domainId != null) {
+ DomainVO domain = _domainDao.findById(domainId);
+ sc.setJoinParameters("domainSearch", "path", domain.getPath() + "%");
+ }
+
+ return _remoteAccessVpnDao.search(sc, searchFilter);
+ }
}
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java
index 5e3dc79de85..9f55814cdd8 100755
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -322,7 +322,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, VirtualM
VMTemplateVO template = _templateDao.findById(vmInstance.getTemplateId());
if (template.getEnablePassword()) {
- password = PasswordGenerator.generateRandomPassword();;
+ password = PasswordGenerator.generateRandomPassword(6);;
} else {
password = "saved_password";
}
@@ -3824,7 +3824,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, VirtualM
}
// Check that the password was passed in and is valid
- String password = PasswordGenerator.generateRandomPassword();
+ String password = PasswordGenerator.generateRandomPassword(6);
if (!template.getEnablePassword()) {
password = "saved_password";
}
diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql
index 14def07e731..bde6a7e097d 100755
--- a/setup/db/create-schema.sql
+++ b/setup/db/create-schema.sql
@@ -952,6 +952,17 @@ CREATE TABLE `cloud`.`load_balancer` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+CREATE TABLE `cloud`.`remote_access_vpn` (
+ `id` bigint unsigned NOT NULL auto_increment,
+ `account_id` bigint unsigned NOT NULL,
+ `zone_id` bigint unsigned NOT NULL,
+ `vpn_server_addr` varchar(15) NOT NULL,
+ `local_ip` varchar(15) NOT NULL,
+ `ip_range` varchar(32) NOT NULL,
+ `ipsec_psk` varchar(255) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
CREATE TABLE `cloud`.`storage_pool` (
`id` bigint unsigned UNIQUE NOT NULL,
`name` varchar(255) COMMENT 'should be NOT NULL',
diff --git a/utils/src/com/cloud/utils/PasswordGenerator.java b/utils/src/com/cloud/utils/PasswordGenerator.java
index 37b3d258706..567a8a24158 100644
--- a/utils/src/com/cloud/utils/PasswordGenerator.java
+++ b/utils/src/com/cloud/utils/PasswordGenerator.java
@@ -25,7 +25,7 @@ import java.util.Random;
*
*/
public class PasswordGenerator {
- public static String generateRandomPassword() {
+ public static String generateRandomPassword(int num) {
Random r = new Random();
StringBuffer password = new StringBuffer();
@@ -46,7 +46,7 @@ public class PasswordGenerator {
// Generate a random 6-character string with only lowercase
// characters
- for (int i = 0; i < 6; i++) {
+ for (int i = 0; i < num; i++) {
// Generate a random lowercase character (don't allow lowercase
// "l" or lowercase "o")
lowercase = generateLowercaseChar(r);