Passed
Push — 2.x ( b197ca...b39b75 )
by Terry
01:57
created

Iptables::iptables()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 40
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 23
nc 4
nop 1
dl 0
loc 40
rs 9.2408
c 1
b 0
f 0
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)
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
            $ip       = $postParams['ip'];
191
            $port     = $postParams['port'];
192
            $subnet   = $postParams['subnet'];
193
            $protocol = $postParams['protocol'];
194
            $action   = $postParams['action'];
195
            $cPort    = $postParams['port_custom'] ?? 'all';
196
197
            $isRemoval = false;
198
199
            if (isset($postParams['remove']) && $postParams['remove'] === 'yes') {
200
                $isRemoval = true;
201
            }
202
203
            if ('custom' === $port) {
204
                $port = $cPort;
205
            }
206
207
            $ipv = '4';
208
209
            if ('IPv6' === $type) {
210
                $ipv = '6';
211
            }
212
213
            /**
214
             * The process of add or remove command string from two files:
215
             * 
216
             * (1) The command file -
217
             *     This file is used on display the commands on the page 
218
             *     Iptables Manager.
219
             * (2) The queue file -
220
             *     This file is a bridge between Shieldon Firewall and Iptalbes.
221
             *     ipbales_bridge.sh will monitor this file, once commands come, 
222
             *     transforming the commands into Iptables syntax commands and 
223
             *     then execute the Iptables commands.
224
             */
225
            if (!$isRemoval) {
226
                $originCommandString = "add,$ipv,$ip,$subnet,$port,$protocol,$action";
227
228
                // Delete line from the log file.
229
                $fileArr = file($commandLogFile);
230
231
                if (is_array($fileArr)) {
232
                    $keyFound = array_search(trim($originCommandString), $fileArr);
233
                    unset($fileArr[$keyFound]);
234
235
                    $t = [];
236
                    $i = 0;
237
                    foreach ($fileArr as $f) {
238
                        $t[$i] = trim($f);
239
                        $i++;
240
                    }
241
                    file_put_contents($commandLogFile, implode(PHP_EOL, $t));
242
                }
243
244
                $applyCommand = "delete,$ipv,$ip,$subnet,$port,$protocol,$action";
245
246
                file_put_contents($iptablesQueueFile, $applyCommand . "\n", FILE_APPEND | LOCK_EX);
247
248
                // Becase we need system cronjob done, and then the web page will show the actual results.
249
                sleep(10);
250
            } else {
251
                $applyCommand = "add,$ipv,$ip,$subnet,$port,$protocol,$action";
252
253
                file_put_contents($iptablesQueueFile, $applyCommand . "\n", FILE_APPEND | LOCK_EX);
254
                sleep(1);
255
            }
256
        }
257
    }
258
259
    /**
260
     * Verify the form.
261
     *
262
     * @param array $postParams
263
     *
264
     * @return bool
265
     */
266
    private function iptablesFormPostVerification(array $postParams): bool
267
    {
268
        $con1 = (
269
            isset($postParams['ip']) &&
270
            filter_var(explode('/', $postParams['ip'])[0], FILTER_VALIDATE_IP)
271
        );
272
273
        $con2 = (
274
            isset($postParams['port']) &&
275
            (
276
                is_numeric($postParams['port']) ||
277
                $postParams['port'] === 'all' ||
278
                $postParams['port'] === 'custom'
279
            )
280
        );
281
282
        $con3 = (
283
            isset($postParams['subnet']) && 
284
            (
285
                is_numeric($postParams['subnet']) || 
286
                $postParams['subnet'] === 'null'
287
            )
288
        );
289
290
        $con4 = (
291
            isset($postParams['protocol']) && 
292
            in_array($postParams['protocol'], ['tcp', 'udp', 'all'])
293
        );
294
295
        $con5 = (
296
            isset($postParams['action']) && 
297
            in_array($postParams['action'], ['allow', 'deny'])
298
        );
299
300
        if ($con1 && $con2 && $con3 && $con4 && $con5) {
301
            return true;
302
        }
303
304
        return false;
305
    }
306
}
307
308