From a10ce22f99b9e4321fd97ba3bb54f65e39e1eb49 Mon Sep 17 00:00:00 2001 From: Alex Huang Date: Wed, 29 Dec 2010 11:36:30 -0800 Subject: [PATCH] remote access vpn from management server side is done --- .../commands/CreateRemoteAccessVpnCmd.java | 4 - .../commands/DeleteRemoteAccessVpnCmd.java | 5 +- .../cloud/api/commands/RemoveVpnUserCmd.java | 11 +- .../com/cloud/network/RemoteAccessVpn.java | 7 + .../network/vpn/RemoteAccessVpnElement.java | 7 +- .../network/vpn/RemoteAccessVpnService.java | 6 +- .../com/cloud/network/RemoteAccessVpnVO.java | 17 +- .../cloud/network/dao/FirewallRulesDao.java | 3 + .../network/dao/FirewallRulesDaoImpl.java | 21 +- .../com/cloud/network/rules/RulesManager.java | 1 + .../cloud/network/rules/RulesManagerImpl.java | 5 + .../vpn/RemoteAccessVpnManagerImpl.java | 338 +++++++++--------- setup/db/create-schema.sql | 1 + 13 files changed, 245 insertions(+), 181 deletions(-) diff --git a/api/src/com/cloud/api/commands/CreateRemoteAccessVpnCmd.java b/api/src/com/cloud/api/commands/CreateRemoteAccessVpnCmd.java index c645909d68b..266a2e4eea6 100644 --- a/api/src/com/cloud/api/commands/CreateRemoteAccessVpnCmd.java +++ b/api/src/com/cloud/api/commands/CreateRemoteAccessVpnCmd.java @@ -28,7 +28,6 @@ import com.cloud.api.ServerApiException; import com.cloud.api.response.RemoteAccessVpnResponse; import com.cloud.domain.Domain; import com.cloud.event.EventTypes; -import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.NetworkRuleConflictException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.RemoteAccessVpn; @@ -162,9 +161,6 @@ public class CreateRemoteAccessVpnCmd extends BaseAsyncCreateCmd { } catch (ResourceUnavailableException ex) { s_logger.warn("Exception: ", ex); throw new ServerApiException(BaseCmd.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); - } catch (ConcurrentOperationException ex) { - s_logger.warn("Exception: ", ex); - throw new ServerApiException(BaseCmd.INTERNAL_ERROR, ex.getMessage()); } } } diff --git a/api/src/com/cloud/api/commands/DeleteRemoteAccessVpnCmd.java b/api/src/com/cloud/api/commands/DeleteRemoteAccessVpnCmd.java index ff3c5729272..917bf35f90b 100644 --- a/api/src/com/cloud/api/commands/DeleteRemoteAccessVpnCmd.java +++ b/api/src/com/cloud/api/commands/DeleteRemoteAccessVpnCmd.java @@ -25,6 +25,7 @@ import com.cloud.api.Implementation; import com.cloud.api.Parameter; import com.cloud.api.response.SuccessResponse; import com.cloud.event.EventTypes; +import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.RemoteAccessVpn; import com.cloud.utils.net.Ip; @@ -70,8 +71,8 @@ public class DeleteRemoteAccessVpnCmd extends BaseAsyncCmd { } @Override - public void execute(){ - _ravService.destroyRemoteAccessVpn(new Ip(publicIp)); + public void execute() throws ResourceUnavailableException { + _ravService.destroyRemoteAccessVpn(new Ip(publicIp), getStartEventId()); } } diff --git a/api/src/com/cloud/api/commands/RemoveVpnUserCmd.java b/api/src/com/cloud/api/commands/RemoveVpnUserCmd.java index e0888fe3643..972b767014e 100644 --- a/api/src/com/cloud/api/commands/RemoveVpnUserCmd.java +++ b/api/src/com/cloud/api/commands/RemoveVpnUserCmd.java @@ -113,11 +113,14 @@ public class RemoveVpnUserCmd extends BaseAsyncCmd { public void execute(){ Account owner = getValidOwner(accountName, domainId); boolean result = _ravService.removeVpnUser(owner.getId(), userName); - if (result) { - SuccessResponse response = new SuccessResponse(getCommandName()); - this.setResponseObject(response); - } else { + if (!result) { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to remove vpn user"); } + + if (!_ravService.applyVpnUsers(owner.getId())) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to apply vpn user removal"); + } + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); } } diff --git a/api/src/com/cloud/network/RemoteAccessVpn.java b/api/src/com/cloud/network/RemoteAccessVpn.java index c4a610830c1..cc0b0b12fef 100644 --- a/api/src/com/cloud/network/RemoteAccessVpn.java +++ b/api/src/com/cloud/network/RemoteAccessVpn.java @@ -21,9 +21,16 @@ import com.cloud.acl.ControlledEntity; import com.cloud.utils.net.Ip; public interface RemoteAccessVpn extends ControlledEntity { + enum State { + Added, + Running, + Removed + } + Ip getServerAddress(); String getIpRange(); String getIpsecPresharedKey(); String getLocalIp(); long getNetworkId(); + State getState(); } diff --git a/api/src/com/cloud/network/vpn/RemoteAccessVpnElement.java b/api/src/com/cloud/network/vpn/RemoteAccessVpnElement.java index 4cdc9c159d5..3ec802fb4a6 100644 --- a/api/src/com/cloud/network/vpn/RemoteAccessVpnElement.java +++ b/api/src/com/cloud/network/vpn/RemoteAccessVpnElement.java @@ -19,11 +19,16 @@ package com.cloud.network.vpn; import java.util.List; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; import com.cloud.network.RemoteAccessVpn; import com.cloud.network.VpnUser; import com.cloud.utils.component.Adapter; public interface RemoteAccessVpnElement extends Adapter { - String[] applyVpnUsers(RemoteAccessVpn vpn, List users); + String[] applyVpnUsers(RemoteAccessVpn vpn, List users) throws ResourceUnavailableException; + boolean start(Network network, RemoteAccessVpn vpn) throws ResourceUnavailableException; + + boolean stop(Network network, RemoteAccessVpn vpn) throws ResourceUnavailableException; } diff --git a/api/src/com/cloud/network/vpn/RemoteAccessVpnService.java b/api/src/com/cloud/network/vpn/RemoteAccessVpnService.java index c2c497f5982..2f4b17d2eb6 100644 --- a/api/src/com/cloud/network/vpn/RemoteAccessVpnService.java +++ b/api/src/com/cloud/network/vpn/RemoteAccessVpnService.java @@ -22,7 +22,6 @@ import java.util.List; import com.cloud.api.commands.ListRemoteAccessVpnsCmd; import com.cloud.api.commands.ListVpnUsersCmd; -import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.NetworkRuleConflictException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.RemoteAccessVpn; @@ -32,9 +31,8 @@ import com.cloud.utils.net.Ip; public interface RemoteAccessVpnService { RemoteAccessVpn createRemoteAccessVpn(Ip vpnServerAddress, String ipRange) throws NetworkRuleConflictException; - void destroyRemoteAccessVpn(Ip vpnServerAddress); - List listRemoteAccessVpns(long vpnOwnerId, Ip publicIp); - RemoteAccessVpn startRemoteAccessVpn(Ip vpnServerAddress) throws ConcurrentOperationException, ResourceUnavailableException; + void destroyRemoteAccessVpn(Ip vpnServerAddress, long startEventId) throws ResourceUnavailableException; + RemoteAccessVpn startRemoteAccessVpn(Ip vpnServerAddress) throws ResourceUnavailableException; VpnUser addVpnUser(long vpnOwnerId, String userName, String password); boolean removeVpnUser(long vpnOwnerId, String userName); diff --git a/server/src/com/cloud/network/RemoteAccessVpnVO.java b/server/src/com/cloud/network/RemoteAccessVpnVO.java index 8a39b213ff7..8d5634c87d8 100644 --- a/server/src/com/cloud/network/RemoteAccessVpnVO.java +++ b/server/src/com/cloud/network/RemoteAccessVpnVO.java @@ -51,7 +51,10 @@ public class RemoteAccessVpnVO implements RemoteAccessVpn { private String ipRange; @Column(name="ipsec_psk") - private String ipsecPresharedKey; + private String ipsecPresharedKey; + + @Column(name="state") + private State state; public RemoteAccessVpnVO() { } @@ -62,7 +65,17 @@ public class RemoteAccessVpnVO implements RemoteAccessVpn { this.ipsecPresharedKey = presharedKey; this.localIp = localIp; this.domainId = domainId; - this.networkId = networkId; + this.networkId = networkId; + this.state = State.Added; + } + + @Override + public State getState() { + return state; + } + + public void setState(State state) { + this.state = state; } @Override diff --git a/server/src/com/cloud/network/dao/FirewallRulesDao.java b/server/src/com/cloud/network/dao/FirewallRulesDao.java index c7941f7c45e..34fe34536f4 100644 --- a/server/src/com/cloud/network/dao/FirewallRulesDao.java +++ b/server/src/com/cloud/network/dao/FirewallRulesDao.java @@ -20,6 +20,7 @@ package com.cloud.network.dao; import java.util.List; +import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRuleVO; import com.cloud.utils.db.GenericDao; import com.cloud.utils.net.Ip; @@ -33,6 +34,8 @@ public interface FirewallRulesDao extends GenericDao { boolean setStateToAdd(FirewallRuleVO rule); boolean revoke(FirewallRuleVO rule); + + boolean releasePorts(Ip ip, String protocol, FirewallRule.Purpose purpose, int[] ports); // public List listIPForwarding(String publicIPAddress, boolean forwarding); // public List listIPForwarding(String publicIPAddress, String port, boolean forwarding); diff --git a/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java b/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java index 57f6cd4688f..c3a4e2dcbf8 100644 --- a/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java +++ b/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java @@ -24,6 +24,7 @@ import javax.ejb.Local; import org.apache.log4j.Logger; +import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule.State; import com.cloud.network.rules.FirewallRuleVO; import com.cloud.utils.db.DB; @@ -39,6 +40,7 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i protected final SearchBuilder AllFieldsSearch; protected final SearchBuilder IpNotRevokedSearch; + protected final SearchBuilder ReleaseSearch; protected FirewallRulesDaoImpl() { super(); @@ -54,12 +56,29 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i AllFieldsSearch.and("networkId", AllFieldsSearch.entity().getNetworkId(), Op.EQ); AllFieldsSearch.done(); - IpNotRevokedSearch = createSearchBuilder(); IpNotRevokedSearch.and("ip", IpNotRevokedSearch.entity().getSourceIpAddress(), Op.EQ); IpNotRevokedSearch.and("state", IpNotRevokedSearch.entity().getState(), Op.NEQ); IpNotRevokedSearch.done(); + ReleaseSearch = createSearchBuilder(); + ReleaseSearch.and("protocol", ReleaseSearch.entity().getProtocol(), Op.EQ); + ReleaseSearch.and("ip", ReleaseSearch.entity().getSourceIpAddress(), Op.EQ); + ReleaseSearch.and("purpose", ReleaseSearch.entity().getPurpose(), Op.EQ); + ReleaseSearch.and("ports", ReleaseSearch.entity().getSourcePortStart(), Op.IN); + ReleaseSearch.done(); + + } + @Override + public boolean releasePorts(Ip ip, String protocol, FirewallRule.Purpose purpose, int[] ports) { + SearchCriteria sc = ReleaseSearch.create(); + sc.setParameters("protocol", protocol); + sc.setParameters("ip", ip); + sc.setParameters("purpose", purpose); + sc.setParameters("ports", ports); + + int results = remove(sc); + return results == ports.length; } @Override diff --git a/server/src/com/cloud/network/rules/RulesManager.java b/server/src/com/cloud/network/rules/RulesManager.java index 32cb1230eef..136464e509f 100644 --- a/server/src/com/cloud/network/rules/RulesManager.java +++ b/server/src/com/cloud/network/rules/RulesManager.java @@ -74,4 +74,5 @@ public interface RulesManager extends RulesService { boolean revokePortForwardingRule(long vmId); FirewallRule[] reservePorts(IpAddress ip, String protocol, FirewallRule.Purpose purpose, int... ports) throws NetworkRuleConflictException; + boolean releasePorts(Ip ip, String protocol, FirewallRule.Purpose purpose, int... ports); } diff --git a/server/src/com/cloud/network/rules/RulesManagerImpl.java b/server/src/com/cloud/network/rules/RulesManagerImpl.java index 60bcda4080e..57404f6ef5a 100644 --- a/server/src/com/cloud/network/rules/RulesManagerImpl.java +++ b/server/src/com/cloud/network/rules/RulesManagerImpl.java @@ -456,6 +456,11 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { return null; } + @Override + public boolean releasePorts(Ip ip, String protocol, FirewallRule.Purpose purpose, int... ports) { + return _firewallDao.releasePorts(ip, protocol, purpose, ports); + } + @Override @DB public FirewallRuleVO[] reservePorts(IpAddress ip, String protocol, FirewallRule.Purpose purpose, int... ports) throws NetworkRuleConflictException { FirewallRuleVO[] rules = new FirewallRuleVO[ports.length]; diff --git a/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java b/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java index 1526aacc4b0..55aeaecf226 100644 --- a/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java +++ b/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java @@ -26,7 +26,6 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; -import com.cloud.api.commands.DeleteRemoteAccessVpnCmd; import com.cloud.api.commands.ListRemoteAccessVpnsCmd; import com.cloud.api.commands.ListVpnUsersCmd; import com.cloud.configuration.Config; @@ -35,8 +34,8 @@ import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; import com.cloud.event.EventTypes; import com.cloud.event.EventUtils; +import com.cloud.event.EventVO; import com.cloud.exception.AccountLimitException; -import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.NetworkRuleConflictException; import com.cloud.exception.ResourceUnavailableException; @@ -73,21 +72,30 @@ import com.cloud.utils.db.Transaction; import com.cloud.utils.net.Ip; import com.cloud.utils.net.NetUtils; -@Local(value=RemoteAccessVpnService.class) +@Local(value = RemoteAccessVpnService.class) public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manager { private final static Logger s_logger = Logger.getLogger(RemoteAccessVpnManagerImpl.class); String _name; - - @Inject AccountDao _accountDao; - @Inject VpnUserDao _vpnUsersDao; - @Inject RemoteAccessVpnDao _remoteAccessVpnDao; - @Inject IPAddressDao _ipAddressDao; - @Inject VirtualNetworkApplianceManager _routerMgr; - @Inject AccountManager _accountMgr; - @Inject NetworkManager _networkMgr; - @Inject RulesManager _rulesMgr; - @Inject DomainDao _domainDao; - + + @Inject + AccountDao _accountDao; + @Inject + VpnUserDao _vpnUsersDao; + @Inject + RemoteAccessVpnDao _remoteAccessVpnDao; + @Inject + IPAddressDao _ipAddressDao; + @Inject + VirtualNetworkApplianceManager _routerMgr; + @Inject + AccountManager _accountMgr; + @Inject + NetworkManager _networkMgr; + @Inject + RulesManager _rulesMgr; + @Inject + DomainDao _domainDao; + int _userLimit; int _pskLength; String _clientIpRange; @@ -97,19 +105,19 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag public RemoteAccessVpn createRemoteAccessVpn(Ip publicIp, String ipRange) throws NetworkRuleConflictException { UserContext ctx = UserContext.current(); Account caller = ctx.getCaller(); - + // make sure ip address exists PublicIpAddress ipAddr = _networkMgr.getPublicIpAddress(publicIp); if (ipAddr == null) { throw new InvalidParameterValueException("Unable to create remote access vpn, invalid public IP address " + publicIp); } - + _accountMgr.checkAccess(caller, ipAddr); - + if (!ipAddr.readyToUse() || ipAddr.getAssociatedWithNetworkId() == null) { throw new InvalidParameterValueException("The Ip address is not ready to be used yet: " + ipAddr.getAddress()); } - + RemoteAccessVpnVO vpnVO = _remoteAccessVpnDao.findByPublicIpAddress(publicIp.toString()); if (vpnVO != null) { throw new InvalidParameterValueException("A Remote Access VPN already exists for this public Ip address"); @@ -120,7 +128,7 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag if (vpnVO != null) { throw new InvalidParameterValueException("A Remote Access VPN already exists for this account"); } - + if (ipRange == null) { ipRange = _clientIpRange; } @@ -134,12 +142,12 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag if (!NetUtils.validIpRange(range[0], range[1])) { throw new InvalidParameterValueException("Invalid ip range " + ipRange); } - + Network network = _networkMgr.getNetwork(ipAddr.getAssociatedWithNetworkId()); Pair cidr = NetUtils.getCidr(network.getCidr()); - - - //FIXME: This check won't work for the case where the guest ip range changes depending on the vlan allocated. + + // FIXME: This check won't work for the case where the guest ip range + // changes depending on the vlan allocated. String[] guestIpRange = NetUtils.getIpRangeFromCidr(cidr.first(), cidr.second()); if (NetUtils.ipRangesOverlap(range[0], range[1], guestIpRange[0], guestIpRange[1])) { throw new InvalidParameterValueException("Invalid ip range: " + ipRange + " overlaps with guest ip range " + guestIpRange[0] + "-" @@ -152,10 +160,11 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag String newIpRange = NetUtils.long2Ip(++startIp) + "-" + range[1]; String sharedSecret = PasswordGenerator.generatePresharedKey(_pskLength); _rulesMgr.reservePorts(ipAddr, NetUtils.UDP_PROTO, Purpose.Vpn, NetUtils.VPN_PORT, NetUtils.VPN_L2TP_PORT, NetUtils.VPN_NATT_PORT); - vpnVO = new RemoteAccessVpnVO(ipAddr.getAllocatedToAccountId(), ipAddr.getAllocatedInDomainId(), ipAddr.getAssociatedWithNetworkId(), publicIp, range[0], newIpRange, sharedSecret); + vpnVO = new RemoteAccessVpnVO(ipAddr.getAllocatedToAccountId(), ipAddr.getAllocatedInDomainId(), ipAddr.getAssociatedWithNetworkId(), + publicIp, range[0], newIpRange, sharedSecret); return _remoteAccessVpnDao.persist(vpnVO); } - + private void validateRemoteAccessVpnConfiguration() throws ConfigurationException { String ipRange = _clientIpRange; if (ipRange == null) { @@ -170,26 +179,69 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag return; } - String [] range = ipRange.split("-"); + String[] range = ipRange.split("-"); if (range.length != 2) { throw new ConfigurationException("Remote Access VPN: Invalid ip range " + ipRange); } - if (!NetUtils.isValidIp(range[0]) || !NetUtils.isValidIp(range[1])){ + if (!NetUtils.isValidIp(range[0]) || !NetUtils.isValidIp(range[1])) { throw new ConfigurationException("Remote Access VPN: Invalid ip in range specification " + ipRange); } - if (!NetUtils.validIpRange(range[0], range[1])){ + if (!NetUtils.validIpRange(range[0], range[1])) { throw new ConfigurationException("Remote Access VPN: Invalid ip range " + ipRange); } } - @Override - public void destroyRemoteAccessVpn(Ip ip) { - } - - @Override - public List listRemoteAccessVpns(long vpnOwnerId, Ip publicIp) { - // TODO Auto-generated method stub - return null; + @Override @DB + public void destroyRemoteAccessVpn(Ip ip, long startEventId) throws ResourceUnavailableException { + long userId = UserContext.current().getCallerUserId(); + Account caller = UserContext.current().getCaller(); + + RemoteAccessVpnVO vpn = _remoteAccessVpnDao.findById(ip); + if (vpn == null) { + s_logger.debug("vpn does not exists " + ip); + return; + } + + _accountMgr.checkAccess(caller, vpn); + + Account owner = _accountDao.findById(vpn.getAccountId()); + Network network = _networkMgr.getNetwork(vpn.getNetworkId()); + + EventUtils.saveStartedEvent(userId, owner.getId(), EventTypes.EVENT_REMOTE_ACCESS_VPN_DESTROY, "Deleting Remote Access VPN for account: " + + owner.getAccountName() + " in " + ip, startEventId); + + vpn.setState(RemoteAccessVpn.State.Removed); + + + List elements = _networkMgr.getRemoteAccessVpnElements(); + boolean success = false; + try { + for (RemoteAccessVpnElement element : elements) { + if (element.stop(network, vpn)) { + success = true; + break; + } + } + } finally { + if (!success) { + EventUtils.saveEvent(userId, owner.getId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_REMOTE_ACCESS_VPN_DESTROY, + "Unable to delete Remote Access VPN ", owner.getAccountName()); + } else { + + Transaction txn = Transaction.currentTxn(); + txn.start(); + _remoteAccessVpnDao.remove(ip); + if (!_rulesMgr.releasePorts(ip, NetUtils.UDP_PROTO, Purpose.Vpn, NetUtils.VPN_L2TP_PORT, NetUtils.VPN_NATT_PORT, NetUtils.VPN_PORT)) { + s_logger.warn("Unable to release the three vpn ports from the firewall rules"); + txn.rollback(); + + } else { + EventUtils.saveEvent(userId, owner.getId(), EventTypes.EVENT_REMOTE_ACCESS_VPN_DESTROY, "Deleted Remote Access VPN for account: " + + owner.getAccountName()); + } + txn.commit(); + } + } } @Override @@ -199,7 +251,8 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag Account caller = UserContext.current().getCaller(); if (!username.matches("^[a-zA-Z0-9][a-zA-Z0-9@._-]{2,63}$")) { - throw new InvalidParameterValueException("Username has to be begin with an alphabet have 3-64 characters including alphabets, numbers and the set '@.-_'"); + throw new InvalidParameterValueException( + "Username has to be begin with an alphabet have 3-64 characters including alphabets, numbers and the set '@.-_'"); } if (!password.matches("^[a-zA-Z0-9][a-zA-Z0-9@#+=._-]{2,31}$")) { throw new InvalidParameterValueException("Password has to be 3-32 characters including alphabets, numbers and the set '@#+=.-_'"); @@ -211,23 +264,24 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag throw new InvalidParameterValueException("Unable to add vpn user: Another operation active"); } _accountMgr.checkAccess(caller, owner); - + long userCount = _vpnUsersDao.getVpnUserCount(owner.getId()); if (userCount >= _userLimit) { throw new AccountLimitException("Cannot add more than " + _userLimit + " remote access vpn users"); } - + VpnUser user = _vpnUsersDao.persist(new VpnUserVO(vpnOwnerId, username, password)); - EventUtils.saveEvent(callerId, owner.getId(), EventTypes.EVENT_VPN_USER_ADD, "Added a VPN user for account: " + owner.getAccountName() + " username= " + username); + EventUtils.saveEvent(callerId, owner.getId(), EventTypes.EVENT_VPN_USER_ADD, "Added a VPN user for account: " + owner.getAccountName() + + " username= " + username); txn.commit(); return user; - } + } @Override public boolean removeVpnUser(long vpnOwnerId, String username) { long callerId = UserContext.current().getCallerUserId(); Account caller = UserContext.current().getCaller(); - + VpnUserVO user = _vpnUsersDao.findByAccountAndUsername(vpnOwnerId, username); if (user == null) { throw new InvalidParameterValueException("Could not find vpn user " + username); @@ -247,99 +301,50 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag _accountMgr.checkAccess(caller, owner); return _vpnUsersDao.listByAccount(vpnOwnerId); } - + @Override - @DB - public RemoteAccessVpnVO startRemoteAccessVpn(Ip vpnServerAddress) throws ConcurrentOperationException, ResourceUnavailableException { -// long userId = UserContext.current().getCallerUserId(); -// Account caller = UserContext.current().getCaller(); -// -// RemoteAccessVpnVO vpn = _remoteAccessVpnDao.findById(vpnId); -// if (vpn == null) { -// throw new InvalidParameterValueException("Unable to find your vpn: " + vpnId); -// } -// -// _accountMgr.checkAccess(caller, vpn); -// -// -// 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()); -// String publicIp = vpn.getServerAddress(); -// Long vpnId = vpn.getId(); -// Transaction txn = Transaction.currentTxn(); -// txn.start(); -// boolean locked = false; -// boolean created = false; -// try { -// IPAddressVO ipAddr = _ipAddressDao.acquireInLockTable(publicIp); -// if (ipAddr == null) { -// throw new ConcurrentOperationException("Another operation active, unable to create vpn"); -// } -// locked = true; -// -// vpn = _routerMgr.startRemoteAccessVpn(vpn); -// created = (vpn != null); -// -// return vpn; -// } 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.releaseFromLockTable(publicIp); -// } -// } - return null; + public RemoteAccessVpnVO startRemoteAccessVpn(Ip vpnId) throws ResourceUnavailableException { + long userId = UserContext.current().getCallerUserId(); + Account caller = UserContext.current().getCaller(); + + RemoteAccessVpnVO vpn = _remoteAccessVpnDao.findById(vpnId); + if (vpn == null) { + throw new InvalidParameterValueException("Unable to find your vpn: " + vpnId); + } + + _accountMgr.checkAccess(caller, vpn); + + Account owner = _accountDao.findById(vpn.getAccountId()); + Network network = _networkMgr.getNetwork(vpn.getNetworkId()); + + EventUtils.saveStartedEvent(userId, owner.getId(), EventTypes.EVENT_REMOTE_ACCESS_VPN_CREATE, "Creating a Remote Access VPN for account: " + + owner.getAccountName() + " in zone "); + + List elements = _networkMgr.getRemoteAccessVpnElements(); + boolean started = false; + try { + for (RemoteAccessVpnElement element : elements) { + if (element.start(network, vpn)) { + started = true; + break; + } + } + return vpn; + } finally { + if (started) { + EventUtils.saveEvent(userId, owner.getId(), EventTypes.EVENT_REMOTE_ACCESS_VPN_CREATE, "Created a Remote Access VPN for account: " + + owner.getAccountName()); + vpn.setState(RemoteAccessVpn.State.Running); + _remoteAccessVpnDao.update(vpn.getServerAddress(), vpn); + } else { + EventUtils.saveEvent(userId, owner.getId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_REMOTE_ACCESS_VPN_CREATE, + "Unable to create Remote Access VPN ", owner.getAccountName()); + } + } } @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.acquireInLockTable(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); -// _rulesDao.deleteIPForwardingByPublicIpAndPort(publicIp, NetUtils.VPN_PORT); -// _rulesDao.deleteIPForwardingByPublicIpAndPort(publicIp, NetUtils.VPN_NATT_PORT); -// _rulesDao.deleteIPForwardingByPublicIpAndPort(publicIp, NetUtils.VPN_L2TP_PORT); -// 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.releaseFromLockTable(publicIp); -// } -// } - return false; // FIXME - } - - @DB @Override + @Override public boolean applyVpnUsers(long vpnOwnerId) { Account caller = UserContext.current().getCaller(); Account owner = _accountDao.findById(vpnOwnerId); @@ -347,33 +352,43 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag s_logger.debug("Applying vpn users for " + owner); List vpns = _remoteAccessVpnDao.findByAccount(vpnOwnerId); - + List users = _vpnUsersDao.listByAccount(vpnOwnerId); - List elements = null; - + List elements = _networkMgr.getRemoteAccessVpnElements(); + boolean success = true; - + boolean[] finals = new boolean[users.size()]; for (RemoteAccessVpnElement element : elements) { s_logger.debug("Applying vpn access to " + element.getName()); for (RemoteAccessVpnVO vpn : vpns) { - String[] results = element.applyVpnUsers(vpn, users); - - for (int i = 0; i < results.length; i++) { - s_logger.debug("VPN User " + users.get(i) + (results[i] == null ? " is set on " : (" couldn't be set due to " + results[i]) + " on ") + vpn); - if (results[i] == null) { - if (!finals[i]) { - finals[i] = true; + try { + String[] results = element.applyVpnUsers(vpn, users); + + for (int i = 0; i < results.length; i++) { + s_logger.debug("VPN User " + users.get(i) + + (results[i] == null ? " is set on " : (" couldn't be set due to " + results[i]) + " on ") + vpn); + if (results[i] == null) { + if (!finals[i]) { + finals[i] = true; + } + } else { + finals[i] = false; + success = false; } - } else { + } + } catch (ResourceUnavailableException e) { + s_logger.warn("Unable to apply vpn users ", e); + success= false; + + for (int i = 0; i < finals.length; i++) { finals[i] = false; - success = false; } } } } - + for (int i = 0; i < finals.length; i++) { if (finals[i]) { VpnUserVO user = users.get(i); @@ -385,10 +400,10 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag } } } - + return success; } - + @Override public List searchForVpnUsers(ListVpnUsersCmd cmd) { Account account = UserContext.current().getCaller(); @@ -400,7 +415,6 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag Filter searchFilter = new Filter(VpnUserVO.class, "username", true, cmd.getStartIndex(), cmd.getPageSizeVal()); Object id = cmd.getId(); - SearchBuilder sb = _vpnUsersDao.createSearchBuilder(); sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); @@ -408,14 +422,15 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag sb.and("accountId", sb.entity().getAccountId(), SearchCriteria.Op.EQ); if ((accountId == null) && (domainId != null)) { - // if accountId isn't specified, we can do a domain match for the admin case + // 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); } @@ -423,7 +438,6 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag if (username != null) { sc.setParameters("username", username); } - if (accountId != null) { sc.setParameters("accountId", accountId); @@ -434,7 +448,6 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag return _vpnUsersDao.search(sc, searchFilter); } - @Override public List searchForRemoteAccessVpns(ListRemoteAccessVpnsCmd cmd) { @@ -442,7 +455,7 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag Account caller = UserContext.current().getCaller(); String accountName = cmd.getAccountName(); Long domainId = cmd.getDomainId(); - + Ip ipAddress = cmd.getPublicIp(); if (ipAddress != null) { PublicIpAddress publicIp = _networkMgr.getPublicIpAddress(ipAddress); @@ -451,11 +464,12 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag } else { Long ipAddrAcctId = publicIp.getAllocatedToAccountId(); if (ipAddrAcctId == null) { - throw new InvalidParameterValueException("Unable to list remote access vpns, IP address " + ipAddress + " is not associated with an account."); + throw new InvalidParameterValueException("Unable to list remote access vpns, IP address " + ipAddress + + " is not associated with an account."); } } _accountMgr.checkAccess(caller, publicIp); - + List vpns = new ArrayList(1); vpns.add(_remoteAccessVpnDao.findById(ipAddress)); return vpns; @@ -469,9 +483,8 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag Filter searchFilter = new Filter(RemoteAccessVpnVO.class, "serverAddress", true, cmd.getStartIndex(), cmd.getPageSizeVal()); - SearchCriteria sc = VpnSearch.create(); - + sc.setParameters("accountId", owner.getId()); DomainVO domain = _domainDao.findById(domainId); sc.setJoinParameters("domainSearch", "path", domain.getPath() + "%"); @@ -479,21 +492,20 @@ public class RemoteAccessVpnManagerImpl implements RemoteAccessVpnService, Manag return _remoteAccessVpnDao.search(sc, searchFilter); } - @Override public boolean configure(String name, Map params) throws ConfigurationException { _name = name; - + ComponentLocator locator = ComponentLocator.getCurrentLocator(); ConfigurationDao configDao = locator.getDao(ConfigurationDao.class); Map configs = configDao.getConfiguration(params); - + _userLimit = NumbersUtil.parseInt(configs.get(Config.RemoteAccessVpnUserLimit.key()), 8); - + _clientIpRange = configs.get(Config.RemoteAccessVpnClientIpRange.key()); - + _pskLength = NumbersUtil.parseInt(configs.get(Config.RemoteAccessVpnPskLength.key()), 24); - + validateRemoteAccessVpnConfiguration(); VpnSearch = _remoteAccessVpnDao.createSearchBuilder(); diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index 9e78d68ef78..c6164fd10b0 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -1055,6 +1055,7 @@ CREATE TABLE `cloud`.`remote_access_vpn` ( `local_ip` varchar(15) NOT NULL, `ip_range` varchar(32) NOT NULL, `ipsec_psk` varchar(256) NOT NULL, + `state` char(32) NOT NULL, PRIMARY KEY (`vpn_server_addr`), CONSTRAINT `fk_remote_access_vpn__account_id` FOREIGN KEY `fk_remote_access_vpn__account_id`(`account_id`) REFERENCES `account` (`id`) ON DELETE CASCADE, CONSTRAINT `fk_remote_access_vpn__domain_id` FOREIGN KEY `fk_remote_access_vpn__domain_id`(`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE,