Passed
Push — 2.x ( ae412b...915907 )
by Terry
02:15
created

Iptables   A

Complexity

Total Complexity 37

Size/Duplication

Total Lines 326
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 105
c 5
b 0
f 0
dl 0
loc 326
rs 9.44
wmc 37

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A ip4status() 0 3 1
A ip4() 0 3 1
A ip6() 0 3 1
A ip6status() 0 3 1
A iptables() 0 40 5
A iptablesStatus() 0 30 3
B iptablesFormPost() 0 64 6
A checkFieldSubnet() 0 9 3
A checkFieldAction() 0 6 2
A iptablesFormPostVerification() 0 23 6
A checkFieldProtocol() 0 6 2
A checkFieldIp() 0 6 2
A checkFieldPort() 0 9 3
1
<?php
2
/*
3
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
declare(strict_types=1);
12
13
namespace Shieldon\Firewall\Panel;
14
15
use Psr\Http\Message\ResponseInterface;
16
use Shieldon\Firewall\Panel\BaseController;
17
use function Shieldon\Firewall\__;
18
use function Shieldon\Firewall\get_request;
19
20
use SplFileObject;
21
use ReflectionObject;
22
23
use function filter_var;
24
use function is_numeric;
25
use function in_array;
26
use function file;
27
use function file_put_contents;
28
use function sleep;
29
use function file_exists;
30
use function explode;
31
use function trim;
32
33
/**
34
 * The bridge between the Shieldon firewall and the Iptables firewall.
35
 */
36
class Iptables extends BaseController
37
{
38
    /**
39
     * Constructor
40
     */
41
    public function __construct() 
42
    {
43
        parent::__construct();
44
    }
45
46
    /**
47
     * The IPv4 table.
48
     *
49
     * @return ResponseInterface
50
     */
51
    public function ip4(): ResponseInterface
52
    {
53
        return $this->iptables('IPv4');
54
    }
55
56
    /**
57
     * The IPv6 table.
58
     *
59
     * @return ResponseInterface
60
     */
61
    public function ip6(): ResponseInterface
62
    {
63
        return $this->iptables('IPv6');
64
    }
65
66
    /**
67
     * The IPv4 table.
68
     *
69
     * @return ResponseInterface
70
     */
71
    public function ip4status(): ResponseInterface
72
    {
73
        return $this->iptablesStatus('IPv4');
74
    }
75
76
    /**
77
     * The IPv6 table.
78
     *
79
     * @return ResponseInterface
80
     */
81
    public function ip6status(): ResponseInterface
82
    {
83
        return $this->iptablesStatus('IPv6');
84
    }
85
86
    /**
87
     * System layer firwall - iptables
88
     * 
89
     * @param string $type The type of IP address.
90
     *
91
     * @return void
92
     */
93
    protected function iptables(string $type = 'IPv4'): ResponseInterface
94
    {
95
        $reflection = new ReflectionObject($this->kernel);
96
        $t = $reflection->getProperty('properties');
97
        $t->setAccessible(true);
98
        $properties = $t->getValue($this->kernel);
99
100
        $dir = $properties['iptables_watching_folder'];
101
102
        $commandLogFile = $dir . '/' . strtolower($type) . '_command.log';
103
        $iptablesQueueFile = $dir . '/iptables_queue.log';
104
105
        if ('POST' === get_request()->getMethod()) {
106
            $this->iptablesFormPost($type, $commandLogFile, $iptablesQueueFile);
107
        }
108
109
        $data = [];
110
        $ipCommand = '';
111
112
        if (file_exists($commandLogFile)) {
113
            $file = new SplFileObject($commandLogFile);
114
115
            $ipCommand = [];
116
117
            while (!$file->eof()) {
118
                $line = trim($file->fgets());
119
                $ipInfo = explode(',', $line);
120
121
                if (!empty($ipInfo[4])) {
122
                    $ipCommand[] = $ipInfo;
123
                }
124
            }
125
        }
126
127
        $data['ipCommand'] = $ipCommand;
128
        $data['type'] = $type;
129
130
        $data['title'] = __('panel', 'title_iptables_manager', 'Iptables Manager') . ' (' . $type . ')';
131
132
        return $this->renderPage('panel/iptables_manager', $data);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->renderPage...tables_manager', $data) returns the type Psr\Http\Message\ResponseInterface which is incompatible with the documented return type void.
Loading history...
133
    }
134
135
    /**
136
     * System layer firwall - iptables Status
137
     * iptables -L
138
     * 
139
     * @param string $type The type of IP address.
140
     *
141
     * @return ResponseInterface
142
     */
143
    protected function iptablesStatus(string $type = 'IPv4'): ResponseInterface
144
    {
145
        $data = [];
146
147
        $reflection = new ReflectionObject($this->kernel);
148
        $t = $reflection->getProperty('properties');
149
        $t->setAccessible(true);
150
        $properties = $t->getValue($this->kernel);
151
152
        $dir = $properties['iptables_watching_folder'];
153
154
        // The iptables log files.
155
        $ipStatusFile = $dir . '/ipv4_status.log';
156
157
        if ('IPv6' === $type) {
158
            $ipStatusFile = $dir . '/ipv6_status.log';
159
        }
160
        
161
        $ipStatus = '';
162
163
        if (file_exists($ipStatusFile)) {
164
            $ipStatus = file_get_contents($ipStatusFile);
165
        }
166
167
        $data['ipStatus'] = $ipStatus;
168
        $data['type'] = $type;
169
170
        $data['title'] = __('panel', 'title_iptables_status', 'Iptables Status') . ' (' . $type . ')';
171
172
        return $this->renderPage('panel/iptables_status', $data);
173
    }
174
175
    /**
176
     * Detect and handle form post action.
177
     *
178
     * @param string $type              IPv4 or IPv6
179
     * @param string $commandLogFile    The log file contains executed commands.
180
     * @param string $iptablesQueueFile The file contains the commands that wil 
181
     *                                  be executed by Iptables
182
     *
183
     * @return void
184
     */
185
    private function iptablesFormPost(string $type, string $commandLogFile, string $iptablesQueueFile): void
186
    {
187
        $postParams = get_request()->getParsedBody();
188
189
        if (!$this->iptablesFormPostVerification($postParams)) {
0 ignored issues
show
Bug introduced by
It seems like $postParams can also be of type null and object; however, parameter $postParams of Shieldon\Firewall\Panel\...sFormPostVerification() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

189
        if (!$this->iptablesFormPostVerification(/** @scrutinizer ignore-type */ $postParams)) {
Loading history...
190
            return;
191
        }
192
193
        $ip       = $postParams['ip'];
194
        $port     = $postParams['port'];
195
        $subnet   = $postParams['subnet'];
196
        $protocol = $postParams['protocol'];
197
        $action   = $postParams['action'];
198
        $cPort    = $postParams['port_custom'] ?? 'all';
199
200
        $ipv = substr($type, -1);
201
202
        if ($port === 'custom') {
203
            $port = $cPort;
204
        }
205
206
        /**
207
         * The process of add or remove command string from two files:
208
         * 
209
         * (1) The command file -
210
         *     This file is used on display the commands on the page 
211
         *     Iptables Manager.
212
         * (2) The queue file -
213
         *     This file is a bridge between Shieldon Firewall and Iptalbes.
214
         *     ipbales_bridge.sh will monitor this file, once commands come, 
215
         *     transforming the commands into Iptables syntax commands and 
216
         *     then execute the Iptables commands.
217
         */
218
        if ($postParams['remove'] === 'yes') {
219
220
            $originCommandString = "add,$ipv,$ip,$subnet,$port,$protocol,$action";
221
222
            // Delete line from the log file.
223
            $fileArr = file($commandLogFile);
224
    
225
            if (is_array($fileArr)) {
226
                $keyFound = array_search(trim($originCommandString), $fileArr);
227
                unset($fileArr[$keyFound]);
228
    
229
                $t = [];
230
                $i = 0;
231
                foreach ($fileArr as $f) {
232
                    $t[$i] = trim($f);
233
                    $i++;
234
                }
235
                file_put_contents($commandLogFile, implode(PHP_EOL, $t));
236
            }
237
    
238
            $applyCommand = "delete,$ipv,$ip,$subnet,$port,$protocol,$action";
239
            file_put_contents($iptablesQueueFile, $applyCommand . "\n", FILE_APPEND | LOCK_EX);
240
            sleep(1);
241
            return;
242
        }
243
244
        $applyCommand = "add,$ipv,$ip,$subnet,$port,$protocol,$action";
245
        file_put_contents($iptablesQueueFile, $applyCommand . "\n", FILE_APPEND | LOCK_EX);
246
247
        // Becase we need system cronjob done, and then the web page will show the actual results.
248
        sleep(10);
249
    }
250
251
    /**
252
     * Verify the form fields.
253
     *
254
     * @param array $postParams
255
     *
256
     * @return bool
257
     */
258
    private function iptablesFormPostVerification(array $postParams): bool
259
    {
260
        if (!$this->checkFieldIp($postParams)) {
261
            return false;
262
        }
263
264
        if (!$this->checkFieldPort($postParams)) {
265
            return false;
266
        }
267
268
        if (!$this->checkFieldSubnet($postParams)) {
269
            return false;
270
        }
271
272
        if (!$this->checkFieldProtocol($postParams)) {
273
            return false;
274
        }
275
276
        if (!$this->checkFieldAction($postParams)) {
277
            return false;
278
        }
279
280
        return true;
281
    }
282
283
    /**
284
     * Verify the form  field - Ip.
285
     *
286
     * @param array $postParams
287
     *
288
     * @return bool
289
     */
290
    private function checkFieldIp($postParams): bool
291
    {
292
        if (filter_var(explode('/', $postParams['ip'])[0], FILTER_VALIDATE_IP)) {
293
            return true;
294
        }
295
        return false;
296
    }
297
298
    /**
299
     * Verify the form field - Port.
300
     *
301
     * @param array $postParams
302
     *
303
     * @return bool
304
     */
305
    private function checkFieldPort($postParams): bool
306
    {
307
        if (
308
            is_numeric($postParams['port']) || 
309
            in_array($postParams['port'], ['all', 'custom'])
310
        ) {
311
            return true;
312
        }
313
        return false;
314
    }
315
316
    /**
317
     * Verify the form field - Subnet.
318
     *
319
     * @param array $postParams
320
     *
321
     * @return bool
322
     */
323
    private function checkFieldSubnet($postParams): bool
324
    {
325
        if (
326
            is_numeric($postParams['subnet']) || 
327
            $postParams['subnet'] === 'null'
328
        ) {
329
            return true;
330
        }
331
        return false;
332
    }
333
334
    /**
335
     * Verify the form field - Protocol.
336
     *
337
     * @param array $postParams
338
     *
339
     * @return bool
340
     */
341
    private function checkFieldProtocol($postParams): bool
342
    {
343
        if (in_array($postParams['protocol'], ['tcp', 'udp', 'all'])) {
344
            return true;
345
        }
346
        return false;
347
    }
348
349
    /**
350
     * Verify the form field - Action.
351
     *
352
     * @param array $postParams
353
     *
354
     * @return bool
355
     */
356
    private function checkFieldAction($postParams): bool
357
    {
358
        if (in_array($postParams['action'], ['allow', 'deny'])) {
359
            return true;
360
        }
361
        return false;
362
    }
363
}
364
365