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 ( 8adb97...f7a166 )
by François
02:09
created

OpenVpn::determineProtoPortLocal()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 33
Code Lines 21

Duplication

Lines 25
Ratio 75.76 %

Importance

Changes 0
Metric Value
dl 25
loc 33
rs 8.439
c 0
b 0
f 0
cc 6
eloc 21
nc 4
nop 3

1 Method

Rating   Name   Duplication   Size   Complexity  
A OpenVpn::getVpnProto() 0 8 2
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\Node;
19
20
use SURFnet\VPN\Common\FileIO;
21
use SURFnet\VPN\Common\ProfileConfig;
22
use RuntimeException;
23
use SURFnet\VPN\Common\HttpClient\CaClient;
24
25
class OpenVpn
26
{
27
    /** @var string */
28
    private $vpnConfigDir;
29
30
    /** @var string */
31
    private $vpnTlsDir;
32
33
    public function __construct($vpnConfigDir, $vpnTlsDir)
34
    {
35
        FileIO::createDir($vpnConfigDir, 0700);
36
        $this->vpnConfigDir = $vpnConfigDir;
37
        FileIO::createDir($vpnTlsDir, 0700);
38
        $this->vpnTlsDir = $vpnTlsDir;
39
    }
40
41
    public function generateKeys(CaClient $caClient, $commonName, $dhSourceFile)
42
    {
43
        $certData = $caClient->addServerCertificate($commonName);
44
45
        $certFileMapping = [
46
            'ca' => sprintf('%s/ca.crt', $this->vpnTlsDir),
47
            'cert' => sprintf('%s/server.crt', $this->vpnTlsDir),
48
            'key' => sprintf('%s/server.key', $this->vpnTlsDir),
49
            'ta' => sprintf('%s/ta.key', $this->vpnTlsDir),
50
        ];
51
52
        foreach ($certFileMapping as $k => $v) {
53
            FileIO::writeFile($v, $certData[$k], 0600);
54
        }
55
56
        // copy the DH parameter file
57
        $dhTargetFile = sprintf('%s/dh.pem', $this->vpnTlsDir);
58
        if (false === copy($dhSourceFile, $dhTargetFile)) {
59
            throw new RuntimeException('unable to copy DH file');
60
        }
61
    }
62
63
    public function writeProfile($instanceNumber, $instanceId, $profileId, ProfileConfig $profileConfig)
64
    {
65
        $range = new IP($profileConfig->v('range'));
66
        $range6 = new IP($profileConfig->v('range6'));
67
        $processCount = $profileConfig->v('processCount');
68
69
        $splitRange = $range->split($processCount);
70
        $splitRange6 = $range6->split($processCount);
71
72
        if ($profileConfig->e('managementIp')) {
73
            $managementIp = $profileConfig->v('managementIp');
74
        } else {
75
            $managementIp = sprintf('10.42.%d.%d', 100 + $instanceNumber, 100 + $profileConfig->v('profileNumber'));
76
        }
77
78
        $processConfig = [
79
            'managementIp' => $managementIp,
80
        ];
81
82
        for ($i = 0; $i < $processCount; ++$i) {
83
            list($proto, $port) = self::getProtoPortListen(
84
                $profileConfig->v('processCount'),
85
                $profileConfig->v('listen'),
86
                $profileConfig->v('portShare')
87
            )[$i];
88
89
            $processConfig['range'] = $splitRange[$i];
90
            $processConfig['range6'] = $splitRange6[$i];
91
            $processConfig['dev'] = sprintf('tun-%d-%d-%d', $instanceNumber, $profileConfig->v('profileNumber'), $i);
92
            $processConfig['proto'] = $proto;
93
            $processConfig['port'] = $port;
94
            $processConfig['local'] = $profileConfig->v('listen');
95
            $processConfig['managementPort'] = 11940 + $i;
96
            $processConfig['configName'] = sprintf(
97
                'server-%s-%s-%d.conf',
98
                $instanceId,
99
                $profileId,
100
                $i
101
            );
102
103
            $this->writeProcess($instanceId, $profileId, $profileConfig, $processConfig);
104
        }
105
    }
106
107
    private function writeProcess($instanceId, $profileId, ProfileConfig $profileConfig, array $processConfig)
108
    {
109
        $tlsDir = sprintf('/etc/openvpn/tls/%s/%s', $instanceId, $profileId);
110
111
        $rangeIp = new IP($processConfig['range']);
112
        $range6Ip = new IP($processConfig['range6']);
113
114
        // static options
115
        $serverConfig = [
116
            '# OpenVPN Server Configuration',
117
            'verb 3',
118
            'dev-type tun',
119
            sprintf('user %s', $profileConfig->v('_user')),
120
            sprintf('group %s', $profileConfig->v('_group')),
121
            'topology subnet',
122
            'persist-key',
123
            'persist-tun',
124
            'keepalive 10 60',
125
            'comp-lzo no',
126
            'remote-cert-tls client',
127
            'tls-version-min 1.2',
128
            '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',
129
            'auth SHA256',
130
            'cipher AES-256-CBC',
131
            'client-connect /usr/sbin/vpn-server-node-client-connect',
132
            'client-disconnect /usr/sbin/vpn-server-node-client-disconnect',
133
            'push "comp-lzo no"',
134
            'push "explicit-exit-notify 3"',
135
            sprintf('ca %s/ca.crt', $tlsDir),
136
            sprintf('cert %s/server.crt', $tlsDir),
137
            sprintf('key %s/server.key', $tlsDir),
138
            sprintf('dh %s/dh.pem', $tlsDir),
139
            sprintf('tls-auth %s/ta.key 0', $tlsDir),
140
            sprintf('server %s %s', $rangeIp->getNetwork(), $rangeIp->getNetmask()),
141
            sprintf('server-ipv6 %s', $range6Ip->getAddressPrefix()),
142
            sprintf('max-clients %d', $rangeIp->getNumberOfHosts() - 1),
143
            sprintf('script-security %d', $profileConfig->v('twoFactor') ? 3 : 2),
144
            sprintf('dev %s', $processConfig['dev']),
145
            sprintf('port %d', $processConfig['port']),
146
            sprintf('management %s %d', $processConfig['managementIp'], $processConfig['managementPort']),
147
            sprintf('setenv INSTANCE_ID %s', $instanceId),
148
            sprintf('setenv PROFILE_ID %s', $profileId),
149
            sprintf('proto %s', $processConfig['proto']),
150
            sprintf('local %s', $processConfig['local']),
151
152
            // increase the renegotiation time to 8h from the default of 1h when
153
            // using 2FA, otherwise the user would be asked for the 2FA key every
154
            // hour
155
            sprintf('reneg-sec %d', $profileConfig->v('twoFactor') ? 28800 : 3600),
156
        ];
157
158
        if (!$profileConfig->v('enableLog')) {
159
            $serverConfig[] = 'log /dev/null';
160
        }
161
162
        if ('tcp' === $processConfig['proto']) {
163
            $serverConfig[] = 'tcp-nodelay';
164
        }
165
166
        if ($profileConfig->v('twoFactor')) {
167
            $serverConfig[] = 'auth-user-pass-verify /usr/sbin/vpn-server-node-verify-otp via-env';
168
        }
169
170
        // Routes
171
        $serverConfig = array_merge($serverConfig, self::getRoutes($profileConfig));
172
173
        // DNS
174
        $serverConfig = array_merge($serverConfig, self::getDns($profileConfig));
175
176
        // Client-to-client
177
        $serverConfig = array_merge($serverConfig, self::getClientToClient($profileConfig));
178
179
        sort($serverConfig, SORT_STRING);
180
181
        $configFile = sprintf('%s/%s', $this->vpnConfigDir, $processConfig['configName']);
182
183
        FileIO::writeFile($configFile, implode(PHP_EOL, $serverConfig), 0600);
184
    }
185
186
    private static function getRoutes(ProfileConfig $profileConfig)
187
    {
188
        $routeConfig = [];
189
        if ($profileConfig->v('defaultGateway')) {
190
            $routeConfig[] = 'push "redirect-gateway def1 bypass-dhcp"';
191
192
            // for Windows clients we need this extra route to mark the TAP adapter as
193
            // trusted and as having "Internet" access to allow the user to set it to
194
            // "Home" or "Work" to allow accessing file shares and printers
195
            // NOTE: this will break OS X tunnelblick because on disconnect it will
196
            // remove all default routes, including the one set before the VPN
197
            // was brought up
198
            //$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...
199
200
            // for iOS we need this OpenVPN 2.4 "ipv6" flag to redirect-gateway
201
            // See https://docs.openvpn.net/docs/openvpn-connect/openvpn-connect-ios-faq.html
202
            $routeConfig[] = 'push "redirect-gateway ipv6"';
203
204
            // we use 2000::/3 instead of ::/0 because it seems to break on native IPv6
205
            // networks where the ::/0 default route already exists
206
            $routeConfig[] = 'push "route-ipv6 2000::/3"';
207
        } else {
208
            // there may be some routes specified, push those, and not the default
209
            foreach ($profileConfig->v('routes') as $route) {
210
                $routeIp = new IP($route);
211
                if (6 === $routeIp->getFamily()) {
212
                    // IPv6
213
                    $routeConfig[] = sprintf('push "route-ipv6 %s"', $routeIp->getAddressPrefix());
214
                } else {
215
                    // IPv4
216
                    $routeConfig[] = sprintf('push "route %s %s"', $routeIp->getAddress(), $routeIp->getNetmask());
217
                }
218
            }
219
        }
220
221
        return $routeConfig;
222
    }
223
224
    private static function getDns(ProfileConfig $profileConfig)
225
    {
226
        // only push DNS if we are the default route
227
        if (!$profileConfig->v('defaultGateway')) {
228
            return [];
229
        }
230
231
        $dnsEntries = [];
232
        foreach ($profileConfig->v('dns') as $dnsAddress) {
233
            $dnsEntries[] = sprintf('push "dhcp-option DNS %s"', $dnsAddress);
234
        }
235
236
        // prevent DNS leakage on Windows
237
        $dnsEntries[] = 'push "block-outside-dns"';
238
239
        return $dnsEntries;
240
    }
241
242
    private static function getClientToClient(ProfileConfig $profileConfig)
243
    {
244
        if (!$profileConfig->v('clientToClient')) {
245
            return [];
246
        }
247
248
        $rangeIp = new IP($profileConfig->v('range'));
249
        $range6Ip = new IP($profileConfig->v('range6'));
250
251
        return [
252
            'client-to-client',
253
            sprintf('push "route %s %s"', $rangeIp->getAddress(), $rangeIp->getNetmask()),
254
            sprintf('push "route-ipv6 %s"', $range6Ip->getAddressPrefix()),
255
        ];
256
    }
257
258
    public static function getVpnProto($listenAddress)
259
    {
260
        if (false !== strpos($listenAddress, ':')) {
261
            return ['udp6', 'tcp6-server'];
262
        }
263
264
        return ['udp', 'tcp-server'];
265
    }
266
267
    public static function getProtoPortListen($processCount, $listenAddress, $portShare)
268
    {
269
        $vpnProto = self::getVpnProto($listenAddress);
270
271
        switch ($processCount) {
272
            case 1:
273
                return [
274
                    [$vpnProto[0], 1194],
275
                ];
276
            case 2:
277
                return [
278
                    [$vpnProto[0], 1194],
279
                    [$vpnProto[1], $portShare ? 1194 : 443],
280
                ];
281
            case 4:
282
            default:
283
                return [
284
                    [$vpnProto[0], 1194],
285
                    [$vpnProto[0], 1195],
286
                    [$vpnProto[1], 1194],
287
                    [$vpnProto[1], $portShare ? 1195 : 443],
288
                ];
289
            case 8:
0 ignored issues
show
Unused Code introduced by
case 8: return array...rtShare ? 1196 : 443)); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
290
                return [
291
                    [$vpnProto[0], 1194],
292
                    [$vpnProto[0], 1195],
293
                    [$vpnProto[0], 1196],
294
                    [$vpnProto[0], 1197],
295
                    [$vpnProto[0], 1198],
296
                    [$vpnProto[1], 1194],
297
                    [$vpnProto[1], 1195],
298
                    [$vpnProto[1], $portShare ? 1196 : 443],
299
                ];
300
        }
301
    }
302
}
303