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 ( 2b49b6...4daf00 )
by François
02:04
created

OpenVpn::getDns()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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