Iptables::iptablesFormPostVerification()   A
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 11
c 3
b 0
f 0
dl 0
loc 23
ccs 11
cts 11
cp 1
rs 9.2222
cc 6
nc 6
nop 1
crap 6
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
 * php version 7.1.0
11
 *
12
 * @category  Web-security
13
 * @package   Shieldon
14
 * @author    Terry Lin <[email protected]>
15
 * @copyright 2019 terrylinooo
16
 * @license   https://github.com/terrylinooo/shieldon/blob/2.x/LICENSE MIT
17
 * @link      https://github.com/terrylinooo/shieldon
18
 * @see       https://shieldon.io
19
 */
20
21
declare(strict_types=1);
22
23
namespace Shieldon\Firewall\Panel;
24
25
use Psr\Http\Message\ResponseInterface;
26
use Shieldon\Firewall\Panel\BaseController;
27
use SplFileObject;
28
use ReflectionObject;
29
use function Shieldon\Firewall\__;
30
use function Shieldon\Firewall\get_request;
31
use function explode;
32
use function file;
33
use function file_exists;
34
use function file_put_contents;
35
use function filter_var;
36
use function in_array;
37
use function is_array;
38
use function is_numeric;
39
use function sleep;
40
use function trim;
41
42
/**
43
 * The bridge between the Shieldon firewall and the iptables firewall.
44
 */
45
class Iptables extends BaseController
46
{
47
    /**
48
     *   Public methods       | Desctiotion
49
     *  ----------------------|---------------------------------------------
50
     *   ip4                  | The page for iptables (IPv4) management.
51
     *   ip6                  | The page for iptables (IPv6) management.
52
     *   ip4status            | The page for dispalying iptables (IPv4) status.
53
     *   ip6status            | The page for dispalying iptables (IPv6) status.
54
     *  ----------------------|---------------------------------------------
55
     */
56
57
    /**
58
     * Constructor
59
     */
60 8
    public function __construct()
61
    {
62 8
        parent::__construct();
63
    }
64
65
    /**
66
     * The IPv4 table.
67
     *
68
     * @return ResponseInterface
69
     */
70 7
    public function ip4(): ResponseInterface
71
    {
72 7
        return $this->iptables('IPv4');
73
    }
74
75
    /**
76
     * The IPv6 table.
77
     *
78
     * @return ResponseInterface
79
     */
80 1
    public function ip6(): ResponseInterface
81
    {
82 1
        return $this->iptables('IPv6');
83
    }
84
85
    /**
86
     * The IPv4 table.
87
     *
88
     * @return ResponseInterface
89
     */
90 1
    public function ip4status(): ResponseInterface
91
    {
92 1
        return $this->iptablesStatus('IPv4');
93
    }
94
95
    /**
96
     * The IPv6 table.
97
     *
98
     * @return ResponseInterface
99
     */
100 1
    public function ip6status(): ResponseInterface
101
    {
102 1
        return $this->iptablesStatus('IPv6');
103
    }
104
105
    /**
106
     * System layer firwall - iptables
107
     *
108
     * @param string $type The type of IP address.
109
     *
110
     * @return ResponseInterface
111
     */
112 7
    protected function iptables(string $type = 'IPv4'): ResponseInterface
113
    {
114 7
        $reflection = new ReflectionObject($this->kernel);
115 7
        $t = $reflection->getProperty('properties');
116 7
        $t->setAccessible(true);
117 7
        $properties = $t->getValue($this->kernel);
118
119 7
        $dir = $properties['iptables_watching_folder'];
120
121 7
        $commandLogFile = $dir . '/' . strtolower($type) . '_command.log';
122 7
        $iptablesQueueFile = $dir . '/iptables_queue.log';
123
124 7
        if ('POST' === get_request()->getMethod()) {
125 6
            $this->iptablesFormPost($type, $commandLogFile, $iptablesQueueFile);
126
        }
127
128 7
        $data = [];
129 7
        $ipCommand = '';
130
131 7
        if (file_exists($commandLogFile)) {
132 7
            $file = new SplFileObject($commandLogFile);
133
134 7
            $ipCommand = [];
135
136 7
            while (!$file->eof()) {
137 7
                $line = trim($file->fgets());
138 7
                $ipInfo = explode(',', $line);
139
140
                // If the column amount is at least 6 maning that the data is
141
                // existed so that we can use it.
142 7
                if (!empty($ipInfo[6])) {
143 6
                    $ipCommand[] = $ipInfo;
144
                }
145
            }
146
        }
147
148 7
        $data['ipCommand'] = $ipCommand;
149 7
        $data['type'] = $type;
150
151 7
        $data['title'] = __('panel', 'title_iptables_manager', 'IpTables Manager') . ' (' . $type . ')';
152
153 7
        return $this->renderPage('panel/iptables_manager', $data);
154
    }
155
156
    /**
157
     * System layer firewall - iptables Status
158
     * iptables -L
159
     *
160
     * @param string $type The type of IP address.
161
     *
162
     * @return ResponseInterface
163
     */
164 1
    protected function iptablesStatus(string $type = 'IPv4'): ResponseInterface
165
    {
166 1
        $data = [];
167
168 1
        $reflection = new ReflectionObject($this->kernel);
169 1
        $t = $reflection->getProperty('properties');
170 1
        $t->setAccessible(true);
171 1
        $properties = $t->getValue($this->kernel);
172
173 1
        $dir = $properties['iptables_watching_folder'];
174
175
        // The iptables log files.
176 1
        $ipStatusFile = $dir . '/ipv4_status.log';
177
178 1
        if ('IPv6' === $type) {
179 1
            $ipStatusFile = $dir . '/ipv6_status.log';
180
        }
181
        
182 1
        $ipStatus = '';
183
184 1
        if (file_exists($ipStatusFile)) {
185 1
            $ipStatus = file_get_contents($ipStatusFile);
186
        }
187
188 1
        $data['ipStatus'] = $ipStatus;
189 1
        $data['type'] = $type;
190
191 1
        $data['title'] = __('panel', 'title_iptables_status', 'IpTables Status') . ' (' . $type . ')';
192
193 1
        return $this->renderPage('panel/iptables_status', $data);
194
    }
195
196
    /**
197
     * Detect and handle form post action.
198
     *
199
     * @param string $type              IPv4 or IPv6
200
     * @param string $commandLogFile    The log file contains executed commands.
201
     * @param string $iptablesQueueFile The file contains the commands that wil
202
     *                                  be executed by iptables
203
     *
204
     * @return void
205
     */
206 6
    private function iptablesFormPost(string $type, string $commandLogFile, string $iptablesQueueFile): void
207
    {
208 6
        $postParams = get_request()->getParsedBody();
209
210 6
        if (!is_array($postParams)) {
211
            // @codeCoverageIgnoreStart
212
            return;
213
            // @codeCoverageIgnoreEnd
214
        }
215
216 6
        if (!$this->iptablesFormPostVerification($postParams)) {
217 5
            return;
218
        }
219
220 1
        $ip       = $postParams['ip'];
221 1
        $port     = $postParams['port'];
222 1
        $subnet   = $postParams['subnet'];
223 1
        $protocol = $postParams['protocol'];
224 1
        $action   = $postParams['action'];
225 1
        $cPort    = $postParams['port_custom'] ?? 'all';
226
227 1
        $ipv = substr($type, -1);
228
229 1
        if ($port === 'custom') {
230 1
            $port = $cPort;
231
        }
232
233
        /**
234
         * The process of add or remove command string from two files:
235
         *
236
         * (1) The command file -
237
         *     This file is used on display the commands on the page
238
         *     iptables Manager.
239
         * (2) The queue file -
240
         *     This file is a bridge between Shieldon Firewall and Iptalbes.
241
         *     ipbales_bridge.sh will monitor this file, once commands come,
242
         *     transforming the commands into iptables syntax commands and
243
         *     then execute the iptables commands.
244
         */
245 1
        if ($postParams['remove'] === 'yes') {
246
            $originCommandString = "add,$ipv,$ip,$subnet,$port,$protocol,$action";
247 1
248
            // Delete line from the log file.
249
            $fileArr = file($commandLogFile);
250 1
    
251
            if (is_array($fileArr)) {
0 ignored issues
show
introduced by
The condition is_array($fileArr) is always true.
Loading history...
252 1
                $keyFound = array_search(trim($originCommandString), $fileArr);
253 1
254
                // Remove the row from the file content.
255
                unset($fileArr[$keyFound]);
256 1
    
257
                $t = [];
258 1
                $i = 0;
259 1
                foreach ($fileArr as $f) {
260 1
                    $t[$i] = trim($f);
261 1
                    $i++;
262 1
                }
263
264
                // Save the filtered content.
265
                file_put_contents($commandLogFile, implode(PHP_EOL, $t));
266 1
            }
267
    
268
            // Pass the command to the iptables bridge file to remove the rule
269
            // which is in the Iptable rule list.
270
            $applyCommand = "delete,$ipv,$ip,$subnet,$port,$protocol,$action";
271 1
            file_put_contents($iptablesQueueFile, $applyCommand . "\n", FILE_APPEND | LOCK_EX);
272 1
            sleep(1);
273 1
274
            // Finish this action, return.
275
            return;
276 1
        }
277
278
        $applyCommand = "add,$ipv,$ip,$subnet,$port,$protocol,$action";
279 1
        file_put_contents($iptablesQueueFile, $applyCommand . "\n", FILE_APPEND | LOCK_EX);
280 1
281
        // Becase we need system cronjob done, and then the web page will show the actual results.
282
        sleep(10);
283 1
    }
284
285
    /**
286
     * Verify the form fields.
287
     *
288
     * @param array $postParams The PSR-7 variable of $_POST
289
     *
290
     * @return bool
291
     */
292
    private function iptablesFormPostVerification(array $postParams): bool
293 6
    {
294
        if (!$this->checkFieldIp($postParams)) {
295 6
            return false;
296 1
        }
297
298
        if (!$this->checkFieldPort($postParams)) {
299 5
            return false;
300 1
        }
301
302
        if (!$this->checkFieldSubnet($postParams)) {
303 4
            return false;
304 1
        }
305
306
        if (!$this->checkFieldProtocol($postParams)) {
307 3
            return false;
308 1
        }
309
310
        if (!$this->checkFieldAction($postParams)) {
311 2
            return false;
312 1
        }
313
314
        return true;
315 1
    }
316
317
    /**
318
     * Verify the form  field - Ip.
319
     *
320
     * @param array $postParams The PSR-7 variable of $_POST
321
     *
322
     * @return bool
323
     */
324
    private function checkFieldIp($postParams): bool
325 6
    {
326
        if (filter_var(explode('/', $postParams['ip'])[0], FILTER_VALIDATE_IP)) {
327 6
            return true;
328 5
        }
329
        return false;
330 1
    }
331
332
    /**
333
     * Verify the form field - Port.
334
     *
335
     * @param array $postParams The PSR-7 variable of $_POST
336
     *
337
     * @return bool
338
     */
339
    private function checkFieldPort($postParams): bool
340 5
    {
341
        if (is_numeric($postParams['port']) ||
342
            in_array($postParams['port'], ['all', 'custom'])
343 5
        ) {
344 5
            return true;
345
        }
346 4
        return false;
347
    }
348 1
349
    /**
350
     * Verify the form field - Subnet.
351
     *
352
     * @param array $postParams The PSR-7 variable of $_POST
353
     *
354
     * @return bool
355
     */
356
    private function checkFieldSubnet($postParams): bool
357
    {
358 4
        if (is_numeric($postParams['subnet']) ||
359
            $postParams['subnet'] === 'null'
360
        ) {
361 4
            return true;
362 4
        }
363
        return false;
364 3
    }
365
366 1
    /**
367
     * Verify the form field - Protocol.
368
     *
369
     * @param array $postParams The PSR-7 variable of $_POST
370
     *
371
     * @return bool
372
     */
373
    private function checkFieldProtocol($postParams): bool
374
    {
375
        if (in_array($postParams['protocol'], ['tcp', 'udp', 'all'])) {
376 3
            return true;
377
        }
378 3
        return false;
379 2
    }
380
381 1
    /**
382
     * Verify the form field - Action.
383
     *
384
     * @param array $postParams The PSR-7 variable of $_POST
385
     *
386
     * @return bool
387
     */
388
    private function checkFieldAction($postParams): bool
389
    {
390
        if (in_array($postParams['action'], ['allow', 'deny'])) {
391 2
            return true;
392
        }
393 2
        return false;
394 1
    }
395
}
396