diff --git a/api/src/com/cloud/agent/api/routing/RemoteAccessVpnCfgCommand.java b/api/src/com/cloud/agent/api/routing/RemoteAccessVpnCfgCommand.java new file mode 100644 index 00000000000..c1bae24cd14 --- /dev/null +++ b/api/src/com/cloud/agent/api/routing/RemoteAccessVpnCfgCommand.java @@ -0,0 +1,80 @@ +/** + * 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 NetworkElementCommand { + + 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 vpnServerAddress, String localIp, String ipRange, String ipsecPresharedKey) { + 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; + } + +} diff --git a/api/src/com/cloud/agent/api/routing/VpnUsersCfgCommand.java b/api/src/com/cloud/agent/api/routing/VpnUsersCfgCommand.java new file mode 100644 index 00000000000..c5391a031de --- /dev/null +++ b/api/src/com/cloud/agent/api/routing/VpnUsersCfgCommand.java @@ -0,0 +1,93 @@ +/** + * 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; +import java.util.List; + +import com.cloud.network.VpnUser; + + +public class VpnUsersCfgCommand extends NetworkElementCommand { + public static class UsernamePassword{ + private String username; + private String password; + boolean add = true; + + public boolean isAdd() { + return add; + } + public void setAdd(boolean add) { + this.add = add; + } + public String getUsername() { + return username; + } + public void setUsername(String username) { + this.username = username; + } + public String getPassword() { + return password; + } + public void setPassword(String password) { + this.password = password; + } + public UsernamePassword(String username, String password) { + super(); + this.username = username; + this.password = password; + } + public UsernamePassword(String username, String password, boolean add) { + super(); + this.username = username; + this.password = password; + this.add = add; + } + protected UsernamePassword() { + //for Gson + } + public String getUsernamePassword() { + return getUsername() + "," + getPassword(); + } + } + UsernamePassword [] userpwds; + + protected VpnUsersCfgCommand() { + + } + + public VpnUsersCfgCommand(List addUsers, List removeUsers) { + userpwds = new UsernamePassword[addUsers.size() + removeUsers.size()]; + int i = 0; + for (VpnUser vpnUser: removeUsers) { + userpwds[i++] = new UsernamePassword(vpnUser.getUsername(), vpnUser.getPassword(), false); + } + for (VpnUser vpnUser: addUsers) { + userpwds[i++] = new UsernamePassword(vpnUser.getUsername(), vpnUser.getPassword(), true); + } + } + + @Override + public boolean executeInSequence() { + return true; + } + + public UsernamePassword[] getUserpwds() { + return userpwds; + } + +} diff --git a/api/src/com/cloud/api/BaseCmd.java b/api/src/com/cloud/api/BaseCmd.java index b2825560b99..80f0dd1aa0c 100755 --- a/api/src/com/cloud/api/BaseCmd.java +++ b/api/src/com/cloud/api/BaseCmd.java @@ -42,6 +42,7 @@ import com.cloud.network.VirtualNetworkApplianceService; import com.cloud.network.lb.LoadBalancingRulesService; import com.cloud.network.rules.RulesService; import com.cloud.network.security.SecurityGroupService; +import com.cloud.network.vpn.RemoteAccessVpnService; import com.cloud.resource.ResourceService; import com.cloud.server.ManagementService; import com.cloud.storage.StorageService; @@ -52,8 +53,8 @@ import com.cloud.user.AccountService; import com.cloud.user.UserContext; import com.cloud.utils.Pair; import com.cloud.utils.component.ComponentLocator; -import com.cloud.vm.BareMetalVmService; import com.cloud.vm.UserVmService; +import com.cloud.vm.BareMetalVmService; public abstract class BaseCmd { private static final Logger s_logger = Logger.getLogger(BaseCmd.class.getName()); @@ -112,6 +113,7 @@ public abstract class BaseCmd { public static EntityManager _entityMgr; public static RulesService _rulesService; public static LoadBalancingRulesService _lbService; + public static RemoteAccessVpnService _ravService; public static BareMetalVmService _bareMetalVmService; @@ -132,6 +134,7 @@ public abstract class BaseCmd { _entityMgr = locator.getManager(EntityManager.class); _rulesService = locator.getManager(RulesService.class); _lbService = locator.getManager(LoadBalancingRulesService.class); + _ravService = locator.getManager(RemoteAccessVpnService.class); _responseGenerator = generator; _bareMetalVmService = locator.getManager(BareMetalVmService.class); } diff --git a/api/src/com/cloud/api/ResponseGenerator.java b/api/src/com/cloud/api/ResponseGenerator.java index a0fa8aa8621..7683ad99cd3 100755 --- a/api/src/com/cloud/api/ResponseGenerator.java +++ b/api/src/com/cloud/api/ResponseGenerator.java @@ -19,7 +19,6 @@ package com.cloud.api; import java.text.DecimalFormat; import java.util.List; -import java.util.Set; import com.cloud.api.commands.QueryAsyncJobResultCmd; import com.cloud.api.response.AccountResponse; @@ -43,8 +42,9 @@ import com.cloud.api.response.LoadBalancerResponse; import com.cloud.api.response.NetworkOfferingResponse; import com.cloud.api.response.NetworkResponse; import com.cloud.api.response.PodResponse; -import com.cloud.api.response.ResourceLimitResponse; +import com.cloud.api.response.RemoteAccessVpnResponse; import com.cloud.api.response.ResourceCountResponse; +import com.cloud.api.response.ResourceLimitResponse; import com.cloud.api.response.SecurityGroupResponse; import com.cloud.api.response.ServiceOfferingResponse; import com.cloud.api.response.SnapshotPolicyResponse; @@ -57,12 +57,13 @@ import com.cloud.api.response.UserResponse; import com.cloud.api.response.UserVmResponse; import com.cloud.api.response.VlanIpRangeResponse; import com.cloud.api.response.VolumeResponse; +import com.cloud.api.response.VpnUsersResponse; import com.cloud.api.response.ZoneResponse; import com.cloud.async.AsyncJob; import com.cloud.capacity.Capacity; import com.cloud.configuration.Configuration; -import com.cloud.configuration.ResourceLimit; import com.cloud.configuration.ResourceCount; +import com.cloud.configuration.ResourceLimit; import com.cloud.dc.DataCenter; import com.cloud.dc.Pod; import com.cloud.dc.Vlan; @@ -71,6 +72,8 @@ import com.cloud.event.Event; import com.cloud.host.Host; import com.cloud.network.IpAddress; import com.cloud.network.Network; +import com.cloud.network.RemoteAccessVpn; +import com.cloud.network.VpnUser; import com.cloud.network.router.VirtualRouter; import com.cloud.network.rules.LoadBalancer; import com.cloud.network.rules.PortForwardingRule; @@ -91,7 +94,6 @@ import com.cloud.user.Account; import com.cloud.user.User; import com.cloud.user.UserAccount; import com.cloud.uservm.UserVm; -import com.cloud.utils.Pair; import com.cloud.vm.InstanceGroup; import com.cloud.vm.VirtualMachine; @@ -159,7 +161,12 @@ public interface ResponseGenerator { Host findHostById(Long hostId); List createTemplateResponses(long templateId, Long zoneId, boolean readyOnly); + List createTemplateResponses(long templateId, long zoneId, boolean readyOnly); + + VpnUsersResponse createVpnUserResponse(VpnUser user); + + RemoteAccessVpnResponse createRemoteAccessVpnResponse(RemoteAccessVpn vpn); List createTemplateResponses(long templateId, Long snapshotId, Long volumeId, boolean readyOnly); diff --git a/api/src/com/cloud/api/commands/AddVpnUserCmd.java b/api/src/com/cloud/api/commands/AddVpnUserCmd.java new file mode 100644 index 00000000000..da7c94e1b12 --- /dev/null +++ b/api/src/com/cloud/api/commands/AddVpnUserCmd.java @@ -0,0 +1,161 @@ +/** + * 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.BaseAsyncCreateCmd; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.VpnUsersResponse; +import com.cloud.domain.Domain; +import com.cloud.event.EventTypes; +import com.cloud.network.VpnUser; +import com.cloud.user.Account; +import com.cloud.user.UserContext; + +@Implementation(description="Adds vpn users", responseObject=VpnUsersResponse.class) +public class AddVpnUserCmd extends BaseAsyncCreateCmd { + public static final Logger s_logger = Logger.getLogger(AddVpnUserCmd.class.getName()); + + private static final String s_name = "addvpnuserresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name="username", type=CommandType.STRING, required=true, description="username for the vpn user") + private String userName; + + @Parameter(name="password", type=CommandType.STRING, required=true, description="password for the username") + private String password; + + @Parameter(name="account", type=CommandType.STRING, description="an optional account for the vpn user. Must be used with domainId.") + private String accountName; + + @Parameter(name="domainid", type=CommandType.LONG, description="an optional domainId for the vpn user. If the account parameter is used, domainId must also be used.") + private Long domainId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + Account account = UserContext.current().getCaller(); + if ((account == null) || isAdmin(account.getType())) { + if ((domainId != null) && (accountName != null)) { + Account userAccount = _responseGenerator.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 "Add Remote Access VPN user for account " + getEntityOwnerId() + " username= " + getUserName(); + } + + + + @Override + public String getEventType() { + return EventTypes.EVENT_VPN_USER_ADD; + } + + @Override + public void execute(){ + VpnUser vpnUser = _entityMgr.findById(VpnUser.class, getEntityId()); + Account account = _entityMgr.findById(Account.class, vpnUser.getAccountId()); + if (!_ravService.applyVpnUsers(vpnUser.getAccountId())) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to add vpn user"); + } + + VpnUsersResponse vpnResponse = new VpnUsersResponse(); + vpnResponse.setId(vpnUser.getId()); + vpnResponse.setUserName(vpnUser.getUsername()); + vpnResponse.setAccountName(account.getAccountName()); + + vpnResponse.setDomainId(account.getDomainId()); + vpnResponse.setDomainName(_entityMgr.findById(Domain.class, account.getDomainId()).getName()); + + vpnResponse.setResponseName(getCommandName()); + vpnResponse.setObjectName("vpnuser"); + this.setResponseObject(vpnResponse); + } + + @Override + public void create() { + Account owner = null; + if (accountName != null) { + owner = _responseGenerator.findAccountByNameDomain(accountName, domainId); + } else { + owner = UserContext.current().getCaller(); + } + + VpnUser vpnUser = _ravService.addVpnUser(owner.getId(), userName, password); + if (vpnUser == null) { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to add vpn user"); + } + setEntityId(vpnUser.getId()); + } +} diff --git a/api/src/com/cloud/api/commands/CreateRemoteAccessVpnCmd.java b/api/src/com/cloud/api/commands/CreateRemoteAccessVpnCmd.java new file mode 100644 index 00000000000..84674a3e948 --- /dev/null +++ b/api/src/com/cloud/api/commands/CreateRemoteAccessVpnCmd.java @@ -0,0 +1,176 @@ +/** + * 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.ApiConstants; +import com.cloud.api.BaseAsyncCmd; +import com.cloud.api.BaseAsyncCreateCmd; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.RemoteAccessVpnResponse; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.IpAddress; +import com.cloud.network.RemoteAccessVpn; +import com.cloud.user.Account; +import com.cloud.user.UserContext; + +@Implementation(description="Creates a l2tp/ipsec remote access vpn", responseObject=RemoteAccessVpnResponse.class) +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=ApiConstants.PUBLIC_IP_ID, type=CommandType.LONG, required=true, description="public ip address id of the vpn server") + private Long publicIpId; + + @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 VPN. Must be used with domainId.") + private String accountName; + + @Parameter(name="domainid", type=CommandType.LONG, description="an optional domainId for the VPN. If the account parameter is used, domainId must also be used.") + private Long domainId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getPublicIpId() { + return publicIpId; + } + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public String getIpRange() { + return ipRange; + } + + public void setIpRange(String ipRange) { + this.ipRange = ipRange; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + Account account = UserContext.current().getCaller(); + if ((account == null) || isAdmin(account.getType())) { + if ((domainId != null) && (accountName != null)) { + Account userAccount = _responseGenerator.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 " + getEntityOwnerId() + " using public ip id=" + publicIpId; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_REMOTE_ACCESS_VPN_CREATE; + } + + @Override + public void create() { + try { + RemoteAccessVpn vpn = _ravService.createRemoteAccessVpn(publicIpId, ipRange); + if (vpn != null) { + this.setEntityId(vpn.getServerAddressId()); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create remote access vpn"); + } + } catch (NetworkRuleConflictException e) { + s_logger.info("Network rule conflict: " + e.getMessage()); + s_logger.trace("Network Rule Conflict: ", e); + throw new ServerApiException(BaseCmd.NETWORK_RULE_CONFLICT_ERROR, e.getMessage()); + } + } + + @Override + public void execute(){ + try { + RemoteAccessVpn result = _ravService.startRemoteAccessVpn(publicIpId); + if (result != null) { + RemoteAccessVpnResponse response = _responseGenerator.createRemoteAccessVpnResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create remote access vpn"); + } + } catch (ResourceUnavailableException ex) { + s_logger.warn("Exception: ", ex); + throw new ServerApiException(BaseCmd.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); + } + } + + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + return getIp().getAssociatedWithNetworkId(); + } + + private IpAddress getIp() { + IpAddress ip = _networkService.getIp(publicIpId); + if (ip == null) { + throw new InvalidParameterValueException("Unable to find ip address by id " + publicIpId); + } + return ip; + } +} diff --git a/api/src/com/cloud/api/commands/DeleteRemoteAccessVpnCmd.java b/api/src/com/cloud/api/commands/DeleteRemoteAccessVpnCmd.java new file mode 100644 index 00000000000..8077b941cd0 --- /dev/null +++ b/api/src/com/cloud/api/commands/DeleteRemoteAccessVpnCmd.java @@ -0,0 +1,93 @@ +/** + * 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.ApiConstants; +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.exception.ResourceUnavailableException; +import com.cloud.network.RemoteAccessVpn; + +@Implementation(description="Destroys a l2tp/ipsec remote access vpn", responseObject=SuccessResponse.class) +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=ApiConstants.PUBLIC_IP_ID, type=CommandType.LONG, required=true, description="public ip address id of the vpn server") + private Long publicIpId; + + // unexposed parameter needed for events logging + @Parameter(name=ApiConstants.ACCOUNT_ID, type=CommandType.LONG, expose=false) + private Long ownerId; + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + if (ownerId == null) { + ownerId = _entityMgr.findById(RemoteAccessVpn.class, publicIpId).getAccountId(); + } + return ownerId; + } + + @Override + public String getEventDescription() { + return "Delete Remote Access VPN for account " + getEntityOwnerId() + " for ip id=" + publicIpId; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_REMOTE_ACCESS_VPN_DESTROY; + } + + @Override + public void execute() throws ResourceUnavailableException { + _ravService.destroyRemoteAccessVpn(publicIpId); + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + return _ravService.getRemoteAccessVpn(publicIpId).getNetworkId(); + } + +} diff --git a/api/src/com/cloud/api/commands/ListRemoteAccessVpnsCmd.java b/api/src/com/cloud/api/commands/ListRemoteAccessVpnsCmd.java new file mode 100644 index 00000000000..fedcb13784f --- /dev/null +++ b/api/src/com/cloud/api/commands/ListRemoteAccessVpnsCmd.java @@ -0,0 +1,92 @@ +/** + * 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.ApiConstants; +import com.cloud.api.BaseListCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.response.ListResponse; +import com.cloud.api.response.RemoteAccessVpnResponse; +import com.cloud.network.RemoteAccessVpn; + +@Implementation(description="Lists remote access vpns", responseObject=RemoteAccessVpnResponse.class) +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=ApiConstants.PUBLIC_IP_ID, type=CommandType.LONG, required=true, description="public ip address id of the vpn server") + private Long publicIpId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public Long getPublicIpId() { + return publicIpId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute(){ + List vpns = _ravService.searchForRemoteAccessVpns(this); + ListResponse response = new ListResponse(); + List vpnResponses = new ArrayList(); + if (vpns != null && !vpns.isEmpty()) { + for (RemoteAccessVpn vpn : vpns) { + vpnResponses.add(_responseGenerator.createRemoteAccessVpnResponse(vpn)); + } + } + response.setResponses(vpnResponses); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } +} diff --git a/api/src/com/cloud/api/commands/ListVpnUsersCmd.java b/api/src/com/cloud/api/commands/ListVpnUsersCmd.java new file mode 100644 index 00000000000..39d01e3033c --- /dev/null +++ b/api/src/com/cloud/api/commands/ListVpnUsersCmd.java @@ -0,0 +1,98 @@ +/** + * 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.BaseListCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.response.ListResponse; +import com.cloud.api.response.VpnUsersResponse; +import com.cloud.network.VpnUser; + +@Implementation(description="Lists vpn users", responseObject=VpnUsersResponse.class) +public class ListVpnUsersCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger (ListVpnUsersCmd.class.getName()); + + private static final String s_name = "listvpnusersresponse"; + + ///////////////////////////////////////////////////// + //////////////// 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. 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 vpn user") + private Long id; + + @Parameter(name="username", type=CommandType.STRING, description="the username of the vpn user.") + private String userName; + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public Long getId() { + return id; + } + + public String getUsername() { + return userName; + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute(){ + List vpnUsers = _ravService.searchForVpnUsers(this); + + ListResponse response = new ListResponse(); + List vpnResponses = new ArrayList(); + for (VpnUser vpnUser : vpnUsers) { + vpnResponses.add(_responseGenerator.createVpnUserResponse(vpnUser)); + } + + response.setResponses(vpnResponses); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } +} diff --git a/api/src/com/cloud/api/commands/RemoveVpnUserCmd.java b/api/src/com/cloud/api/commands/RemoveVpnUserCmd.java new file mode 100644 index 00000000000..972b767014e --- /dev/null +++ b/api/src/com/cloud/api/commands/RemoveVpnUserCmd.java @@ -0,0 +1,126 @@ +/** + * 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.BaseAsyncCmd; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.SuccessResponse; +import com.cloud.event.EventTypes; +import com.cloud.user.Account; +import com.cloud.user.UserContext; + +@Implementation(description="Removes vpn user", responseObject=SuccessResponse.class) +public class RemoveVpnUserCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(RemoveVpnUserCmd.class.getName()); + + private static final String s_name = "removevpnuserresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + @Parameter(name="username", type=CommandType.STRING, required=true, description="username for the vpn user") + private String userName; + + @Parameter(name="account", type=CommandType.STRING, description="an optional account for the vpn user. Must be used with domainId.") + private String accountName; + + @Parameter(name="domainid", type=CommandType.LONG, description="an optional domainId for the vpn user. If the account parameter is used, domainId must also be used.") + private Long domainId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + Account account = UserContext.current().getCaller(); + if ((account == null) || isAdmin(account.getType())) { + if ((domainId != null) && (accountName != null)) { + Account userAccount = _responseGenerator.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 "Remove Remote Access VPN user for account " + getEntityOwnerId() + " username= " + getUserName(); + } + + + @Override + public String getEventType() { + return EventTypes.EVENT_VPN_USER_REMOVE; + } + + @Override + public void execute(){ + Account owner = getValidOwner(accountName, domainId); + boolean result = _ravService.removeVpnUser(owner.getId(), userName); + 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/api/response/RemoteAccessVpnResponse.java b/api/src/com/cloud/api/response/RemoteAccessVpnResponse.java new file mode 100644 index 00000000000..de514c06b4e --- /dev/null +++ b/api/src/com/cloud/api/response/RemoteAccessVpnResponse.java @@ -0,0 +1,116 @@ +/** + * 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.api.ApiConstants; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class RemoteAccessVpnResponse extends BaseResponse { + + @SerializedName(ApiConstants.PUBLIC_IP_ID) @Param(description="the public ip address of the vpn server") + private Long publicIpId; + + @SerializedName(ApiConstants.PUBLIC_IP) @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; + + @SerializedName("state") @Param(description="the state of the rule") + private String state; + + public String getAccountName() { + return accountName; + } + + 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; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public Long getPublicIpId() { + return publicIpId; + } + + public void setPublicIpId(Long publicIpId) { + this.publicIpId = publicIpId; + } + +} diff --git a/api/src/com/cloud/api/response/VpnUsersResponse.java b/api/src/com/cloud/api/response/VpnUsersResponse.java new file mode 100644 index 00000000000..516b064bbda --- /dev/null +++ b/api/src/com/cloud/api/response/VpnUsersResponse.java @@ -0,0 +1,80 @@ +/** + * 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 VpnUsersResponse extends BaseResponse { + @SerializedName("id") @Param(description="the vpn userID") + private Long id; + + @SerializedName("username") @Param(description="the username of the vpn user") + private String userName; + + @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 getUserName() { + return userName; + } + + public void setUserName(String name) { + this.userName = name; + } + + 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/api/src/com/cloud/network/RemoteAccessVpn.java b/api/src/com/cloud/network/RemoteAccessVpn.java new file mode 100644 index 00000000000..c39722de9c0 --- /dev/null +++ b/api/src/com/cloud/network/RemoteAccessVpn.java @@ -0,0 +1,35 @@ +/** + * 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 com.cloud.acl.ControlledEntity; + +public interface RemoteAccessVpn extends ControlledEntity { + enum State { + Added, + Running, + Removed + } + + long getServerAddressId(); + String getIpRange(); + String getIpsecPresharedKey(); + String getLocalIp(); + long getNetworkId(); + State getState(); +} diff --git a/api/src/com/cloud/network/VpnUser.java b/api/src/com/cloud/network/VpnUser.java new file mode 100644 index 00000000000..c7a68238e88 --- /dev/null +++ b/api/src/com/cloud/network/VpnUser.java @@ -0,0 +1,36 @@ +/** + * 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 com.cloud.acl.ControlledEntity; + +public interface VpnUser extends ControlledEntity { + enum State { + Add, + Revoke, + Active + } + + long getId(); + + String getUsername(); + + String getPassword(); + + State getState(); +} diff --git a/api/src/com/cloud/network/vpn/RemoteAccessVpnElement.java b/api/src/com/cloud/network/vpn/RemoteAccessVpnElement.java new file mode 100644 index 00000000000..0a1dfbfd379 --- /dev/null +++ b/api/src/com/cloud/network/vpn/RemoteAccessVpnElement.java @@ -0,0 +1,34 @@ +/** + * 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.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) throws ResourceUnavailableException; + + boolean startVpn(Network network, RemoteAccessVpn vpn) throws ResourceUnavailableException; + + boolean stopVpn(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 new file mode 100644 index 00000000000..a5628bcc894 --- /dev/null +++ b/api/src/com/cloud/network/vpn/RemoteAccessVpnService.java @@ -0,0 +1,47 @@ +/** + * 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.vpn; + +import java.util.List; + +import com.cloud.api.commands.ListRemoteAccessVpnsCmd; +import com.cloud.api.commands.ListVpnUsersCmd; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.RemoteAccessVpn; +import com.cloud.network.VpnUser; + +public interface RemoteAccessVpnService { + + RemoteAccessVpn createRemoteAccessVpn(long vpnServerAddressId, String ipRange) throws NetworkRuleConflictException; + void destroyRemoteAccessVpn(long vpnServerAddressId) throws ResourceUnavailableException; + RemoteAccessVpn startRemoteAccessVpn(long vpnServerAddressId) throws ResourceUnavailableException; + + VpnUser addVpnUser(long vpnOwnerId, String userName, String password); + boolean removeVpnUser(long vpnOwnerId, String userName); + List listVpnUsers(long vpnOwnerId, String userName); + boolean applyVpnUsers(long vpnOwnerId); + + List searchForRemoteAccessVpns(ListRemoteAccessVpnsCmd cmd); + List searchForVpnUsers(ListVpnUsersCmd cmd); + + List listRemoteAccessVpns(long networkId); + + RemoteAccessVpn getRemoteAccessVpn(long vpnId); + +} diff --git a/build/build-cloud.xml b/build/build-cloud.xml index 2337fa292a9..a50ad6123d7 100755 --- a/build/build-cloud.xml +++ b/build/build-cloud.xml @@ -543,6 +543,9 @@ + + + diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index a3dfea18445..7d941e4ea52 100755 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -231,6 +231,15 @@ uploadCustomCertificate=com.cloud.api.commands.UploadCustomCertificateCmd;1 ### other commands 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 + +addVpnUser=com.cloud.api.commands.AddVpnUserCmd;15 +removeVpnUser=com.cloud.api.commands.RemoveVpnUserCmd;15 +listVpnUsers=com.cloud.api.commands.ListVpnUsersCmd;15 + #### network offering commands #### createNetworkOffering=com.cloud.api.commands.CreateNetworkOfferingCmd;1 updateNetworkOffering=com.cloud.api.commands.UpdateNetworkOfferingCmd;1 diff --git a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 095b28c1db9..62d1c02aa17 100644 --- a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -138,12 +138,14 @@ import com.cloud.agent.api.routing.IpAssocCommand; import com.cloud.agent.api.routing.IpAssocAnswer; import com.cloud.agent.api.routing.LoadBalancerConfigCommand; import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; import com.cloud.agent.api.routing.SavePasswordCommand; import com.cloud.agent.api.routing.SetPortForwardingRulesAnswer; import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; import com.cloud.agent.api.routing.SetStaticNatRulesAnswer; import com.cloud.agent.api.routing.SetStaticNatRulesCommand; import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.agent.api.routing.VpnUsersCfgCommand; import com.cloud.agent.api.storage.CopyVolumeAnswer; import com.cloud.agent.api.storage.CopyVolumeCommand; import com.cloud.agent.api.storage.CreateAnswer; @@ -468,6 +470,10 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return execute((PoolEjectCommand) cmd); } else if (clazz == StartCommand.class) { return execute((StartCommand)cmd); + } else if (clazz == RemoteAccessVpnCfgCommand.class) { + return execute((RemoteAccessVpnCfgCommand)cmd); + } else if (clazz == VpnUsersCfgCommand.class) { + return execute((VpnUsersCfgCommand)cmd); } else if (clazz == CheckSshCommand.class) { return execute((CheckSshCommand)cmd); } else if (clazz == SecurityIngressRulesCmd.class) { @@ -1352,6 +1358,45 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } return new Answer(cmd); } + + protected synchronized Answer execute(final RemoteAccessVpnCfgCommand cmd) { + Connection conn = getConnection(); + String args = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); + if (cmd.isCreate()) { + args += " -r " + cmd.getIpRange(); + args += " -p " + cmd.getPresharedKey(); + args += " -s " + cmd.getVpnServerIp(); + args += " -l " + cmd.getLocalIp(); + args += " -c "; + + } else { + args += " -d "; + args += " -s " + cmd.getVpnServerIp(); + } + String result = callHostPlugin(conn, "vmops", "lt2p_vpn", "args", args); + if (result == null || result.isEmpty()) { + return new Answer(cmd, false, "Configure VPN failed"); + } + return new Answer(cmd); + } + + protected synchronized Answer execute(final VpnUsersCfgCommand cmd) { + Connection conn = getConnection(); + for (VpnUsersCfgCommand.UsernamePassword userpwd: cmd.getUserpwds()) { + String args = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); + if (!userpwd.isAdd()) { + args += " -U " + userpwd.getUsername(); + } else { + args += " -u " + userpwd.getUsernamePassword(); + } + String result = callHostPlugin(conn, "vmops", "lt2p_vpn", "args", args); + if (result == null || result.isEmpty()) { + return new Answer(cmd, false, "Configure VPN user failed for user " + userpwd.getUsername()); + } + } + + return new Answer(cmd); + } protected Answer execute(final VmDataCommand cmd) { Connection conn = getConnection(); diff --git a/core/src/com/cloud/network/VpnUserVO.java b/core/src/com/cloud/network/VpnUserVO.java new file mode 100644 index 00000000000..586fb690db4 --- /dev/null +++ b/core/src/com/cloud/network/VpnUserVO.java @@ -0,0 +1,111 @@ +/** + * 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.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name=("vpn_users")) +public class VpnUserVO implements VpnUser { + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private long id; + + @Column(name="owner_id") + private long accountId; + + @Column(name="domain_id") + private long domainId; + + @Column(name="username") + private String username; + + @Column(name="password") + private String password; + + @Column(name="state") + @Enumerated(value=EnumType.STRING) + private State state; + + public VpnUserVO() { } + + public VpnUserVO(long accountId, long domainId, String userName, String password) { + this.accountId = accountId; + this.domainId = domainId; + this.username = userName; + this.password = password; + this.state = State.Add; + } + + @Override + public long getId() { + return id; + } + + @Override + public long getAccountId() { + return accountId; + } + + @Override + public String getUsername() { + return username; + } + + public void setUsername(String userName) { + this.username = userName; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public State getState() { + return state; + } + + public void setState(State state) { + this.state = state; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public long getDomainId() { + return domainId; + } + + + @Override + public String toString() { + return new StringBuilder("VpnUser[").append(id).append("-").append(username).append("-").append(accountId).append("]").toString(); + } +} diff --git a/patches/systemvm/debian/buildsystemvm.sh b/patches/systemvm/debian/buildsystemvm.sh index d45cc4b9050..0b1756b38f0 100755 --- a/patches/systemvm/debian/buildsystemvm.sh +++ b/patches/systemvm/debian/buildsystemvm.sh @@ -348,6 +348,10 @@ EOF } +vpn_config() { + cp -r ${scriptdir}/vpn/* ./ +} + packages() { DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical @@ -368,6 +372,8 @@ packages() { chroot . apt-get --no-install-recommends -q -y --force-yes install dnsmasq #nfs client chroot . apt-get --no-install-recommends -q -y --force-yes install nfs-common + #vpn stuff + chroot . apt-get --no-install-recommends -q -y --force-yes install xl2tpd openswan bcrelay ppp ipsec-tools tdb-tools #vmware tools chroot . apt-get --no-install-recommends -q -y --force-yes install open-vm-tools #xenstore utils @@ -437,6 +443,7 @@ cleanup() { signature() { (cd ${scriptdir}/config; tar cvf ${MOUNTPOINT}/usr/share/cloud/cloud-scripts.tar *) + (cd ${scriptdir}/vpn; tar rvf ${MOUNTPOINT}/usr/share/cloud/cloud-scripts.tar *) gzip -c ${MOUNTPOINT}/usr/share/cloud/cloud-scripts.tar > ${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 $CLOUDSTACK_RELEASE $(date)" > ${MOUNTPOINT}/etc/cloudstack-release @@ -505,6 +512,9 @@ services echo "*************CONFIGURING APACHE********************" apache2 +echo "*************CONFIGURING VPN********************" +vpn_config + echo "*************CLEANING UP********************" cleanup 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..abffc5ef208 --- /dev/null +++ b/patches/systemvm/debian/vpn/opt/cloud/bin/vpn_l2tp.sh @@ -0,0 +1,185 @@ +#!/bin/bash + + + + # + # 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 . + # + + + +#set -x +usage() { + printf "Usage:\n" + printf "Create VPN : %s -c -r -l -p -s \n" $(basename $0) + printf "Delete VPN : %s -d -s \n" $(basename $0) + printf "Add VPN User : %s -u \n" $(basename $0) + printf "Remote VPN User: %s -U /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" $public_ip + iptables_ "-I" $public_ip + + ipsec_server "restart" + + ipsec auto --rereadsecrets + ipsec auto --replace L2TP-PSK +} + +destroy_l2tp_ipsec_vpn_server() { + local public_ip=$1 + + ipsec auto --down L2TP-PSK + + iptables_ "-D" $public_ip + + ipsec_server "stop" +} + +remove_l2tp_ipsec_user() { + local u=$1 + sed -i -e "/^$u .*$/d" /etc/ppp/chap-secrets + if [ -x /usr/bin/tdbdump ]; then + pid=$(tdbdump /var/run/pppd2.tdb | grep -w $u | awk -F';' '{print $4}' | awk -F= '{print $2}') + [ "$pid" != "" ] && kill -9 $pid + fi + return 0 +} + +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 $server_ip + 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..d71b0fb9575 --- /dev/null +++ b/scripts/network/domr/l2tp_vpn.sh @@ -0,0 +1,31 @@ +#!/bin/bash + + + + # + # 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 . + # + + +# +# @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 d1f63f50fe1..5056c88581e 100755 --- a/scripts/vm/hypervisor/xenserver/vmops +++ b/scripts/vm/hypervisor/xenserver/vmops @@ -229,6 +229,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): @@ -1043,6 +1058,6 @@ def checkRouter(session, args): return txt if __name__ == "__main__": - XenAPIPlugin.dispatch({"pingtest": pingtest, "setup_iscsi":setup_iscsi, "gethostvmstats": gethostvmstats, "getvncport": getvncport, "getgateway": getgateway, "preparemigration": preparemigration, "setIptables": setIptables, "pingdomr": pingdomr, "pingxenserver": pingxenserver, "ipassoc": ipassoc, "vm_data": vm_data, "savePassword": savePassword, "saveDhcpEntry": saveDhcpEntry, "setFirewallRule": setFirewallRule, "setLoadBalancerRule": setLoadBalancerRule, "createFile": createFile, "deleteFile": deleteFile, "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, "cleanup_rules":cleanup_rules, "checkRouter": checkRouter}) + XenAPIPlugin.dispatch({"pingtest": pingtest, "setup_iscsi":setup_iscsi, "gethostvmstats": gethostvmstats, "getvncport": getvncport, "getgateway": getgateway, "preparemigration": preparemigration, "setIptables": setIptables, "pingdomr": pingdomr, "pingxenserver": pingxenserver, "ipassoc": ipassoc, "vm_data": vm_data, "savePassword": savePassword, "saveDhcpEntry": saveDhcpEntry, "setFirewallRule": setFirewallRule, "setLoadBalancerRule": setLoadBalancerRule, "createFile": createFile, "deleteFile": deleteFile, "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, "cleanup_rules":cleanup_rules, "checkRouter": checkRouter}) diff --git a/scripts/vm/hypervisor/xenserver/xenserver56/patch b/scripts/vm/hypervisor/xenserver/xenserver56/patch index e702482b9e4..8a3fe90f8ac 100644 --- a/scripts/vm/hypervisor/xenserver/xenserver56/patch +++ b/scripts/vm/hypervisor/xenserver/xenserver56/patch @@ -29,6 +29,7 @@ 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 copy_vhd_to_secondarystorage.sh=..,0755,/opt/xensource/bin copy_vhd_from_secondarystorage.sh=..,0755,/opt/xensource/bin setup_heartbeat_sr.sh=..,0755,/opt/xensource/bin diff --git a/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch b/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch index f277fe53904..887ff6a77e7 100644 --- a/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch +++ b/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch @@ -30,6 +30,7 @@ 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 cloud-setup-bonding.sh=..,0755,/opt/xensource/bin copy_vhd_to_secondarystorage.sh=..,0755,/opt/xensource/bin copy_vhd_from_secondarystorage.sh=..,0755,/opt/xensource/bin diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 13c83c6b06b..3e4795ea091 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -57,6 +57,7 @@ import com.cloud.api.response.NetworkOfferingResponse; import com.cloud.api.response.NetworkResponse; import com.cloud.api.response.NicResponse; import com.cloud.api.response.PodResponse; +import com.cloud.api.response.RemoteAccessVpnResponse; import com.cloud.api.response.ResourceLimitResponse; import com.cloud.api.response.ResourceCountResponse; import com.cloud.api.response.SecurityGroupResponse; @@ -73,6 +74,7 @@ import com.cloud.api.response.UserResponse; import com.cloud.api.response.UserVmResponse; import com.cloud.api.response.VlanIpRangeResponse; import com.cloud.api.response.VolumeResponse; +import com.cloud.api.response.VpnUsersResponse; import com.cloud.api.response.ZoneResponse; import com.cloud.async.AsyncJob; import com.cloud.async.AsyncJobResult; @@ -104,6 +106,8 @@ import com.cloud.network.Network.Capability; import com.cloud.network.Network.Service; import com.cloud.network.NetworkProfile; import com.cloud.network.Networks.TrafficType; +import com.cloud.network.RemoteAccessVpn; +import com.cloud.network.VpnUser; import com.cloud.network.router.VirtualRouter; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.LoadBalancer; @@ -1242,6 +1246,43 @@ public class ApiResponseHelper implements ResponseGenerator { return ApiDBUtils.findTemplateById(templateId); } + @Override + public VpnUsersResponse createVpnUserResponse(VpnUser vpnUser) { + VpnUsersResponse vpnResponse = new VpnUsersResponse(); + vpnResponse.setId(vpnUser.getId()); + vpnResponse.setUserName(vpnUser.getUsername()); + + Account accountTemp = ApiDBUtils.findAccountById(vpnUser.getAccountId()); + if (accountTemp != null) { + vpnResponse.setAccountName(accountTemp.getAccountName()); + vpnResponse.setDomainId(accountTemp.getDomainId()); + vpnResponse.setDomainName(ApiDBUtils.findDomainById(accountTemp.getDomainId()).getName()); + } + + vpnResponse.setObjectName("vpnuser"); + return vpnResponse; + } + + @Override + public RemoteAccessVpnResponse createRemoteAccessVpnResponse(RemoteAccessVpn vpn) { + RemoteAccessVpnResponse vpnResponse = new RemoteAccessVpnResponse(); + vpnResponse.setPublicIpId(vpn.getServerAddressId()); + vpnResponse.setPublicIp(ApiDBUtils.findIpAddressById(vpn.getServerAddressId()).getAddress().addr()); + vpnResponse.setIpRange(vpn.getIpRange()); + vpnResponse.setPresharedKey(vpn.getIpsecPresharedKey()); + vpnResponse.setDomainId(vpn.getDomainId()); + + Account accountTemp = ApiDBUtils.findAccountById(vpn.getAccountId()); + if (accountTemp != null) { + vpnResponse.setAccountName(accountTemp.getAccountName()); + vpnResponse.setDomainName(ApiDBUtils.findDomainById(accountTemp.getDomainId()).getName()); + } + vpnResponse.setState(vpn.getState().toString()); + vpnResponse.setObjectName("remoteaccessvpn"); + + return vpnResponse; + } + @Override public TemplateResponse createIsoResponse(VirtualMachineTemplate result) { TemplateResponse response = new TemplateResponse(); diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index e513f701937..e7adc32bbef 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -68,12 +68,20 @@ public enum Config { OvsNetwork("Network", ManagementServer.class, Boolean.class, "open.vswitch.vlan.network", "false", "enable/disable vlan remapping of open vswitch network", null), OvsTunnelNetwork("Network", ManagementServer.class, Boolean.class, "open.vswitch.tunnel.network", "false", "enable/disable open vswitch tunnel network(no vlan)", null), VmNetworkThrottlingRate("Network", ManagementServer.class, Integer.class, "vm.network.throttling.rate", "200", "Default data transfer rate in megabits per second allowed in User vm's default network.", null), + RedundantRouter("Network", ManagementServer.class, Boolean.class, "network.redundantrouter", "false", "enable/disable redundant virtual router", null), SecurityGroupWorkCleanupInterval("Network", ManagementServer.class, Integer.class, "network.securitygroups.work.cleanup.interval", "120", "Time interval (seconds) in which finished work is cleaned up from the work table", null), SecurityGroupWorkerThreads("Network", ManagementServer.class, Integer.class, "network.securitygroups.workers.pool.size", "50", "Number of worker threads processing the security group update work queue", null), SecurityGroupWorkGlobalLockTimeout("Network", ManagementServer.class, Integer.class, "network.securitygroups.work.lock.timeout", "300", "Lock wait timeout (seconds) while updating the security group work queue", null), - // Usage + + //VPN + RemoteAccessVpnPskLength("Network", AgentManager.class, Integer.class, "remote.access.vpn.psk.length", "24", "The length of the ipsec preshared key (minimum 8, maximum 256)", null), + RemoteAccessVpnClientIpRange("Network", AgentManager.class, String.class, "remote.access.vpn.client.iprange", "10.1.2.1-10.1.2.8", "The range of ips to be allocated to remote access vpn clients. The first ip in the range is used by the VPN server", null), + RemoteAccessVpnUserLimit("Network", AgentManager.class, String.class, "remote.access.vpn.user.limit", "8", "The maximum number of VPN users that can be created per account", null), + + // Usage + CapacityCheckPeriod("Usage", ManagementServer.class, Integer.class, "capacity.check.period", "300000", "The interval in milliseconds between capacity checks", null), StorageAllocatedCapacityThreshold("Usage", ManagementServer.class, Float.class, "storage.allocated.capacity.threshold", "0.85", "Percentage (as a value between 0 and 1) of allocated storage utilization above which alerts will be sent about low storage available.", null), StorageCapacityThreshold("Usage", ManagementServer.class, Float.class, "storage.capacity.threshold", "0.85", "Percentage (as a value between 0 and 1) of storage utilization above which alerts will be sent about low storage available.", null), diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index d25cf86f4ba..4375aa80331 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -2849,7 +2849,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura if (trafficType == TrafficType.Guest) { firewallService = true; lbService = true; - vpnService = false; + vpnService = true; gatewayService = true; } diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java index 748ab52769d..33149c93600 100755 --- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java +++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java @@ -79,6 +79,8 @@ import com.cloud.network.dao.LoadBalancerVMMapDaoImpl; import com.cloud.network.dao.NetworkDaoImpl; import com.cloud.network.dao.NetworkDomainDaoImpl; import com.cloud.network.dao.NetworkRuleConfigDaoImpl; +import com.cloud.network.dao.RemoteAccessVpnDaoImpl; +import com.cloud.network.dao.VpnUserDaoImpl; import com.cloud.network.lb.LoadBalancingRulesManagerImpl; import com.cloud.network.ovs.OvsNetworkManagerImpl; import com.cloud.network.ovs.OvsTunnelManagerImpl; @@ -99,6 +101,7 @@ import com.cloud.network.security.dao.SecurityGroupRulesDaoImpl; import com.cloud.network.security.dao.SecurityGroupVMMapDaoImpl; import com.cloud.network.security.dao.SecurityGroupWorkDaoImpl; import com.cloud.network.security.dao.VmRulesetLogDaoImpl; +import com.cloud.network.vpn.RemoteAccessVpnManagerImpl; import com.cloud.offerings.dao.NetworkOfferingDaoImpl; import com.cloud.resource.ResourceManagerImpl; import com.cloud.service.dao.ServiceOfferingDaoImpl; @@ -238,6 +241,8 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com addDao("NicDao", NicDaoImpl.class); addDao("InstanceGroupDao", InstanceGroupDaoImpl.class); addDao("InstanceGroupVMMapDao", InstanceGroupVMMapDaoImpl.class); + addDao("RemoteAccessVpnDao", RemoteAccessVpnDaoImpl.class); + addDao("VpnUserDao", VpnUserDaoImpl.class); addDao("ItWorkDao", ItWorkDaoImpl.class); addDao("FirewallRulesDao", FirewallRulesDaoImpl.class); addDao("PortForwardingRulesDao", PortForwardingRulesDaoImpl.class); @@ -295,6 +300,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com addManager("EntityManager", EntityManagerImpl.class); addManager("LoadBalancingRulesManager", LoadBalancingRulesManagerImpl.class); addManager("RulesManager", RulesManagerImpl.class); + addManager("RemoteAccessVpnManager", RemoteAccessVpnManagerImpl.class); addManager("OvsNetworkManager", OvsNetworkManagerImpl.class); addManager("OvsTunnelManager", OvsTunnelManagerImpl.class); addManager("Capacity Manager", CapacityManagerImpl.class); diff --git a/server/src/com/cloud/network/NetworkManager.java b/server/src/com/cloud/network/NetworkManager.java index 4edf669b3db..c46f2b6728e 100644 --- a/server/src/com/cloud/network/NetworkManager.java +++ b/server/src/com/cloud/network/NetworkManager.java @@ -36,6 +36,7 @@ import com.cloud.network.addr.PublicIp; import com.cloud.network.guru.NetworkGuru; import com.cloud.network.rules.FirewallRule; import com.cloud.network.vpn.PasswordResetElement; +import com.cloud.network.vpn.RemoteAccessVpnElement; import com.cloud.offerings.NetworkOfferingVO; import com.cloud.user.Account; import com.cloud.user.AccountVO; @@ -140,6 +141,8 @@ public interface NetworkManager extends NetworkService { boolean applyRules(List rules, boolean continueOnError) throws ResourceUnavailableException; + List getRemoteAccessVpnElements(); + PublicIpAddress getPublicIpAddress(long ipAddressId); List listPodVlans(long podId); diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index ab9d4de9ba0..b6ff9dcd067 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -102,6 +102,8 @@ import com.cloud.network.lb.LoadBalancingRulesManager; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.RulesManager; import com.cloud.network.vpn.PasswordResetElement; +import com.cloud.network.vpn.RemoteAccessVpnElement; +import com.cloud.network.vpn.RemoteAccessVpnService; import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering.Availability; import com.cloud.offerings.NetworkOfferingVO; @@ -200,6 +202,8 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag @Inject UsageEventDao _usageEventDao; @Inject + RemoteAccessVpnService _vpnMgr; + @Inject PodVlanMapDao _podVlanMapDao; @Inject(adapter = NetworkGuru.class) Adapters _networkGurus; @@ -1441,6 +1445,18 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag return _networksDao.findById(id); } + @Override + public List getRemoteAccessVpnElements() { + List elements = new ArrayList(); + for (NetworkElement element : _networkElements) { + if (element instanceof RemoteAccessVpnElement) { + elements.add((RemoteAccessVpnElement) element); + } + } + + return elements; + } + @Override public void cleanupNics(VirtualMachineProfile vm) { List nics = _nicDao.listByVmId(vm.getId()); @@ -2391,6 +2407,18 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag success = false; } + // apply vpn rules + List vpnsToReapply = _vpnMgr.listRemoteAccessVpns(networkId); + if (vpnsToReapply != null) { + for (RemoteAccessVpn vpn : vpnsToReapply) { + // Start remote access vpn per ip + if (_vpnMgr.startRemoteAccessVpn(vpn.getServerAddressId()) == null) { + s_logger.warn("Failed to reapply vpn rules as a part of network id=" + networkId + " restart"); + success = false; + } + } + } + return success; } @@ -2745,6 +2773,18 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag success = false; } + // remote access vpn can be enabled only for static nat ip, so this part should never be executed under normal + // conditions + // only when ip address failed to be cleaned up as a part of account destroy and was marked as Releasing, this part of + // the code would be triggered + s_logger.debug("Cleaning up remote access vpns as a part of public IP id=" + ipId + " release..."); + try { + _vpnMgr.destroyRemoteAccessVpn(ipId); + } catch (ResourceUnavailableException e) { + s_logger.warn("Unable to destroy remote access vpn for ip id=" + ipId + " as a part of ip release", e); + success = false; + } + return success; } diff --git a/server/src/com/cloud/network/RemoteAccessVpnVO.java b/server/src/com/cloud/network/RemoteAccessVpnVO.java new file mode 100644 index 00000000000..0e6d48b5562 --- /dev/null +++ b/server/src/com/cloud/network/RemoteAccessVpnVO.java @@ -0,0 +1,118 @@ +/** + * 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.Id; +import javax.persistence.Table; + +@Entity +@Table(name=("remote_access_vpn")) +public class RemoteAccessVpnVO implements RemoteAccessVpn { + @Column(name="account_id") + private long accountId; + + @Column(name="network_id") + private long networkId; + + @Column(name="domain_id") + private long domainId; + + @Id + @Column(name="vpn_server_addr_id") + private long serverAddressId; + + @Column(name="local_ip") + private String localIp; + + @Column(name="ip_range") + private String ipRange; + + @Column(name="ipsec_psk") + private String ipsecPresharedKey; + + @Column(name="state") + private State state; + + public RemoteAccessVpnVO() { } + + public RemoteAccessVpnVO(long accountId, long domainId, long networkId, long publicIpId, String localIp, String ipRange, String presharedKey) { + this.accountId = accountId; + this.serverAddressId = publicIpId; + this.ipRange = ipRange; + this.ipsecPresharedKey = presharedKey; + this.localIp = localIp; + this.domainId = domainId; + this.networkId = networkId; + this.state = State.Added; + } + + @Override + public State getState() { + return state; + } + + public void setState(State state) { + this.state = state; + } + + @Override + public long getAccountId() { + return accountId; + } + + @Override + public long getServerAddressId() { + return serverAddressId; + } + + @Override + public String getIpRange() { + return ipRange; + } + + public void setIpRange(String ipRange) { + this.ipRange = ipRange; + } + + @Override + public String getIpsecPresharedKey() { + return ipsecPresharedKey; + } + + public void setIpsecPresharedKey(String ipsecPresharedKey) { + this.ipsecPresharedKey = ipsecPresharedKey; + } + + @Override + public String getLocalIp() { + return localIp; + } + + @Override + public long getDomainId() { + return domainId; + } + + @Override + public long getNetworkId() { + return networkId; + } +} diff --git a/server/src/com/cloud/network/dao/RemoteAccessVpnDao.java b/server/src/com/cloud/network/dao/RemoteAccessVpnDao.java new file mode 100644 index 00000000000..9e324e2bc64 --- /dev/null +++ b/server/src/com/cloud/network/dao/RemoteAccessVpnDao.java @@ -0,0 +1,33 @@ +/** + * 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 java.util.List; + +import com.cloud.network.RemoteAccessVpn; +import com.cloud.network.RemoteAccessVpnVO; +import com.cloud.utils.db.GenericDao; + +public interface RemoteAccessVpnDao extends GenericDao { + RemoteAccessVpnVO findByPublicIpAddress(long ipAddressId); + RemoteAccessVpnVO findByPublicIpAddressAndState(long ipAddressId, RemoteAccessVpn.State state); + RemoteAccessVpnVO findByAccountAndNetwork(Long accountId, Long zoneId); + List findByAccount(Long accountId); + List listByNetworkId(Long networkId); +} diff --git a/server/src/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java b/server/src/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java new file mode 100644 index 00000000000..92de55b5cc0 --- /dev/null +++ b/server/src/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java @@ -0,0 +1,85 @@ +/** + * 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 java.util.List; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.network.RemoteAccessVpn; +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 AllFieldsSearch; + + + protected RemoteAccessVpnDaoImpl() { + AllFieldsSearch = createSearchBuilder(); + AllFieldsSearch.and("accountId", AllFieldsSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("networkId", AllFieldsSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("ipAddress", AllFieldsSearch.entity().getServerAddressId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), SearchCriteria.Op.EQ); + AllFieldsSearch.done(); + } + + @Override + public RemoteAccessVpnVO findByPublicIpAddress(long ipAddressId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("ipAddress", ipAddressId); + return findOneBy(sc); + } + + @Override + public RemoteAccessVpnVO findByAccountAndNetwork(Long accountId, Long networkId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("accountId", accountId); + sc.setParameters("networkId", networkId); + return findOneBy(sc); + } + + @Override + public List findByAccount(Long accountId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("accountId", accountId); + return listBy(sc); + } + + @Override + public RemoteAccessVpnVO findByPublicIpAddressAndState(long ipAddressId, RemoteAccessVpn.State state) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("ipAddress", ipAddressId); + sc.setParameters("state", state); + return findOneBy(sc); + } + + @Override + public List listByNetworkId(Long networkId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("networkId", networkId); + return listBy(sc); + } +} diff --git a/server/src/com/cloud/network/dao/VpnUserDao.java b/server/src/com/cloud/network/dao/VpnUserDao.java new file mode 100644 index 00000000000..0f94ba5b1b0 --- /dev/null +++ b/server/src/com/cloud/network/dao/VpnUserDao.java @@ -0,0 +1,30 @@ +/** + * 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 java.util.List; + +import com.cloud.network.VpnUserVO; +import com.cloud.utils.db.GenericDao; + +public interface VpnUserDao extends GenericDao { + List listByAccount(Long accountId); + VpnUserVO findByAccountAndUsername(Long acccountId, String userName); + long getVpnUserCount(Long accountId); +} diff --git a/server/src/com/cloud/network/dao/VpnUserDaoImpl.java b/server/src/com/cloud/network/dao/VpnUserDaoImpl.java new file mode 100644 index 00000000000..6fb19f2a9fe --- /dev/null +++ b/server/src/com/cloud/network/dao/VpnUserDaoImpl.java @@ -0,0 +1,86 @@ +/** + * 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 java.util.List; + +import javax.ejb.Local; + +import com.cloud.network.VpnUser.State; +import com.cloud.network.VpnUserVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Func; + +@Local(value={VpnUserDao.class}) +public class VpnUserDaoImpl extends GenericDaoBase implements VpnUserDao { + private final SearchBuilder AccountSearch; + private final SearchBuilder AccountNameSearch; + private final GenericSearchBuilder VpnUserCount; + + + protected VpnUserDaoImpl() { + + AccountSearch = createSearchBuilder(); + AccountSearch.and("accountId", AccountSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + AccountSearch.done(); + + AccountNameSearch = createSearchBuilder(); + AccountNameSearch.and("accountId", AccountNameSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + AccountNameSearch.and("username", AccountNameSearch.entity().getUsername(), SearchCriteria.Op.EQ); + AccountNameSearch.done(); + + VpnUserCount = createSearchBuilder(Long.class); + VpnUserCount.and("accountId", VpnUserCount.entity().getAccountId(), SearchCriteria.Op.EQ); + VpnUserCount.and("state", VpnUserCount.entity().getState(), SearchCriteria.Op.NEQ); + VpnUserCount.select(null, Func.COUNT, null); + VpnUserCount.done(); + } + + @Override + public List listByAccount(Long accountId) { + SearchCriteria sc = AccountSearch.create(); + sc.setParameters("accountId", accountId); + return listBy(sc); + } + + @Override + public VpnUserVO findByAccountAndUsername(Long accountId, String userName) { + SearchCriteria sc = AccountNameSearch.create(); + sc.setParameters("accountId", accountId); + sc.setParameters("username", userName); + + return findOneBy(sc); + } + + @Override + public long getVpnUserCount(Long accountId) { + SearchCriteria sc = VpnUserCount.create(); + sc.setParameters("accountId", accountId); + sc.setParameters("state", State.Revoke); + List rs = customSearch(sc, null); + if (rs.size() == 0) { + return 0; + } + + return rs.get(0); + } +} diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java index f4d9927c5a6..3a0cc2c9107 100644 --- a/server/src/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VirtualRouterElement.java @@ -40,6 +40,8 @@ import com.cloud.network.Network.Provider; import com.cloud.network.Network.Service; import com.cloud.network.NetworkManager; import com.cloud.network.PublicIpAddress; +import com.cloud.network.RemoteAccessVpn; +import com.cloud.network.VpnUser; import com.cloud.network.dao.LoadBalancerDao; import com.cloud.network.dao.NetworkDao; import com.cloud.network.lb.LoadBalancingRulesManager; @@ -47,6 +49,7 @@ import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.router.VirtualRouter; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.RulesManager; +import com.cloud.network.vpn.RemoteAccessVpnElement; import com.cloud.offering.NetworkOffering; import com.cloud.offerings.NetworkOfferingVO; import com.cloud.offerings.dao.NetworkOfferingDao; @@ -66,7 +69,7 @@ import com.cloud.vm.dao.UserVmDao; @Local(value=NetworkElement.class) -public class VirtualRouterElement extends DhcpElement implements NetworkElement { +public class VirtualRouterElement extends DhcpElement implements NetworkElement, RemoteAccessVpnElement { private static final Logger s_logger = Logger.getLogger(VirtualRouterElement.class); private static final Map> capabilities = setCapabilities(); @@ -185,6 +188,42 @@ public class VirtualRouterElement extends DhcpElement implements NetworkElement return true; } } + + + @Override + public String[] applyVpnUsers(RemoteAccessVpn vpn, List users) throws ResourceUnavailableException{ + Network network = _networksDao.findById(vpn.getNetworkId()); + DataCenter dc = _configMgr.getZone(network.getDataCenterId()); + if (canHandle(network.getGuestType(),dc)) { + return _routerMgr.applyVpnUsers(network, users); + } else { + s_logger.debug("Element " + this.getName() + " doesn't handle applyVpnUsers command"); + return null; + } + } + + @Override + public boolean startVpn(Network network, RemoteAccessVpn vpn) throws ResourceUnavailableException { + DataCenter dc = _configMgr.getZone(network.getDataCenterId()); + if (canHandle(network.getGuestType(),dc)) { + return _routerMgr.startRemoteAccessVpn(network, vpn); + } else { + s_logger.debug("Element " + this.getName() + " doesn't handle createVpn command"); + return false; + } + } + + @Override + public boolean stopVpn(Network network, RemoteAccessVpn vpn) throws ResourceUnavailableException { + DataCenter dc = _configMgr.getZone(network.getDataCenterId()); + if (canHandle(network.getGuestType(),dc)) { + return _routerMgr.deleteRemoteAccessVpn(network, vpn); + } else { + s_logger.debug("Element " + this.getName() + " doesn't handle removeVpn command"); + return false; + } + } + @Override public boolean applyIps(Network network, List ipAddress) throws ResourceUnavailableException { @@ -233,8 +272,11 @@ public class VirtualRouterElement extends DhcpElement implements NetworkElement firewallCapabilities.put(Capability.SupportedSourceNatTypes, "per account"); capabilities.put(Service.Firewall, firewallCapabilities); - - capabilities.put(Service.Vpn, null); + + //Set capabilities for vpn + Map vpnCapabilities = new HashMap(); + vpnCapabilities.put(Capability.SupportedVpnTypes, "pptp,l2tp,ipsec"); + capabilities.put(Service.Vpn, vpnCapabilities); Map dnsCapabilities = new HashMap(); dnsCapabilities.put(Capability.AllowDnsSuffixModification, "true"); diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java index 879afb4b832..c4d41d70cec 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java @@ -26,7 +26,9 @@ import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.Network; import com.cloud.network.PublicIpAddress; +import com.cloud.network.RemoteAccessVpn; import com.cloud.network.VirtualNetworkApplianceService; +import com.cloud.network.VpnUser; import com.cloud.network.rules.FirewallRule; import com.cloud.user.Account; import com.cloud.user.User; @@ -70,13 +72,20 @@ public interface VirtualNetworkApplianceManager extends Manager, VirtualNetworkA List deployDhcp(Network guestNetwork, DeployDestination dest, Account owner, Map params) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException; + List addVirtualMachineIntoNetwork(Network config, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context, List routers) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException; + + boolean startRemoteAccessVpn(Network network, RemoteAccessVpn vpn) throws ResourceUnavailableException; + + boolean deleteRemoteAccessVpn(Network network, RemoteAccessVpn vpn) throws ResourceUnavailableException; boolean associateIP (Network network, List ipAddress) throws ResourceUnavailableException; boolean applyFirewallRules(Network network, List rules) throws ResourceUnavailableException; List getRoutersForNetwork(long networkId); + + String[] applyVpnUsers(Network network, List users) throws ResourceUnavailableException; VirtualRouter stop(VirtualRouter router, boolean forced, User callingUser, Account callingAccount) throws ConcurrentOperationException, ResourceUnavailableException; } diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index c984937ba73..d9d8245d01f 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -48,10 +48,12 @@ import com.cloud.agent.api.routing.DhcpEntryCommand; import com.cloud.agent.api.routing.IpAssocCommand; import com.cloud.agent.api.routing.LoadBalancerConfigCommand; import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; import com.cloud.agent.api.routing.SavePasswordCommand; import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; import com.cloud.agent.api.routing.SetStaticNatRulesCommand; import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.agent.api.routing.VpnUsersCfgCommand; import com.cloud.agent.api.to.IpAddressTO; import com.cloud.agent.api.to.LoadBalancerTO; import com.cloud.agent.api.to.PortForwardingRuleTO; @@ -106,8 +108,11 @@ import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.IsolationType; import com.cloud.network.Networks.TrafficType; import com.cloud.network.PublicIpAddress; +import com.cloud.network.RemoteAccessVpn; import com.cloud.network.SshKeysDistriMonitor; import com.cloud.network.VirtualNetworkApplianceService; +import com.cloud.network.VpnUser; +import com.cloud.network.VpnUserVO; import com.cloud.network.addr.PublicIp; import com.cloud.network.dao.FirewallRulesCidrsDao; import com.cloud.network.dao.FirewallRulesDao; @@ -116,6 +121,8 @@ import com.cloud.network.dao.LoadBalancerDao; import com.cloud.network.dao.LoadBalancerVMMapDao; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkRuleConfigDao; +import com.cloud.network.dao.RemoteAccessVpnDao; +import com.cloud.network.dao.VpnUserDao; import com.cloud.network.lb.LoadBalancingRule; import com.cloud.network.lb.LoadBalancingRule.LbDestination; import com.cloud.network.lb.LoadBalancingRulesManager; @@ -265,6 +272,10 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian @Inject VirtualMachineManager _itMgr; @Inject + VpnUserDao _vpnUsersDao; + @Inject + RemoteAccessVpnDao _remoteAccessVpnDao; + @Inject RulesManager _rulesMgr; @Inject NetworkDao _networkDao; @@ -273,6 +284,8 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian @Inject PortForwardingRulesDao _pfRulesDao; @Inject + RemoteAccessVpnDao _vpnDao; + @Inject VMInstanceDao _instanceDao; @Inject NicDao _nicDao; @@ -1334,12 +1347,18 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian // Re-apply public ip addresses - should come before PF/LB/VPN createAssociateIPCommands(router, publicIps, cmds, 0); + List vpns = new ArrayList(); List pfRules = new ArrayList(); List staticNatFirewallRules = new ArrayList(); for (PublicIpAddress ip : publicIps) { pfRules.addAll(_pfRulesDao.listForApplication(ip.getId())); staticNatFirewallRules.addAll(_rulesDao.listByIpAndPurpose(ip.getId(), Purpose.StaticNat)); + + RemoteAccessVpn vpn = _vpnDao.findById(ip.getId()); + if (vpn != null) { + vpns.add(vpn); + } } // Re-apply port forwarding rules @@ -1358,6 +1377,14 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian createApplyStaticNatRulesCommands(staticNatRules, router, cmds); } + // Re-apply vpn rules + s_logger.debug("Found " + vpns.size() + " vpn(s) to apply as a part of domR " + router + " start."); + if (!vpns.isEmpty()) { + for (RemoteAccessVpn vpn : vpns) { + createApplyVpnCommands(vpn, router, cmds); + } + } + // Re-apply load balancing rules List lbs = _loadBalancerDao.listByNetworkId(networkId); List lbRules = new ArrayList(); @@ -1411,6 +1438,87 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian public void finalizeExpunge(DomainRouterVO vm) { } + + @Override + public boolean startRemoteAccessVpn(Network network, RemoteAccessVpn vpn) throws ResourceUnavailableException { + + List routers = _routerDao.findByNetwork(network.getId()); + if (routers == null || routers.isEmpty()) { + s_logger.warn("Failed to start remote access VPN: no router found for account and zone"); + throw new ResourceUnavailableException("Failed to start remote access VPN: no router found for account and zone", DataCenter.class, network.getDataCenterId()); + } + + for (DomainRouterVO router : routers) { + if (router.getState() != State.Running) { + s_logger.warn("Failed to start remote access VPN: router not in right state " + router.getState()); + throw new ResourceUnavailableException("Failed to start remote access VPN: router not in right state " + router.getState(), DataCenter.class, network.getDataCenterId()); + } + + Commands cmds = new Commands(OnError.Stop); + + createApplyVpnCommands(vpn, router, cmds); + + try { + _agentMgr.send(router.getHostId(), cmds); + } catch (OperationTimedoutException e) { + s_logger.debug("Failed to start remote access VPN: ", e); + throw new AgentUnavailableException("Unable to send commands to virtual router ", router.getHostId(), e); + } + Answer answer = cmds.getAnswer("users"); + if (!answer.getResult()) { + s_logger.error("Unable to start vpn: unable add users to vpn in zone " + router.getDataCenterIdToDeployIn() + " for account " + vpn.getAccountId() + " on domR: " + router.getInstanceName() + + " due to " + answer.getDetails()); + throw new ResourceUnavailableException("Unable to start vpn: Unable to add users to vpn in zone " + router.getDataCenterIdToDeployIn() + " for account " + vpn.getAccountId() + " on domR: " + + router.getInstanceName() + " due to " + answer.getDetails(), DataCenter.class, router.getDataCenterIdToDeployIn()); + } + answer = cmds.getAnswer("startVpn"); + if (!answer.getResult()) { + s_logger.error("Unable to start vpn in zone " + router.getDataCenterIdToDeployIn() + " for account " + vpn.getAccountId() + " on domR: " + router.getInstanceName() + " due to " + + answer.getDetails()); + throw new ResourceUnavailableException("Unable to start vpn in zone " + router.getDataCenterIdToDeployIn() + " for account " + vpn.getAccountId() + " on domR: " + router.getInstanceName() + + " due to " + answer.getDetails(), DataCenter.class, router.getDataCenterIdToDeployIn()); + } + + } + return true; + } + + + @Override + public boolean deleteRemoteAccessVpn(Network network, RemoteAccessVpn vpn) throws ResourceUnavailableException { + + List routers = _routerDao.findByNetwork(network.getId()); + if (routers == null || routers.isEmpty()) { + s_logger.warn("Failed to delete remote access VPN: no router found for account and zone"); + throw new ResourceUnavailableException("Failed to delete remote access VPN", DataCenter.class, network.getDataCenterId()); + } + + boolean result = true; + for (DomainRouterVO router : routers) { + if (router.getState() != State.Running) { + s_logger.warn("Failed to delete remote access VPN: domR is not in right state " + router.getState()); + throw new ResourceUnavailableException("Failed to delete remote access VPN: domR is not in right state " + router.getState(), DataCenter.class, network.getDataCenterId()); + } + Commands cmds = new Commands(OnError.Continue); + IpAddress ip = _networkMgr.getIp(vpn.getServerAddressId()); + + RemoteAccessVpnCfgCommand removeVpnCmd = new RemoteAccessVpnCfgCommand(false, ip.getAddress().addr(), vpn.getLocalIp(), vpn.getIpRange(), vpn.getIpsecPresharedKey()); + removeVpnCmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); + removeVpnCmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, router.getGuestIpAddress()); + removeVpnCmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); + + DataCenterVO dcVo = _dcDao.findById(router.getDataCenterIdToDeployIn()); + removeVpnCmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString()); + + cmds.addCommand(removeVpnCmd); + + result = result && sendCommandsToRouter(router, cmds); + } + + return result; + } + + private DomainRouterVO start(DomainRouterVO router, User user, Account caller, Map params, DeploymentPlan planToDeploy) throws StorageUnavailableException, InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { s_logger.debug("Starting router " + router); @@ -1548,6 +1656,58 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian return _routerDao.persist(router); } + @Override + public String[] applyVpnUsers(Network network, List users) throws ResourceUnavailableException { + List routers = _routerDao.findByNetwork(network.getId()); + if (routers == null || routers.isEmpty()) { + s_logger.warn("Failed to add/remove VPN users: no router found for account and zone"); + throw new ResourceUnavailableException("Unable to assign ip addresses, domR doesn't exist for network " + network.getId(), DataCenter.class, network.getDataCenterId()); + } + + String[] result = new String[users.size()]; + for (DomainRouterVO router : routers) { + if (router.getState() != State.Running) { + s_logger.warn("Failed to add/remove VPN users: router not in running state"); + throw new ResourceUnavailableException("Unable to assign ip addresses, domR is not in right state " + router.getState(), DataCenter.class, network.getDataCenterId()); + } + + Commands cmds = new Commands(OnError.Continue); + List addUsers = new ArrayList(); + List removeUsers = new ArrayList(); + for (VpnUser user : users) { + if (user.getState() == VpnUser.State.Add || user.getState() == VpnUser.State.Active) { + addUsers.add(user); + } else if (user.getState() == VpnUser.State.Revoke) { + removeUsers.add(user); + } + } + + VpnUsersCfgCommand cmd = new VpnUsersCfgCommand(addUsers, removeUsers); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, router.getGuestIpAddress()); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); + DataCenterVO dcVo = _dcDao.findById(router.getPodIdToDeployIn()); + cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString()); + + cmds.addCommand(cmd); + + // Currently we receive just one answer from the agent. In the future we have to parse individual answers and set + // results accordingly + boolean agentResult = sendCommandsToRouter(router, cmds); + + for (int i = 0; i < result.length; i++) { + // the result of the command is true only when it got applied successfully on all domain routers + if (agentResult && result[i] == null) { + result[i] = null; + } else { + result[i] = String.valueOf(agentResult); + } + } + } + + return result; + } + @Override public DomainRouterVO findById(long id) { return _routerDao.findById(id); @@ -1745,6 +1905,36 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } + private void createApplyVpnCommands(RemoteAccessVpn vpn, DomainRouterVO router, Commands cmds) { + List vpnUsers = _vpnUsersDao.listByAccount(vpn.getAccountId()); + List addUsers = new ArrayList(); + List removeUsers = new ArrayList(); + for (VpnUser user : vpnUsers) { + if (user.getState() == VpnUser.State.Add) { + addUsers.add(user); + } else if (user.getState() == VpnUser.State.Revoke) { + removeUsers.add(user); + } + } + + VpnUsersCfgCommand addUsersCmd = new VpnUsersCfgCommand(addUsers, removeUsers); + addUsersCmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); + addUsersCmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, router.getGuestIpAddress()); + addUsersCmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); + + IpAddress ip = _networkMgr.getIp(vpn.getServerAddressId()); + + RemoteAccessVpnCfgCommand startVpnCmd = new RemoteAccessVpnCfgCommand(true, ip.getAddress().addr(), vpn.getLocalIp(), vpn.getIpRange(), vpn.getIpsecPresharedKey()); + startVpnCmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); + startVpnCmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, router.getGuestIpAddress()); + startVpnCmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); + DataCenterVO dcVo = _dcDao.findById(router.getDataCenterIdToDeployIn()); + startVpnCmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString()); + + cmds.addCommand("users", addUsersCmd); + cmds.addCommand("startVpn", startVpnCmd); + } + private void createVmDataCommands(DomainRouterVO router, Commands cmds) { long networkId = router.getNetworkId(); List vms = _userVmDao.listByNetworkIdAndStates(networkId, State.Running, State.Migrating, State.Stopping); diff --git a/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java b/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java new file mode 100755 index 00000000000..c0c5add1ca3 --- /dev/null +++ b/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java @@ -0,0 +1,589 @@ +/** + * 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.vpn; + +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.api.commands.ListRemoteAccessVpnsCmd; +import com.cloud.api.commands.ListVpnUsersCmd; +import com.cloud.configuration.Config; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.domain.Domain; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.exception.AccountLimitException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.Network.Service; +import com.cloud.network.NetworkManager; +import com.cloud.network.PublicIpAddress; +import com.cloud.network.RemoteAccessVpn; +import com.cloud.network.RemoteAccessVpnVO; +import com.cloud.network.VpnUser; +import com.cloud.network.VpnUser.State; +import com.cloud.network.VpnUserVO; +import com.cloud.network.dao.FirewallRulesDao; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.RemoteAccessVpnDao; +import com.cloud.network.dao.VpnUserDao; +import com.cloud.network.router.VirtualNetworkApplianceManager; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRule.Purpose; +import com.cloud.network.rules.RulesManager; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.UserContext; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.Pair; +import com.cloud.utils.PasswordGenerator; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.component.Inject; +import com.cloud.utils.component.Manager; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.net.NetUtils; + +@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 FirewallRulesDao _rulesDao; + + int _userLimit; + int _pskLength; + String _clientIpRange; + SearchBuilder VpnSearch; + + @Override + public RemoteAccessVpn createRemoteAccessVpn(long publicIpId, String ipRange) throws NetworkRuleConflictException { + UserContext ctx = UserContext.current(); + Account caller = ctx.getCaller(); + + // make sure ip address exists + PublicIpAddress ipAddr = _networkMgr.getPublicIpAddress(publicIpId); + if (ipAddr == null) { + throw new InvalidParameterValueException("Unable to create remote access vpn, invalid public IP address id" + publicIpId); + } + + _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(publicIpId); + + if (vpnVO != null) { + //if vpn is in Added state, return it to the api + if (vpnVO.getState() == RemoteAccessVpn.State.Added) { + return vpnVO; + } + 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.findByAccountAndNetwork(ipAddr.getAllocatedToAccountId(), ipAddr.getAssociatedWithNetworkId()); + if (vpnVO != null) { + //if vpn is in Added state, return it to the api + if (vpnVO.getState() == RemoteAccessVpn.State.Added) { + return vpnVO; + } + throw new InvalidParameterValueException("A Remote Access VPN already exists for this account"); + } + + //Verify that vpn service is enabled for the network + Network network = _networkMgr.getNetwork(ipAddr.getAssociatedWithNetworkId()); + if (!_networkMgr.isServiceSupported(network.getNetworkOfferingId(), Service.Vpn)) { + throw new InvalidParameterValueException("Vpn service is not supported in network id=" + ipAddr.getAssociatedWithNetworkId()); + } + + if (ipRange == null) { + ipRange = _clientIpRange; + } + 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 in range specification " + ipRange); + } + if (!NetUtils.validIpRange(range[0], range[1])) { + throw new InvalidParameterValueException("Invalid ip range " + ipRange); + } + + 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. + 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] + "-" + + guestIpRange[1]); + } + // TODO: check sufficient range + // TODO: check overlap with private and public ip ranges in datacenter + + long startIp = NetUtils.ip2Long(range[0]); + 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(), + publicIpId, range[0], newIpRange, sharedSecret); + return _remoteAccessVpnDao.persist(vpnVO); + } + + private void validateRemoteAccessVpnConfiguration() throws ConfigurationException { + String ipRange = _clientIpRange; + if (ipRange == null) { + s_logger.warn("Remote Access VPN configuration missing client ip range -- ignoring"); + return; + } + Integer pskLength = _pskLength; + if (pskLength != null && (pskLength < 8 || pskLength > 256)) { + throw new ConfigurationException("Remote Access VPN: IPSec preshared key length should be between 8 and 256"); + } else if (pskLength == null) { + s_logger.warn("Remote Access VPN configuration missing Preshared Key Length -- ignoring"); + return; + } + + 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])) { + throw new ConfigurationException("Remote Access VPN: Invalid ip in range specification " + ipRange); + } + if (!NetUtils.validIpRange(range[0], range[1])) { + throw new ConfigurationException("Remote Access VPN: Invalid ip range " + ipRange); + } + } + + @Override @DB + public void destroyRemoteAccessVpn(long ipId) throws ResourceUnavailableException { + Account caller = UserContext.current().getCaller(); + + RemoteAccessVpnVO vpn = _remoteAccessVpnDao.findById(ipId); + if (vpn == null) { + s_logger.debug("vpn id=" + ipId + " does not exists "); + return; + } + + _accountMgr.checkAccess(caller, vpn); + + Network network = _networkMgr.getNetwork(vpn.getNetworkId()); + + vpn.setState(RemoteAccessVpn.State.Removed); + _remoteAccessVpnDao.update(vpn.getServerAddressId(), vpn); + + + List elements = _networkMgr.getRemoteAccessVpnElements(); + boolean success = false; + try { + for (RemoteAccessVpnElement element : elements) { + if (element.stopVpn(network, vpn)) { + success = true; + break; + } + } + } finally { + if (success) { + Transaction txn = Transaction.currentTxn(); + try { + txn.start(); + _remoteAccessVpnDao.remove(ipId); + + //Cleanup corresponding ports + List ports = _rulesDao.listByIpAndPurpose(ipId, Purpose.Vpn); + if (ports != null) { + for (FirewallRule port : ports) { + _rulesDao.remove(port.getId()); + s_logger.debug("Successfully removed firewall rule with ip id=" + port.getSourceIpAddressId() + " and port " + port.getSourcePortStart() + " as a part of vpn cleanup"); + } + } + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + s_logger.warn("Unable to release the three vpn ports from the firewall rules", ex); + } + } + } + } + + @Override + @DB + public VpnUser addVpnUser(long vpnOwnerId, String username, String password) { + 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 '@.-_'"); + } + 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 '@#+=.-_'"); + } + Transaction txn = Transaction.currentTxn(); + txn.start(); + Account owner = _accountDao.lockRow(vpnOwnerId, true); + if (owner == null) { + 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, owner.getDomainId(), username, password)); + txn.commit(); + return user; + } + + @Override + public boolean removeVpnUser(long vpnOwnerId, String username) { + Account caller = UserContext.current().getCaller(); + + VpnUserVO user = _vpnUsersDao.findByAccountAndUsername(vpnOwnerId, username); + if (user == null) { + throw new InvalidParameterValueException("Could not find vpn user " + username); + } + _accountMgr.checkAccess(caller, user); + + user.setState(State.Revoke); + _vpnUsersDao.update(user.getId(), user); + return true; + } + + @Override + public List listVpnUsers(long vpnOwnerId, String userName) { + Account caller = UserContext.current().getCaller(); + Account owner = _accountDao.findById(vpnOwnerId); + _accountMgr.checkAccess(caller, owner); + return _vpnUsersDao.listByAccount(vpnOwnerId); + } + + @Override + public RemoteAccessVpnVO startRemoteAccessVpn(long vpnId) throws ResourceUnavailableException { + 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); + + Network network = _networkMgr.getNetwork(vpn.getNetworkId()); + + List elements = _networkMgr.getRemoteAccessVpnElements(); + boolean started = false; + try { + for (RemoteAccessVpnElement element : elements) { + if (element.startVpn(network, vpn)) { + started = true; + break; + } + } + return vpn; + } finally { + if (started) { + vpn.setState(RemoteAccessVpn.State.Running); + _remoteAccessVpnDao.update(vpn.getServerAddressId(), vpn); + } + } + } + + @DB + @Override + public boolean applyVpnUsers(long vpnOwnerId) { + Account caller = UserContext.current().getCaller(); + Account owner = _accountDao.findById(vpnOwnerId); + _accountMgr.checkAccess(caller, owner); + + s_logger.debug("Applying vpn users for " + owner); + List vpns = _remoteAccessVpnDao.findByAccount(vpnOwnerId); + + List users = _vpnUsersDao.listByAccount(vpnOwnerId); + + //If user is in Active state, we still have to resend them therefore their status has to be Add + for (VpnUserVO user : users) { + if (user.getState() == State.Active) { + user.setState(State.Add); + _vpnUsersDao.update(user.getId(), user); + } + } + + 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) { + try { + String[] results = element.applyVpnUsers(vpn, users); + if (results != null) { + 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; + } + } + } + } 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; + } + } + } + } + + for (int i = 0; i < finals.length; i++) { + VpnUserVO user = users.get(i); + if (finals[i]) { + if (user.getState() == State.Add) { + user.setState(State.Active); + _vpnUsersDao.update(user.getId(), user); + } else if (user.getState() == State.Revoke) { + _vpnUsersDao.remove(user.getId()); + } + } else { + s_logger.warn("Failed to apply vpn for user " + user.getUsername() + ", accountId=" + user.getAccountId()); + } + } + + return success; + } + + @Override + public List searchForVpnUsers(ListVpnUsersCmd cmd) { + Account caller = UserContext.current().getCaller(); + String username = cmd.getUsername(); + String path = null; + + //Verify account information + Pair accountDomainPair = _accountMgr.finalizeAccountDomainForList(caller, cmd.getAccountName(), cmd.getDomainId()); + String accountName = accountDomainPair.first(); + Long domainId = accountDomainPair.second(); + + + if (caller.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN || caller.getType() == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) { + Domain domain = _accountMgr.getDomain(caller.getDomainId()); + path = domain.getPath(); + } + + 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); + sb.and("username", sb.entity().getUsername(), SearchCriteria.Op.EQ); + sb.and("accountId", sb.entity().getAccountId(), SearchCriteria.Op.EQ); + sb.and("domainId", sb.entity().getDomainId(), SearchCriteria.Op.EQ); + sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ); + + if (path != null) { + //for domain admin we should show only subdomains information + 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(); + + //list only active users + sc.setParameters("state", State.Active); + + if (id != null) { + sc.setParameters("id", id); + } + + if (username != null) { + sc.setParameters("username", username); + } + + if (domainId != null) { + sc.setParameters("domainId", domainId); + if (accountName != null) { + Account account = _accountMgr.getActiveAccount(accountName, domainId); + sc.setParameters("accountId", account.getId()); + } + } + + if (path != null) { + sc.setJoinParameters("domainSearch", "path", path + "%"); + } + + return _vpnUsersDao.search(sc, searchFilter); + } + + @Override + public List searchForRemoteAccessVpns(ListRemoteAccessVpnsCmd cmd) { + // do some parameter validation + Account caller = UserContext.current().getCaller(); + String path = null; + + Pair accountDomainPair = _accountMgr.finalizeAccountDomainForList(caller, cmd.getAccountName(), cmd.getDomainId()); + String accountName = accountDomainPair.first(); + Long domainId = accountDomainPair.second(); + + if (caller.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN || caller.getType() == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) { + Domain domain = _accountMgr.getDomain(caller.getDomainId()); + path = domain.getPath(); + } + + Long ipAddressId = cmd.getPublicIpId(); + if (ipAddressId != null) { + PublicIpAddress publicIp = _networkMgr.getPublicIpAddress(ipAddressId); + if (publicIp == null) { + throw new InvalidParameterValueException("Unable to list remote access vpns, IP address " + ipAddressId + " not found."); + } else { + Long ipAddrAcctId = publicIp.getAllocatedToAccountId(); + if (ipAddrAcctId == null) { + throw new InvalidParameterValueException("Unable to list remote access vpns, IP address " + ipAddressId + + " is not associated with an account."); + } + } + _accountMgr.checkAccess(caller, publicIp); + } + + + Filter filter = new Filter(RemoteAccessVpnVO.class, "serverAddressId", false, cmd.getStartIndex(), cmd.getPageSizeVal()); + SearchBuilder sb = _remoteAccessVpnDao.createSearchBuilder(); + sb.and("serverAddressId", sb.entity().getServerAddressId(), Op.EQ); + sb.and("accountId", sb.entity().getAccountId(), Op.EQ); + sb.and("domainId", sb.entity().getDomainId(), Op.EQ); + sb.and("state", sb.entity().getState(), Op.EQ); + + if (path != null) { + //for domain admin we should show only subdomains information + 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(); + + sc.setParameters("state", RemoteAccessVpn.State.Running); + + if (ipAddressId != null) { + sc.setParameters("serverAddressId", ipAddressId); + } + + if (domainId != null) { + sc.setParameters("domainId", domainId); + if (accountName != null) { + Account account = _accountMgr.getActiveAccount(accountName, domainId); + sc.setParameters("accountId", account.getId()); + } + } + + if (path != null) { + sc.setJoinParameters("domainSearch", "path", path + "%"); + } + + return _remoteAccessVpnDao.search(sc, filter); + } + + @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(); + VpnSearch.and("accountId", VpnSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + SearchBuilder domainSearch = _domainDao.createSearchBuilder(); + domainSearch.and("path", domainSearch.entity().getPath(), SearchCriteria.Op.LIKE); + VpnSearch.join("domainSearch", domainSearch, VpnSearch.entity().getDomainId(), domainSearch.entity().getId(), JoinBuilder.JoinType.INNER); + VpnSearch.done(); + + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public List listRemoteAccessVpns(long networkId) { + return _remoteAccessVpnDao.listByNetworkId(networkId); + } + + @Override + public RemoteAccessVpn getRemoteAccessVpn(long vpnId) { + return _remoteAccessVpnDao.findById(vpnId); + } + +} diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index fe3a55696a7..7c6f0e5dd2e 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -54,9 +54,10 @@ import com.cloud.api.commands.UpdateResourceLimitCmd; import com.cloud.api.commands.UpdateUserCmd; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; -import com.cloud.configuration.ResourceCount.ResourceType; +import com.cloud.configuration.ResourceCount; import com.cloud.configuration.ResourceCountVO; import com.cloud.configuration.ResourceLimitVO; +import com.cloud.configuration.ResourceCount.ResourceType; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.configuration.dao.ResourceCountDao; import com.cloud.configuration.dao.ResourceLimitDao; @@ -71,16 +72,22 @@ import com.cloud.event.UsageEventVO; import com.cloud.event.dao.UsageEventDao; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.NetworkManager; import com.cloud.network.NetworkVO; +import com.cloud.network.RemoteAccessVpnVO; +import com.cloud.network.VpnUserVO; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.RemoteAccessVpnDao; +import com.cloud.network.dao.VpnUserDao; import com.cloud.network.security.SecurityGroupManager; import com.cloud.network.security.dao.SecurityGroupDao; +import com.cloud.network.vpn.RemoteAccessVpnService; import com.cloud.server.Criteria; import com.cloud.storage.StorageManager; import com.cloud.storage.VMTemplateVO; @@ -115,6 +122,7 @@ import com.cloud.vm.ReservationContextImpl; import com.cloud.vm.UserVmManager; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.dao.InstanceGroupDao; import com.cloud.vm.dao.UserVmDao; @@ -179,6 +187,12 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag @Inject private UsageEventDao _usageEventDao; @Inject + private RemoteAccessVpnDao _remoteAccessVpnDao; + @Inject + private RemoteAccessVpnService _remoteAccessVpnMgr; + @Inject + private VpnUserDao _vpnUser; + @Inject private DataCenterDao _dcDao; private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("AccountChecker")); @@ -1096,6 +1110,23 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag } } + // delete remote access vpns and associated users + List remoteAccessVpns = _remoteAccessVpnDao.findByAccount(accountId); + List vpnUsers = _vpnUser.listByAccount(accountId); + + for (VpnUserVO vpnUser : vpnUsers) { + _remoteAccessVpnMgr.removeVpnUser(accountId, vpnUser.getUsername()); + } + + try { + for (RemoteAccessVpnVO vpn : remoteAccessVpns) { + _remoteAccessVpnMgr.destroyRemoteAccessVpn(vpn.getServerAddressId()); + } + } catch (ResourceUnavailableException ex) { + s_logger.warn("Failed to cleanup remote access vpn resources as a part of account id=" + accountId + " cleanup due to Exception: ", ex); + accountCleanupNeeded = true; + } + // Cleanup security groups int numRemoved = _securityGroupDao.removeByAccountId(accountId); s_logger.info("deleteAccount: Deleted " + numRemoved + " network groups for account " + accountId);