GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 480f40...e0a7c0 )
by François
03:02
created

OpenVpn::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
nc 1
nop 2
dl 0
loc 7
rs 9.4285
c 1
b 0
f 0
1
<?php
2
/**
3
 *  Copyright (C) 2016 SURFnet.
4
 *
5
 *  This program is free software: you can redistribute it and/or modify
6
 *  it under the terms of the GNU Affero General Public License as
7
 *  published by the Free Software Foundation, either version 3 of the
8
 *  License, or (at your option) any later version.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU Affero General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU Affero General Public License
16
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
 */
18
namespace SURFnet\VPN\Server\Config;
19
20
use SURFnet\VPN\Server\InstanceConfig;
21
use SURFnet\VPN\Server\PoolConfig;
22
use SURFnet\VPN\Server\IP;
23
use SURFnet\VPN\Common\FileIO;
24
use RuntimeException;
25
26
class OpenVpn
27
{
28
    /** @var string */
29
    private $vpnConfigDir;
30
31
    /** @var string */
32
    private $vpnTlsDir;
33
34
    public function __construct($vpnConfigDir, $vpnTlsDir)
35
    {
36
        FileIO::createDir($vpnConfigDir, 0700);
37
        $this->vpnConfigDir = $vpnConfigDir;
38
        FileIO::createDir($vpnTlsDir, 0700);
39
        $this->vpnTlsDir = $vpnTlsDir;
40
    }
41
42
    public function generateKeys($apiUri, $userName, $userPass, $commonName, $dhLength)
43
    {
44
        $postData = [
45
            'common_name' => $commonName,
46
        ];
47
48
        $apiUri = sprintf('%s/add_server_certificate', $apiUri);
49
50
        $ch = curl_init($apiUri);
51
        $optionsSet = curl_setopt_array(
52
            $ch,
53
            [
54
                CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
55
                CURLOPT_USERPWD => sprintf('%s:%s', $userName, $userPass),
56
                CURLOPT_POST => true,
57
                CURLOPT_POSTFIELDS => http_build_query($postData),
58
                CURLOPT_RETURNTRANSFER => true,
59
            ]
60
        );
61
62
        if (!$optionsSet) {
63
            throw new RuntimeException('unable to set all cURL options');
64
        }
65
66
        $response = curl_exec($ch);
67
        curl_close($ch);
68
        if (false === $response) {
69
            throw new RuntimeException(sprintf('cURL request error: %s', curl_error($ch)));
70
        }
71
72
        $configData = json_decode($response, true);
73
        $certData = $configData['data']['add_server_certificate'];
74
75
        $certFileMapping = [
76
            'ca' => sprintf('%s/ca.crt', $this->vpnTlsDir),
77
            'cert' => sprintf('%s/server.crt', $this->vpnTlsDir),
78
            'key' => sprintf('%s/server.key', $this->vpnTlsDir),
79
            'ta' => sprintf('%s/ta.key', $this->vpnTlsDir),
80
        ];
81
82
        foreach ($certFileMapping as $k => $v) {
83
            FileIO::writeFile($v, $certData[$k], 0600);
84
        }
85
86
        // generate the DH params
87
        $dhFile = sprintf('%s/dh.pem', $this->vpnTlsDir);
88
        $cmd = sprintf('/usr/bin/openssl dhparam -out %s %d >/dev/null 2>/dev/null', $dhFile, $dhLength);
89
        $output = [];
90
        $returnValue = -1;
91
        exec($cmd, $output, $returnValue);
92
        if (0 !== $returnValue) {
93
            throw new RuntimeException('unable to generate DH');
94
        }
95
    }
96
97
    public function write($instanceId, InstanceConfig $instanceConfig)
98
    {
99
        $instanceNumber = $instanceConfig->v('instanceNumber');
100
        foreach (array_keys($instanceConfig->v('vpnPools')) as $poolNumber => $poolId) {
101
            $poolConfig = new PoolConfig($instanceConfig->v('vpnPools', $poolId));
102
            $this->writePool($instanceNumber, $instanceId, $poolNumber, $poolId, $poolConfig);
103
        }
104
    }
105
106
    private function writePool($instanceNumber, $instanceId, $poolNumber, $poolId, PoolConfig $poolConfig)
107
    {
108
        $range = new IP($poolConfig->v('range'));
109
        $range6 = new IP($poolConfig->v('range6'));
110
        $processCount = $poolConfig->v('processCount');
111
112
        $splitRange = $range->split($processCount);
113
        $splitRange6 = $range6->split($processCount);
114
115
        $processConfig = [
116
            'managementIp' => sprintf('127.42.%d.%d', 100 + $instanceNumber, 100 + $poolNumber),
117
        ];
118
119
        for ($i = 0; $i < $processCount; ++$i) {
120
            // protocol is udp unless it is the last process when there is
121
            // not just one process
122
            if (1 === $processCount || $i !== $processCount - 1) {
123
                $proto = 'udp';
124
                $port = 1194 + $i;
125
            } else {
126
                $proto = 'tcp';
127
                $port = 1194;
128
            }
129
130
            $processConfig['range'] = $splitRange[$i];
131
            $processConfig['range6'] = $splitRange6[$i];
132
            $processConfig['dev'] = sprintf('tun-%d-%d-%d', $instanceNumber, $poolNumber, $i);
133
            $processConfig['proto'] = $proto;
134
            $processConfig['port'] = $port;
135
            $processConfig['managementPort'] = 11940 + $i;
136
            $processConfig['configName'] = sprintf(
137
                'server-%s-%s-%d.conf',
138
                $instanceId,
139
                $poolId,
140
                $i
141
            );
142
143
            $this->writeProcess($instanceId, $poolId, $poolConfig, $processConfig);
144
        }
145
    }
146
147
    private function writeProcess($instanceId, $poolId, PoolConfig $poolConfig, array $processConfig)
148
    {
149
        $tlsDir = sprintf('/etc/openvpn/tls/%s', $instanceId);
150
151
        $rangeIp = new IP($processConfig['range']);
152
        $range6Ip = new IP($processConfig['range6']);
153
154
        // static options
155
        $serverConfig = [
156
            '# OpenVPN Server Configuration',
157
            'verb 3',
158
            'dev-type tun',
159
            'user openvpn',
160
            'group openvpn',
161
            'topology subnet',
162
            'persist-key',
163
            'persist-tun',
164
            'keepalive 10 60',
165
            'comp-lzo no',
166
            'remote-cert-tls client',
167
            'tls-version-min 1.2',
168
            'tls-cipher TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA',
169
            'auth SHA256',
170
            'cipher AES-256-CBC',
171
            'client-connect /usr/sbin/vpn-server-api-client-connect',
172
            'client-disconnect /usr/sbin/vpn-server-api-client-disconnect',
173
            'push "comp-lzo no"',
174
            'push "explicit-exit-notify 3"',
175
            sprintf('ca %s/ca.crt', $tlsDir),
176
            sprintf('cert %s/server.crt', $tlsDir),
177
            sprintf('key %s/server.key', $tlsDir),
178
            sprintf('dh %s/dh.pem', $tlsDir),
179
            sprintf('tls-auth %s/ta.key 0', $tlsDir),
180
            sprintf('server %s %s', $rangeIp->getNetwork(), $rangeIp->getNetmask()),
181
            sprintf('server-ipv6 %s', $range6Ip->getAddressPrefix()),
182
            sprintf('max-clients %d', $rangeIp->getNumberOfHosts() - 1),
183
            sprintf('script-security %d', $poolConfig->v('twoFactor') ? 3 : 2),
184
            sprintf('dev %s', $processConfig['dev']),
185
            sprintf('port %d', $processConfig['port']),
186
            sprintf('management %s %d', $processConfig['managementIp'], $processConfig['managementPort']),
187
            sprintf('setenv INSTANCE_ID %s', $instanceId),
188
            sprintf('setenv POOL_ID %s', $poolId),
189
            sprintf('proto %s', 'tcp' === $processConfig['proto'] ? 'tcp-server' : 'udp'),
190
            sprintf('local %s', 'tcp' === $processConfig['proto'] ? $processConfig['managementIp'] : $poolConfig->v('listen')),
191
192
            // increase the renegotiation time to 8h from the default of 1h when
193
            // using 2FA, otherwise the user would be asked for the 2FA key every
194
            // hour
195
            sprintf('reneg-sec %d', $poolConfig->v('twoFactor') ? 28800 : 3600),
196
        ];
197
198
        if (!$poolConfig->v('enableLog')) {
199
            $serverConfig[] = 'log /dev/null';
200
        }
201
202
        if ('tcp' === $processConfig['proto']) {
203
            $serverConfig[] = 'tcp-nodelay';
204
        }
205
206
        if ($poolConfig->v('twoFactor')) {
207
            $serverConfig[] = 'auth-user-pass-verify /usr/sbin/vpn-server-api-verify-otp via-env';
208
        }
209
210
        // Routes
211
        $serverConfig = array_merge($serverConfig, self::getRoutes($poolConfig));
212
213
        // DNS
214
        $serverConfig = array_merge($serverConfig, self::getDns($poolConfig));
215
216
        // Client-to-client
217
        $serverConfig = array_merge($serverConfig, self::getClientToClient($poolConfig));
218
219
        sort($serverConfig, SORT_STRING);
220
221
        $configFile = sprintf('%s/%s', $this->vpnConfigDir, $processConfig['configName']);
222
223
        FileIO::writeFile($configFile, implode(PHP_EOL, $serverConfig), 0600);
224
    }
225
226
    private static function getRoutes(PoolConfig $poolConfig)
227
    {
228
        $routeConfig = [];
229
        if ($poolConfig->v('defaultGateway')) {
230
            $routeConfig[] = 'push "redirect-gateway def1 bypass-dhcp"';
231
232
            // for Windows clients we need this extra route to mark the TAP adapter as
233
            // trusted and as having "Internet" access to allow the user to set it to
234
            // "Home" or "Work" to allow accessing file shares and printers
235
            // NOTE: this will break OS X tunnelblick because on disconnect it will
236
            // remove all default routes, including the one set before the VPN
237
            // was brought up
238
            //$routeConfig[] = 'push "route 0.0.0.0 0.0.0.0"';
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
239
240
            // for iOS we need this OpenVPN 2.4 "ipv6" flag to redirect-gateway
241
            // See https://docs.openvpn.net/docs/openvpn-connect/openvpn-connect-ios-faq.html
242
            $routeConfig[] = 'push "redirect-gateway ipv6"';
243
244
            // we use 2000::/3 instead of ::/0 because it seems to break on native IPv6
245
            // networks where the ::/0 default route already exists
246
            $routeConfig[] = 'push "route-ipv6 2000::/3"';
247
        } else {
248
            // there may be some routes specified, push those, and not the default
249
            foreach ($poolConfig->v('routes') as $route) {
250
                $routeIp = new IP($route);
251
                if (6 === $routeIp->getFamily()) {
252
                    // IPv6
253
                    $routeConfig[] = sprintf('push "route-ipv6 %s"', $routeIp->getAddressPrefix());
254
                } else {
255
                    // IPv4
256
                    $routeConfig[] = sprintf('push "route %s %s"', $routeIp->getAddress(), $routeIp->getNetmask());
257
                }
258
            }
259
        }
260
261
        return $routeConfig;
262
    }
263
264
    private static function getDns(PoolConfig $poolConfig)
265
    {
266
        // only push DNS if we are the default route
267
        if (!$poolConfig->v('defaultGateway')) {
268
            return [];
269
        }
270
271
        $dnsEntries = [];
272
        foreach ($poolConfig->v('dns') as $dnsAddress) {
273
            $dnsEntries[] = sprintf('push "dhcp-option DNS %s"', $dnsAddress);
274
        }
275
276
        // prevent DNS leakage on Windows
277
        $dnsEntries[] = 'push "block-outside-dns"';
278
279
        return $dnsEntries;
280
    }
281
282
    private static function getClientToClient(PoolConfig $poolConfig)
283
    {
284
        if (!$poolConfig->v('clientToClient')) {
285
            return [];
286
        }
287
288
        $rangeIp = new IP($poolConfig->v('range'));
289
        $range6Ip = new IP($poolConfig->v('range6'));
290
291
        return [
292
            'client-to-client',
293
            sprintf('push "route %s %s"', $rangeIp->getAddress(), $rangeIp->getNetmask()),
294
            sprintf('push "route-ipv6 %s"', $range6Ip->getAddressPrefix()),
295
        ];
296
    }
297
}
298