예제 #1
0
 /**
  * Disconnect all clients with this CN from all pools and instances
  * managed by this service.
  *
  * @param string $commonName the CN to kill
  */
 public function kill($commonName)
 {
     $clientsKilled = 0;
     // loop over all pools
     foreach (array_keys($this->instanceConfig->v('vpnPools')) as $poolNumber => $poolId) {
         $poolConfig = new PoolConfig($this->instanceConfig->v('vpnPools', $poolId));
         $managementIp = sprintf('127.42.%d.%d', 100 + $this->instanceConfig->v('instanceNumber'), 100 + $poolNumber);
         // loop over all processes
         for ($i = 0; $i < $poolConfig->v('processCount'); ++$i) {
             // add all kills from this instance to poolKills
             try {
                 // open the socket connection
                 $this->managementSocket->open(sprintf('tcp://%s:%d', $managementIp, 11940 + $i));
                 $response = $this->managementSocket->command(sprintf('kill %s', $commonName));
                 if (0 === mb_strpos($response[0], 'SUCCESS: ')) {
                     ++$clientsKilled;
                 }
                 // close the socket connection
                 $this->managementSocket->close();
             } catch (ManagementSocketException $e) {
                 // we log the error, but continue with the next instance
                 $this->logger->error(sprintf('error with socket "%s:%s", message: "%s"', $managementIp, 11940 + $i, $e->getMessage()));
             }
         }
     }
     return 0 !== $clientsKilled;
 }
예제 #2
0
 public function init(Service $service)
 {
     $service->get('/server_pools', function (Request $request, array $hookData) {
         Utils::requireUser($hookData, ['vpn-admin-portal', 'vpn-user-portal']);
         $responseData = [];
         foreach (array_keys($this->instanceConfig->v('vpnPools')) as $poolId) {
             $poolConfig = new PoolConfig($this->instanceConfig->v('vpnPools', $poolId));
             $responseData[$poolId] = $poolConfig->v();
         }
         return new ApiResponse('server_pools', $responseData);
     });
     $service->get('/server_pool', function (Request $request, array $hookData) {
         Utils::requireUser($hookData, ['vpn-admin-portal', 'vpn-user-portal']);
         $poolId = $request->getQueryParameter('pool_id');
         InputValidation::poolId($poolId);
         $poolConfig = new PoolConfig($this->instanceConfig->v('vpnPools', $poolId));
         return new ApiResponse('server_pool', $poolConfig->v());
     });
 }
예제 #3
0
 public function connect(array $envData)
 {
     $userId = self::getUserId($envData['common_name']);
     $dataDir = sprintf('%s/data/%s', $this->baseDir, $envData['INSTANCE_ID']);
     // is the user account disabled?
     // XXX we have to be careful, if the directory is not readable by the
     // openvpn user, it is assumed the user is not disabled! We have to
     // find a more robust solution for this!
     $disabledUsersDir = sprintf('%s/users/disabled', $dataDir);
     if (@file_exists(sprintf('%s/%s', $disabledUsersDir, $userId))) {
         throw new ConnectionException('client not allowed, user is disabled');
     }
     // is the common name disabled?
     // XXX we have to be careful, if the directory is not readable by the
     // openvpn user, it is assumed the user is not disabled! We have to
     // find a more robust solution for this!
     $disabledCommonNamesDir = sprintf('%s/common_names/disabled', $dataDir);
     if (@file_exists(sprintf('%s/%s', $disabledCommonNamesDir, $envData['common_name']))) {
         throw new ConnectionException('client not allowed, CN is disabled');
     }
     $configDir = sprintf('%s/config/%s', $this->baseDir, $envData['INSTANCE_ID']);
     // read the instance/pool configuration
     $instanceConfig = InstanceConfig::fromFile(sprintf('%s/config.yaml', $configDir));
     // is the ACL enabled?
     $poolId = $envData['POOL_ID'];
     $poolConfig = new PoolConfig($instanceConfig->v('vpnPools', $poolId));
     if ($poolConfig->v('enableAcl')) {
         $aclGroupProvider = $poolConfig->v('aclGroupProvider');
         $groupProviderClass = sprintf('SURFnet\\VPN\\Server\\GroupProvider\\%s', $aclGroupProvider);
         $groupProvider = new $groupProviderClass($dataDir, $instanceConfig);
         $aclGroupList = $poolConfig->v('aclGroupList');
         if (false === self::isMember($groupProvider->getGroups($userId), $aclGroupList)) {
             throw new ConnectionException(sprintf('client not allowed, not a member of "%s"', implode(',', $aclGroupList)));
         }
     }
 }
예제 #4
0
 *
 * IPv6:
 * The IPv6 address is generated according to RFC 4193 (Global ID), it results
 * in a /60 network.
 */
try {
    $p = new CliParser('Automatically generate an IP address and basic config for a pool', ['instance' => ['the instance to target, e.g. vpn.example', true, true], 'pool' => ['the pool to target, e.g. internet', true, true], 'host' => ['the hostname clients connect to', true, true], 'ext' => ['the external interface, e.g. eth0', true, true]]);
    $opt = $p->parse($argv);
    if ($opt->e('help')) {
        echo $p->help();
        exit(0);
    }
    $v4 = sprintf('10.%s.%s.0/24', hexdec(bin2hex(random_bytes(1))), hexdec(bin2hex(random_bytes(1))));
    $v6 = sprintf('fd%s:%s:%s:%s::/60', bin2hex(random_bytes(1)), bin2hex(random_bytes(2)), bin2hex(random_bytes(2)), bin2hex(random_bytes(2) & hex2bin('fff0')));
    echo sprintf('IPv4 CIDR  : %s', $v4) . PHP_EOL;
    echo sprintf('IPv6 prefix: %s', $v6) . PHP_EOL;
    $configFile = sprintf('%s/config/%s/config.yaml', dirname(__DIR__), $opt->v('instance'));
    $instanceConfig = InstanceConfig::fromFile($configFile);
    $poolConfig = new PoolConfig($instanceConfig->v('vpnPools', $opt->v('pool')));
    $instanceConfigData = $instanceConfig->v();
    $poolConfigData = $poolConfig->v();
    $poolConfigData['range'] = $v4;
    $poolConfigData['range6'] = $v6;
    $poolConfigData['hostName'] = $opt->v('host');
    $poolConfigData['extIf'] = $opt->v('ext');
    $instanceConfigData['vpnPools'][$opt->v('pool')] = $poolConfigData;
    InstanceConfig::toFile($configFile, $instanceConfigData);
} catch (Exception $e) {
    echo sprintf('ERROR: %s', $e->getMessage()) . PHP_EOL;
    exit(1);
}
예제 #5
0
 private static function getClientToClient(PoolConfig $poolConfig)
 {
     if (!$poolConfig->v('clientToClient')) {
         return [];
     }
     $rangeIp = new IP($poolConfig->v('range'));
     $range6Ip = new IP($poolConfig->v('range6'));
     return ['client-to-client', sprintf('push "route %s %s"', $rangeIp->getAddress(), $rangeIp->getNetmask()), sprintf('push "route-ipv6 %s"', $range6Ip->getAddressPrefix())];
 }
예제 #6
0
 private static function getForwardFirewall($instanceNumber, $poolNumber, PoolConfig $poolConfig, $inetFamily)
 {
     $forwardFirewall = [];
     if ($poolConfig->v('blockSmb')) {
         // drop SMB outgoing traffic
         // @see https://medium.com/@ValdikSS/deanonymizing-windows-users-and-capturing-microsoft-and-vpn-accounts-f7e53fe73834
         foreach (['tcp', 'udp'] as $proto) {
             $forwardFirewall[] = sprintf('-A vpn-%s-%s -o %s -m multiport -p %s --dports 137:139,445 -j REJECT --reject-with %s', $instanceNumber, $poolNumber, $poolConfig->v('extIf'), $proto, 4 === $inetFamily ? 'icmp-host-prohibited' : 'icmp6-adm-prohibited');
         }
     }
     return $forwardFirewall;
 }