mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			687 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			Java
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			687 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			Java
		
	
	
		
			Executable File
		
	
	
	
	
| // Licensed to the Apache Software Foundation (ASF) under one
 | |
| // or more contributor license agreements.  See the NOTICE file
 | |
| // distributed with this work for additional information
 | |
| // regarding copyright ownership.  The ASF licenses this file
 | |
| // to you under the Apache License, Version 2.0 (the
 | |
| // "License"); you may not use this file except in compliance
 | |
| // with the License.  You may obtain a copy of the License at
 | |
| //
 | |
| //   http://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing,
 | |
| // software distributed under the License is distributed on an
 | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 | |
| // KIND, either express or implied.  See the License for the
 | |
| // specific language governing permissions and limitations
 | |
| // under the License.
 | |
| package com.cloud.network.vpn;
 | |
| 
 | |
| import java.util.ArrayList;
 | |
| import java.util.Iterator;
 | |
| import java.util.List;
 | |
| import java.util.Map;
 | |
| 
 | |
| import javax.ejb.Local;
 | |
| import javax.inject.Inject;
 | |
| import javax.naming.ConfigurationException;
 | |
| 
 | |
| import org.apache.log4j.Logger;
 | |
| import org.apache.cloudstack.api.command.user.vpn.ListRemoteAccessVpnsCmd;
 | |
| import org.apache.cloudstack.api.command.user.vpn.ListVpnUsersCmd;
 | |
| import org.apache.cloudstack.context.CallContext;
 | |
| import org.apache.cloudstack.framework.config.ConfigKey;
 | |
| import org.apache.cloudstack.framework.config.Configurable;
 | |
| import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 | |
| 
 | |
| import com.cloud.configuration.Config;
 | |
| import com.cloud.domain.DomainVO;
 | |
| import com.cloud.domain.dao.DomainDao;
 | |
| import com.cloud.event.EventTypes;
 | |
| import com.cloud.event.UsageEventUtils;
 | |
| import com.cloud.event.dao.UsageEventDao;
 | |
| 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.NetworkModel;
 | |
| import com.cloud.network.PublicIpAddress;
 | |
| import com.cloud.network.RemoteAccessVpn;
 | |
| 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.IPAddressVO;
 | |
| import com.cloud.network.dao.RemoteAccessVpnDao;
 | |
| import com.cloud.network.dao.RemoteAccessVpnVO;
 | |
| import com.cloud.network.dao.VpnUserDao;
 | |
| import com.cloud.network.element.RemoteAccessVPNServiceProvider;
 | |
| import com.cloud.network.rules.FirewallManager;
 | |
| import com.cloud.network.rules.FirewallRule;
 | |
| import com.cloud.network.rules.FirewallRule.Purpose;
 | |
| import com.cloud.network.rules.FirewallRuleVO;
 | |
| import com.cloud.network.rules.RulesManager;
 | |
| import com.cloud.network.vpc.Vpc;
 | |
| import com.cloud.network.vpc.dao.VpcDao;
 | |
| import com.cloud.projects.Project.ListProjectResourcesCriteria;
 | |
| import com.cloud.server.ConfigurationServer;
 | |
| import com.cloud.user.Account;
 | |
| import com.cloud.user.AccountManager;
 | |
| import com.cloud.user.DomainManager;
 | |
| 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.Ternary;
 | |
| import com.cloud.utils.component.ManagerBase;
 | |
| 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.TransactionCallback;
 | |
| import com.cloud.utils.db.TransactionCallbackNoReturn;
 | |
| import com.cloud.utils.db.SearchCriteria.Op;
 | |
| import com.cloud.utils.db.Transaction;
 | |
| import com.cloud.utils.db.TransactionCallbackWithException;
 | |
| import com.cloud.utils.db.TransactionStatus;
 | |
| import com.cloud.utils.net.NetUtils;
 | |
| 
 | |
| @Local(value = RemoteAccessVpnService.class)
 | |
| public class RemoteAccessVpnManagerImpl extends ManagerBase implements RemoteAccessVpnService, Configurable {
 | |
|     private final static Logger s_logger = Logger.getLogger(RemoteAccessVpnManagerImpl.class);
 | |
| 
 | |
|     static final ConfigKey<String> RemoteAccessVpnClientIpRange = new ConfigKey<String>("Network", String.class, RemoteAccessVpnClientIpRangeCK, "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", false, ConfigKey.Scope.Account);
 | |
| 
 | |
|     @Inject AccountDao _accountDao;
 | |
|     @Inject VpnUserDao _vpnUsersDao;
 | |
|     @Inject RemoteAccessVpnDao _remoteAccessVpnDao;
 | |
|     @Inject IPAddressDao _ipAddressDao;
 | |
|     @Inject AccountManager _accountMgr;
 | |
|     @Inject DomainManager _domainMgr;
 | |
|     @Inject NetworkModel _networkMgr;
 | |
|     @Inject RulesManager _rulesMgr;
 | |
|     @Inject DomainDao _domainDao;
 | |
|     @Inject FirewallRulesDao _rulesDao;
 | |
|     @Inject FirewallManager _firewallMgr;
 | |
|     @Inject UsageEventDao _usageEventDao;
 | |
|     @Inject ConfigurationDao _configDao;
 | |
|     @Inject List<RemoteAccessVPNServiceProvider> _vpnServiceProviders;
 | |
|     @Inject ConfigurationServer _configServer;
 | |
|     @Inject VpcDao _vpcDao;
 | |
| 
 | |
|     int _userLimit;
 | |
|     int _pskLength;
 | |
|     SearchBuilder<RemoteAccessVpnVO> VpnSearch;
 | |
| 
 | |
|     @Override
 | |
|     @DB
 | |
|     public RemoteAccessVpn createRemoteAccessVpn(final long publicIpId, String ipRange, boolean openFirewall)
 | |
|             throws NetworkRuleConflictException {
 | |
|         CallContext ctx = CallContext.current();
 | |
|         final Account caller = ctx.getCallingAccount();
 | |
| 
 | |
|         Long networkId = null;
 | |
| 
 | |
|         // make sure ip address exists
 | |
|         final 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, null, true, ipAddr);
 | |
| 
 | |
|         if (!ipAddr.readyToUse()) {
 | |
|             throw new InvalidParameterValueException("The Ip address is not ready to be used yet: " + ipAddr.getAddress());
 | |
|         }
 | |
| 
 | |
|         IPAddressVO ipAddress = _ipAddressDao.findById(publicIpId);
 | |
| 
 | |
|         networkId = ipAddress.getAssociatedWithNetworkId();
 | |
|         if (networkId != null) {
 | |
|         	_networkMgr.checkIpForService(ipAddress, Service.Vpn, null);
 | |
|         }
 | |
|         
 | |
|         final Long vpcId = ipAddress.getVpcId();
 | |
|         /* IP Address used for VPC must be the source NAT IP of whole VPC */
 | |
|         if (vpcId != null && ipAddress.isSourceNat()) {
 | |
|         	assert networkId == null;
 | |
|         	// No firewall setting for VPC, it would be open internally
 | |
|         	openFirewall = false;
 | |
|         }
 | |
| 
 | |
|         final boolean openFirewallFinal = openFirewall;
 | |
| 
 | |
|         if (networkId == null && vpcId == null) {
 | |
|             throw new InvalidParameterValueException("Unable to create remote access vpn for the ipAddress: " + ipAddr.getAddress().addr() +
 | |
|                     " as ip is not associated with any network or VPC");
 | |
|         }
 | |
| 
 | |
|         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");
 | |
|         }
 | |
| 
 | |
|         if (ipRange == null) {
 | |
|             ipRange = RemoteAccessVpnClientIpRange.valueIn(ipAddr.getAccountId());
 | |
|         }
 | |
|         final 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<String, Integer> cidr = null;
 | |
| 
 | |
|         // TODO: assumes one virtual network / domr per account per zone
 | |
|         if (networkId != null) {
 | |
|         	vpnVO = _remoteAccessVpnDao.findByAccountAndNetwork(ipAddr.getAccountId(), networkId);
 | |
|         	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(networkId);
 | |
|         	if (!_networkMgr.areServicesSupportedInNetwork(network.getId(), Service.Vpn)) {
 | |
|         		throw new InvalidParameterValueException("Vpn service is not supported in network id=" + ipAddr.getAssociatedWithNetworkId());
 | |
|         	}
 | |
|         	cidr = NetUtils.getCidr(network.getCidr());
 | |
|         } else { // Don't need to check VPC because there is only one IP(source NAT IP) available for VPN 
 | |
|         	Vpc vpc = _vpcDao.findById(vpcId);
 | |
|         	cidr = NetUtils.getCidr(vpc.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]);
 | |
|         final String newIpRange = NetUtils.long2Ip(++startIp) + "-" + range[1];
 | |
|         final String sharedSecret = PasswordGenerator.generatePresharedKey(_pskLength);
 | |
| 
 | |
|         return Transaction.execute(new TransactionCallbackWithException<RemoteAccessVpn, NetworkRuleConflictException>() {
 | |
|             @Override
 | |
|             public RemoteAccessVpn doInTransaction(TransactionStatus status) throws NetworkRuleConflictException {
 | |
|             	if (vpcId == null) {
 | |
|             		_rulesMgr.reservePorts(ipAddr, NetUtils.UDP_PROTO, Purpose.Vpn, openFirewallFinal, caller, NetUtils.VPN_PORT, NetUtils.VPN_L2TP_PORT, NetUtils.VPN_NATT_PORT);
 | |
|             	}
 | |
|                 RemoteAccessVpnVO vpnVO = new RemoteAccessVpnVO(ipAddr.getAccountId(), ipAddr.getDomainId(), ipAddr.getAssociatedWithNetworkId(),
 | |
|                         publicIpId, vpcId, range[0], newIpRange, sharedSecret);
 | |
|                 return _remoteAccessVpnDao.persist(vpnVO);
 | |
|             }
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     private void validateRemoteAccessVpnConfiguration() throws ConfigurationException {
 | |
|         String ipRange = RemoteAccessVpnClientIpRange.value();
 | |
|         if (ipRange == null) {
 | |
|             s_logger.warn("Remote Access VPN global 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 destroyRemoteAccessVpnForIp(long ipId, Account caller) throws ResourceUnavailableException {
 | |
|         final RemoteAccessVpnVO vpn = _remoteAccessVpnDao.findByPublicIpAddress(ipId);
 | |
|         if (vpn == null) {
 | |
|             s_logger.debug("there are no Remote access vpns for public ip address id=" + ipId);
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         _accountMgr.checkAccess(caller, null, true, vpn);
 | |
| 
 | |
|         vpn.setState(RemoteAccessVpn.State.Removed);
 | |
|         _remoteAccessVpnDao.update(vpn.getId(), vpn);
 | |
| 
 | |
|         boolean success = false;
 | |
|         try {
 | |
|             for (RemoteAccessVPNServiceProvider element : _vpnServiceProviders) {
 | |
|                 if (element.stopVpn(vpn)) {
 | |
|                     success = true;
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|         } finally {
 | |
|             if (success) {
 | |
|                 //Cleanup corresponding ports
 | |
|                 final List<? extends FirewallRule> vpnFwRules = _rulesDao.listByIpAndPurpose(ipId, Purpose.Vpn);
 | |
|                 
 | |
|                 boolean applyFirewall = false;
 | |
|                 final List<FirewallRuleVO> fwRules = new ArrayList<FirewallRuleVO>();
 | |
|                 //if related firewall rule is created for the first vpn port, it would be created for the 2 other ports as well, so need to cleanup the backend
 | |
|                 if (vpnFwRules.size() != 0 && _rulesDao.findByRelatedId(vpnFwRules.get(0).getId()) != null) {
 | |
|                     applyFirewall = true;
 | |
|                 }
 | |
| 
 | |
|                 if (applyFirewall) {
 | |
|                     Transaction.execute(new TransactionCallbackNoReturn() {
 | |
|                         @Override
 | |
|                         public void doInTransactionWithoutResult(TransactionStatus status) {
 | |
|                             for (FirewallRule vpnFwRule : vpnFwRules) {
 | |
|                                 //don't apply on the backend yet; send all 3 rules in a banch
 | |
|                                 _firewallMgr.revokeRelatedFirewallRule(vpnFwRule.getId(), false);
 | |
|                                 fwRules.add(_rulesDao.findByRelatedId(vpnFwRule.getId()));
 | |
|                             }
 | |
|         
 | |
|                             s_logger.debug("Marked " + fwRules.size() + " firewall rules as Revoked as a part of disable remote access vpn");
 | |
|                         }
 | |
|                     });
 | |
| 
 | |
|                     //now apply vpn rules on the backend
 | |
|                     s_logger.debug("Reapplying firewall rules for ip id=" + ipId + " as a part of disable remote access vpn");
 | |
|                     success = _firewallMgr.applyIngressFirewallRules(ipId, caller);
 | |
|                 }
 | |
| 
 | |
|                 if (success) {
 | |
|                     try {
 | |
|                         Transaction.execute(new TransactionCallbackNoReturn() {
 | |
|                             @Override
 | |
|                             public void doInTransactionWithoutResult(TransactionStatus status) {
 | |
|                                 _remoteAccessVpnDao.remove(vpn.getId());
 | |
|                                 // Stop billing of VPN users when VPN is removed. VPN_User_ADD events will be generated when VPN is created again
 | |
|                                 List<VpnUserVO> vpnUsers = _vpnUsersDao.listByAccount(vpn.getAccountId());
 | |
|                                 for(VpnUserVO user : vpnUsers){
 | |
|                                     // VPN_USER_REMOVE event is already generated for users in Revoke state
 | |
|                                     if(user.getState() != VpnUser.State.Revoke){
 | |
|                                         UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VPN_USER_REMOVE, user.getAccountId(),
 | |
|                                                 0, user.getId(), user.getUsername(), user.getClass().getName(), user.getUuid());
 | |
|                                     }
 | |
|                                 }
 | |
|                                 if (vpnFwRules != null) {
 | |
|                                     for (FirewallRule vpnFwRule : vpnFwRules) {
 | |
|                                         _rulesDao.remove(vpnFwRule.getId());
 | |
|                                         s_logger.debug("Successfully removed firewall rule with ip id=" + vpnFwRule.getSourceIpAddressId() + " and port " + vpnFwRule.getSourcePortStart() + " as a part of vpn cleanup");
 | |
|                                     }
 | |
|                                 }
 | |
|                             }
 | |
|                         });
 | |
|                     } catch (Exception ex) {
 | |
|                         s_logger.warn("Unable to release the three vpn ports from the firewall rules", ex);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     @DB
 | |
|     public VpnUser addVpnUser(final long vpnOwnerId, final String username, final String password) {
 | |
|         final Account caller = CallContext.current().getCallingAccount();
 | |
| 
 | |
|         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 '@#+=.-_'");
 | |
|         }
 | |
| 
 | |
|         return Transaction.execute(new TransactionCallback<VpnUser>() {
 | |
|             @Override
 | |
|             public VpnUser doInTransaction(TransactionStatus status) {
 | |
|                 Account owner = _accountDao.lockRow(vpnOwnerId, true);
 | |
|                 if (owner == null) {
 | |
|                     throw new InvalidParameterValueException("Unable to add vpn user: Another operation active");
 | |
|                 }
 | |
|                 _accountMgr.checkAccess(caller, null, true, owner);
 | |
|         
 | |
|                 //don't allow duplicated user names for the same account
 | |
|                 VpnUserVO vpnUser = _vpnUsersDao.findByAccountAndUsername(owner.getId(), username);
 | |
|                 if (vpnUser != null) {
 | |
|                     throw new InvalidParameterValueException("VPN User with name " + username + " is already added for account " + 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));
 | |
|                 UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VPN_USER_ADD, user.getAccountId(), 0, user.getId(),
 | |
|                         user.getUsername(), user.getClass().getName(), user.getUuid());
 | |
|                 
 | |
|                 return user;
 | |
|             }
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     @DB @Override
 | |
|     public boolean removeVpnUser(long vpnOwnerId, String username, Account caller) {
 | |
|         final VpnUserVO user = _vpnUsersDao.findByAccountAndUsername(vpnOwnerId, username);
 | |
|         if (user == null) {
 | |
|             throw new InvalidParameterValueException("Could not find vpn user " + username);
 | |
|         }
 | |
|         _accountMgr.checkAccess(caller, null, true, user);
 | |
| 
 | |
|         Transaction.execute(new TransactionCallbackNoReturn() {
 | |
|             @Override
 | |
|             public void doInTransactionWithoutResult(TransactionStatus status) {
 | |
|                 user.setState(State.Revoke);
 | |
|                 _vpnUsersDao.update(user.getId(), user);
 | |
|                 UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VPN_USER_REMOVE, user.getAccountId(), 0, user.getId(),
 | |
|                         user.getUsername(), user.getClass().getName(), user.getUuid());
 | |
|             }
 | |
|         });
 | |
| 
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public List<? extends VpnUser> listVpnUsers(long vpnOwnerId, String userName) {
 | |
|         Account caller = CallContext.current().getCallingAccount();
 | |
|         Account owner = _accountDao.findById(vpnOwnerId);
 | |
|         _accountMgr.checkAccess(caller, null, true, owner);
 | |
|         return _vpnUsersDao.listByAccount(vpnOwnerId);
 | |
|     }
 | |
| 
 | |
|     @Override @DB
 | |
|     public RemoteAccessVpnVO startRemoteAccessVpn(long ipAddressId, boolean openFirewall) throws ResourceUnavailableException {
 | |
|         Account caller = CallContext.current().getCallingAccount();
 | |
| 
 | |
|         final RemoteAccessVpnVO vpn = _remoteAccessVpnDao.findByPublicIpAddress(ipAddressId);
 | |
|         if (vpn == null) {
 | |
|             throw new InvalidParameterValueException("Unable to find your vpn: " + ipAddressId);
 | |
|         }
 | |
|         
 | |
|         if (vpn.getVpcId() != null) {
 | |
|         	openFirewall = false;
 | |
|         }
 | |
| 
 | |
|         _accountMgr.checkAccess(caller, null, true, vpn);
 | |
| 
 | |
|         boolean started = false;
 | |
|         try {
 | |
|             boolean firewallOpened = true;
 | |
|             if (openFirewall) {
 | |
|                 firewallOpened = _firewallMgr.applyIngressFirewallRules(vpn.getServerAddressId(), caller);
 | |
|             }
 | |
| 
 | |
|             if (firewallOpened) {
 | |
|                 for (RemoteAccessVPNServiceProvider element : _vpnServiceProviders) {
 | |
|                     if (element.startVpn(vpn)) {
 | |
|                         started = true;
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return vpn;
 | |
|         } finally {
 | |
|             if (started) {
 | |
|                 Transaction.execute(new TransactionCallbackNoReturn() {
 | |
|                     @Override
 | |
|                     public void doInTransactionWithoutResult(TransactionStatus status) {
 | |
|                         vpn.setState(RemoteAccessVpn.State.Running);
 | |
|                         _remoteAccessVpnDao.update(vpn.getId(), vpn);
 | |
|         
 | |
|                         // Start billing of existing VPN users in ADD and Active state
 | |
|                         List<VpnUserVO> vpnUsers = _vpnUsersDao.listByAccount(vpn.getAccountId());
 | |
|                         for(VpnUserVO user : vpnUsers){
 | |
|                             if(user.getState() != VpnUser.State.Revoke){
 | |
|                                 UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VPN_USER_ADD, user.getAccountId(), 0,
 | |
|                                         user.getId(), user.getUsername(), user.getClass().getName(), user.getUuid());
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 });
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     @DB
 | |
|     @Override
 | |
|     public boolean applyVpnUsers(long vpnOwnerId, String userName) {
 | |
|         Account caller = CallContext.current().getCallingAccount();
 | |
|         Account owner = _accountDao.findById(vpnOwnerId);
 | |
|         _accountMgr.checkAccess(caller, null, true, owner);
 | |
| 
 | |
|         s_logger.debug("Applying vpn users for " + owner);
 | |
|         List<RemoteAccessVpnVO> vpns = _remoteAccessVpnDao.findByAccount(vpnOwnerId);
 | |
| 
 | |
|         List<VpnUserVO> 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);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         boolean success = true;
 | |
| 
 | |
|         boolean[] finals = new boolean[users.size()];
 | |
|         for (RemoteAccessVPNServiceProvider element : _vpnServiceProviders) {
 | |
|             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++) {
 | |
|             final 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 {
 | |
|                 if (user.getState() == State.Add && (user.getUsername()).equals(userName)) {
 | |
|                     Transaction.execute(new TransactionCallbackNoReturn() {
 | |
|                         @Override
 | |
|                         public void doInTransactionWithoutResult(TransactionStatus status) {
 | |
|                             _vpnUsersDao.remove(user.getId());
 | |
|                             UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VPN_USER_REMOVE, user.getAccountId(),
 | |
|                                     0, user.getId(), user.getUsername(), user.getClass().getName(), user.getUuid());
 | |
|                         }
 | |
|                     });
 | |
|                 }
 | |
|                 s_logger.warn("Failed to apply vpn for user " + user.getUsername() + ", accountId=" + user.getAccountId());
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return success;
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public Pair<List<? extends VpnUser>, Integer> searchForVpnUsers(ListVpnUsersCmd cmd) {
 | |
|         String username = cmd.getUsername();
 | |
|         Long id = cmd.getId();
 | |
|         Account caller = CallContext.current().getCallingAccount();
 | |
|         List<Long> permittedAccounts = new ArrayList<Long>();
 | |
| 
 | |
|         Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(cmd.getDomainId(), cmd.isRecursive(), null);
 | |
|         _accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false);
 | |
|         Long domainId = domainIdRecursiveListProject.first();
 | |
|         Boolean isRecursive = domainIdRecursiveListProject.second();
 | |
|         ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
 | |
|         Filter searchFilter = new Filter(VpnUserVO.class, "username", true, cmd.getStartIndex(), cmd.getPageSizeVal());
 | |
|         SearchBuilder<VpnUserVO> sb = _vpnUsersDao.createSearchBuilder();
 | |
|         _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
 | |
| 
 | |
| 
 | |
|         sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
 | |
|         sb.and("username", sb.entity().getUsername(), SearchCriteria.Op.EQ);
 | |
|         sb.and("state", sb.entity().getState(), Op.IN);
 | |
| 
 | |
|         SearchCriteria<VpnUserVO> sc = sb.create();
 | |
|         _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
 | |
| 
 | |
|         //list only active users
 | |
|         sc.setParameters("state", State.Active, State.Add);
 | |
| 
 | |
|         if (id != null) {
 | |
|             sc.setParameters("id", id);
 | |
|         }
 | |
| 
 | |
|         if (username != null) {
 | |
|             sc.setParameters("username", username);
 | |
|         }
 | |
| 
 | |
|         Pair<List<VpnUserVO>, Integer> result = _vpnUsersDao.searchAndCount(sc, searchFilter);
 | |
|         return new Pair<List<? extends VpnUser>, Integer>(result.first(), result.second());
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public Pair<List<? extends RemoteAccessVpn>, Integer> searchForRemoteAccessVpns(ListRemoteAccessVpnsCmd cmd) {
 | |
|         // do some parameter validation
 | |
|         Account caller = CallContext.current().getCallingAccount();
 | |
|         Long ipAddressId = cmd.getPublicIpId();
 | |
|         List<Long> permittedAccounts = new ArrayList<Long>();
 | |
| 
 | |
|         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.getAccountId();
 | |
|                 if (ipAddrAcctId == null) {
 | |
|                     throw new InvalidParameterValueException("Unable to list remote access vpns, IP address " + ipAddressId
 | |
|                             + " is not associated with an account.");
 | |
|                 }
 | |
|             }
 | |
|             _accountMgr.checkAccess(caller, null, true, publicIp);
 | |
|         }
 | |
| 
 | |
|         Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(cmd.getDomainId(), cmd.isRecursive(), null);
 | |
|         _accountMgr.buildACLSearchParameters(caller, null, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false);
 | |
|         Long domainId = domainIdRecursiveListProject.first();
 | |
|         Boolean isRecursive = domainIdRecursiveListProject.second();
 | |
|         ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
 | |
| 
 | |
|         Filter filter = new Filter(RemoteAccessVpnVO.class, "serverAddressId", false, cmd.getStartIndex(), cmd.getPageSizeVal());
 | |
|         SearchBuilder<RemoteAccessVpnVO> sb = _remoteAccessVpnDao.createSearchBuilder();
 | |
|         _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
 | |
| 
 | |
|         sb.and("serverAddressId", sb.entity().getServerAddressId(), Op.EQ);
 | |
|         sb.and("state", sb.entity().getState(), Op.EQ);
 | |
| 
 | |
|         SearchCriteria<RemoteAccessVpnVO> sc = sb.create();
 | |
|         _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
 | |
| 
 | |
| 
 | |
|         sc.setParameters("state", RemoteAccessVpn.State.Running);
 | |
| 
 | |
|         if (ipAddressId != null) {
 | |
|             sc.setParameters("serverAddressId", ipAddressId);
 | |
|         }
 | |
| 
 | |
|         Pair<List<RemoteAccessVpnVO>, Integer> result = _remoteAccessVpnDao.searchAndCount(sc, filter);
 | |
|         return new Pair<List<? extends RemoteAccessVpn>, Integer> (result.first(), result.second());
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
 | |
|         Map<String, String> configs = _configDao.getConfiguration(params);
 | |
| 
 | |
|         _userLimit = NumbersUtil.parseInt(configs.get(Config.RemoteAccessVpnUserLimit.key()), 8);
 | |
| 
 | |
|         _pskLength = NumbersUtil.parseInt(configs.get(Config.RemoteAccessVpnPskLength.key()), 24);
 | |
| 
 | |
|         validateRemoteAccessVpnConfiguration();
 | |
| 
 | |
|         VpnSearch = _remoteAccessVpnDao.createSearchBuilder();
 | |
|         VpnSearch.and("accountId", VpnSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
 | |
|         SearchBuilder<DomainVO> 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 List<? extends RemoteAccessVpn> listRemoteAccessVpns(long networkId) {
 | |
|         return _remoteAccessVpnDao.listByNetworkId(networkId);
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public RemoteAccessVpn getRemoteAccessVpn(long vpnAddrId) {
 | |
|         return _remoteAccessVpnDao.findByPublicIpAddress(vpnAddrId);
 | |
|     }
 | |
| 
 | |
|     public List<RemoteAccessVPNServiceProvider> getRemoteAccessVPNServiceProviders() {
 | |
|         List<RemoteAccessVPNServiceProvider> result = new ArrayList<RemoteAccessVPNServiceProvider>();
 | |
|         for (Iterator<RemoteAccessVPNServiceProvider> e = _vpnServiceProviders.iterator(); e.hasNext();) {
 | |
|             result.add(e.next());
 | |
|         }
 | |
| 
 | |
|         return result;
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public String getConfigComponentName() {
 | |
|         return RemoteAccessVpnService.class.getSimpleName();
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public ConfigKey<?>[] getConfigKeys() {
 | |
|         return new ConfigKey<?>[] {RemoteAccessVpnClientIpRange};
 | |
|     }
 | |
| }
 |