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 ( 2ffe0f...8dada9 )
by François
04:02
created

OpenVpn::write()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 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\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
use SURFnet\VPN\Common\HttpClient\CaClient;
26
27
class OpenVpn
28
{
29
    /** @var string */
30
    private $vpnConfigDir;
31
32
    /** @var string */
33
    private $vpnTlsDir;
34
35
    public function __construct($vpnConfigDir, $vpnTlsDir)
36
    {
37
        FileIO::createDir($vpnConfigDir, 0700);
38
        $this->vpnConfigDir = $vpnConfigDir;
39
        FileIO::createDir($vpnTlsDir, 0700);
40
        $this->vpnTlsDir = $vpnTlsDir;
41
    }
42
43
    public function generateKeys(CaClient $caClient, $commonName, $dhLength)
44
    {
45
        $certData = $caClient->addServerCertificate($commonName);
46
47
        $certFileMapping = [
48
            'ca' => sprintf('%s/ca.crt', $this->vpnTlsDir),
49
            'cert' => sprintf('%s/server.crt', $this->vpnTlsDir),
50
            'key' => sprintf('%s/server.key', $this->vpnTlsDir),
51
            'ta' => sprintf('%s/ta.key', $this->vpnTlsDir),
52
        ];
53
54
        foreach ($certFileMapping as $k => $v) {
55
            FileIO::writeFile($v, $certData[$k], 0600);
56
        }
57
58
        // generate the DH params
59
        // XXX use exec function we can steal from vpn-ca-api
60
        $dhFile = sprintf('%s/dh.pem', $this->vpnTlsDir);
61
        $cmd = sprintf('/usr/bin/openssl dhparam -out %s %d >/dev/null 2>/dev/null', $dhFile, $dhLength);
62
        $output = [];
63
        $returnValue = -1;
64
        exec($cmd, $output, $returnValue);
65
        if (0 !== $returnValue) {
66
            throw new RuntimeException('unable to generate DH');
67
        }
68
    }
69
70
    public function writePool($instanceNumber, $instanceId, $poolId, PoolConfig $poolConfig)
71
    {
72
        $range = new IP($poolConfig->v('range'));
73
        $range6 = new IP($poolConfig->v('range6'));
74
        $processCount = $poolConfig->v('processCount');
75
76
        $splitRange = $range->split($processCount);
77
        $splitRange6 = $range6->split($processCount);
78
79
        $processConfig = [
80
            'managementIp' => sprintf('127.42.%d.%d', 100 + $instanceNumber, 100 + $poolConfig->v('poolNumber')),
81
        ];
82
83
        for ($i = 0; $i < $processCount; ++$i) {
84
            // protocol is udp unless it is the last process when there is
85
            // not just one process
86
            if (1 === $processCount || $i !== $processCount - 1) {
87
                $proto = 'udp';
88
                $port = 1194 + $i;
89
            } else {
90
                $proto = 'tcp';
91
                $port = 1194;
92
            }
93
94
            $processConfig['range'] = $splitRange[$i];
95
            $processConfig['range6'] = $splitRange6[$i];
96
            $processConfig['dev'] = sprintf('tun-%d-%d-%d', $instanceNumber, $poolConfig->v('poolNumber'), $i);
97
            $processConfig['proto'] = $proto;
98
            $processConfig['port'] = $port;
99
            $processConfig['managementPort'] = 11940 + $i;
100
            $processConfig['configName'] = sprintf(
101
                'server-%s-%s-%d.conf',
102
                $instanceId,
103
                $poolId,
104
                $i
105
            );
106
107
            $this->writeProcess($instanceId, $poolId, $poolConfig, $processConfig);
108
        }
109
    }
110
111
    private function writeProcess($instanceId, $poolId, PoolConfig $poolConfig, array $processConfig)
112
    {
113
        $tlsDir = sprintf('/etc/openvpn/tls/%s/%s', $instanceId, $poolId);
114
115
        $rangeIp = new IP($processConfig['range']);
116
        $range6Ip = new IP($processConfig['range6']);
117
118
        // static options
119
        $serverConfig = [
120
            '# OpenVPN Server Configuration',
121
            'verb 3',
122
            'dev-type tun',
123
            'user openvpn',
124
            'group openvpn',
125
            'topology subnet',
126
            'persist-key',
127
            'persist-tun',
128
            'keepalive 10 60',
129
            'comp-lzo no',
130
            'remote-cert-tls client',
131
            'tls-version-min 1.2',
132
            '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',
133
            'auth SHA256',
134
            'cipher AES-256-CBC',
135
            'client-connect /usr/sbin/vpn-server-api-client-connect',
136
            'client-disconnect /usr/sbin/vpn-server-api-client-disconnect',
137
            'push "comp-lzo no"',
138
            'push "explicit-exit-notify 3"',
139
            sprintf('ca %s/ca.crt', $tlsDir),
140
            sprintf('cert %s/server.crt', $tlsDir),
141
            sprintf('key %s/server.key', $tlsDir),
142
            sprintf('dh %s/dh.pem', $tlsDir),
143
            sprintf('tls-auth %s/ta.key 0', $tlsDir),
144
            sprintf('server %s %s', $rangeIp->getNetwork(), $rangeIp->getNetmask()),
145
            sprintf('server-ipv6 %s', $range6Ip->getAddressPrefix()),
146
            sprintf('max-clients %d', $rangeIp->getNumberOfHosts() - 1),
147
            sprintf('script-security %d', $poolConfig->v('twoFactor') ? 3 : 2),
148
            sprintf('dev %s', $processConfig['dev']),
149
            sprintf('port %d', $processConfig['port']),
150
            sprintf('management %s %d', $processConfig['managementIp'], $processConfig['managementPort']),
151
            sprintf('setenv INSTANCE_ID %s', $instanceId),
152
            sprintf('setenv POOL_ID %s', $poolId),
153
            sprintf('proto %s', 'tcp' === $processConfig['proto'] ? 'tcp-server' : 'udp'),
154
            sprintf('local %s', 'tcp' === $processConfig['proto'] ? $processConfig['managementIp'] : $poolConfig->v('listen')),
155
156
            // increase the renegotiation time to 8h from the default of 1h when
157
            // using 2FA, otherwise the user would be asked for the 2FA key every
158
            // hour
159
            sprintf('reneg-sec %d', $poolConfig->v('twoFactor') ? 28800 : 3600),
160
        ];
161
162
        if (!$poolConfig->v('enableLog')) {
163
            $serverConfig[] = 'log /dev/null';
164
        }
165
166
        if ('tcp' === $processConfig['proto']) {
167
            $serverConfig[] = 'tcp-nodelay';
168
        }
169
170
        if ($poolConfig->v('twoFactor')) {
171
            $serverConfig[] = 'auth-user-pass-verify /usr/sbin/vpn-server-api-verify-otp via-env';
172
        }
173
174
        // Routes
175
        $serverConfig = array_merge($serverConfig, self::getRoutes($poolConfig));
176
177
        // DNS
178
        $serverConfig = array_merge($serverConfig, self::getDns($poolConfig));
179
180
        // Client-to-client
181
        $serverConfig = array_merge($serverConfig, self::getClientToClient($poolConfig));
182
183
        sort($serverConfig, SORT_STRING);
184
185
        $configFile = sprintf('%s/%s', $this->vpnConfigDir, $processConfig['configName']);
186
187
        FileIO::writeFile($configFile, implode(PHP_EOL, $serverConfig), 0600);
188
    }
189
190
    private static function getRoutes(PoolConfig $poolConfig)
191
    {
192
        $routeConfig = [];
193
        if ($poolConfig->v('defaultGateway')) {
194
            $routeConfig[] = 'push "redirect-gateway def1 bypass-dhcp"';
195
196
            // for Windows clients we need this extra route to mark the TAP adapter as
197
            // trusted and as having "Internet" access to allow the user to set it to
198
            // "Home" or "Work" to allow accessing file shares and printers
199
            // NOTE: this will break OS X tunnelblick because on disconnect it will
200
            // remove all default routes, including the one set before the VPN
201
            // was brought up
202
            //$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...
203
204
            // for iOS we need this OpenVPN 2.4 "ipv6" flag to redirect-gateway
205
            // See https://docs.openvpn.net/docs/openvpn-connect/openvpn-connect-ios-faq.html
206
            $routeConfig[] = 'push "redirect-gateway ipv6"';
207
208
            // we use 2000::/3 instead of ::/0 because it seems to break on native IPv6
209
            // networks where the ::/0 default route already exists
210
            $routeConfig[] = 'push "route-ipv6 2000::/3"';
211
        } else {
212
            // there may be some routes specified, push those, and not the default
213
            foreach ($poolConfig->v('routes') as $route) {
214
                $routeIp = new IP($route);
215
                if (6 === $routeIp->getFamily()) {
216
                    // IPv6
217
                    $routeConfig[] = sprintf('push "route-ipv6 %s"', $routeIp->getAddressPrefix());
218
                } else {
219
                    // IPv4
220
                    $routeConfig[] = sprintf('push "route %s %s"', $routeIp->getAddress(), $routeIp->getNetmask());
221
                }
222
            }
223
        }
224
225
        return $routeConfig;
226
    }
227
228
    private static function getDns(PoolConfig $poolConfig)
229
    {
230
        // only push DNS if we are the default route
231
        if (!$poolConfig->v('defaultGateway')) {
232
            return [];
233
        }
234
235
        $dnsEntries = [];
236
        foreach ($poolConfig->v('dns') as $dnsAddress) {
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(PoolConfig $poolConfig)
247
    {
248
        if (!$poolConfig->v('clientToClient')) {
249
            return [];
250
        }
251
252
        $rangeIp = new IP($poolConfig->v('range'));
253
        $range6Ip = new IP($poolConfig->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