Passed
Push — develop ( 3b062d...2b12c9 )
by Nikolay
23:37 queued 09:37
created

IptablesConf::updateFirewallRules()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 35
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 24
dl 0
loc 35
rs 8.9137
c 0
b 0
f 0
cc 6
nc 10
nop 0
1
<?php
2
/**
3
 * Copyright © MIKO LLC - All Rights Reserved
4
 * Unauthorized copying of this file, via any medium is strictly prohibited
5
 * Proprietary and confidential
6
 * Written by Alexey Portnov, 4 2020
7
 */
8
9
namespace MikoPBX\Core\System\Configs;
10
11
use MikoPBX\Common\Models\{FirewallRules, NetworkFilters};
12
use MikoPBX\Core\System\MikoPBXConfig;
13
use MikoPBX\Core\System\Util;
14
use Phalcon\Di\Injectable;
15
16
class IptablesConf extends Injectable
17
{
18
    private bool $firewall_enable;
19
    private Fail2BanConf $fail2ban;
20
21
    /**
22
     * Firewall constructor.
23
     */
24
    public function __construct()
25
    {
26
        $mikoPBXConfig = new MikoPBXConfig();
27
28
        $firewall_enable       = $mikoPBXConfig->getGeneralSettings('PBXFirewallEnabled');
29
        $this->firewall_enable = ($firewall_enable === '1');
30
31
        $this->fail2ban = new Fail2BanConf();
32
    }
33
34
    /**
35
     * Applies iptables settings and restart firewall
36
     */
37
    public static function reloadFirewall(): void
38
    {
39
        $pid_file = '/var/run/service_reload_firewall.pid';
40
        if (file_exists($pid_file)) {
41
            $old_pid = file_get_contents($pid_file);
42
            $process = Util::getPidOfProcess("^{$old_pid}");
43
            if ($process !== '') {
44
                return; // another restart process exists
45
            }
46
        }
47
        file_put_contents($pid_file, getmypid());
48
49
        $firewall = new self();
50
        $firewall->applyConfig();
51
        unlink($pid_file);
52
    }
53
54
    /**
55
     * Apples iptables settings
56
     **/
57
    public function applyConfig(): void
58
    {
59
        $this->fail2ban->fail2banStop();
60
        $this->dropAllRules();
61
        $this->fail2ban->fail2banMakeDirs();
62
63
        if ($this->firewall_enable) {
64
            $arr_command   = [];
65
            $arr_command[] = $this->getIptablesInputRule('', '-m conntrack --ctstate ESTABLISHED,RELATED');
66
            // Добавляем разрешения на сервисы.
67
            $this->addFirewallRules($arr_command);
68
            // Все остальное запрещаем.
69
            $arr_command[] = $this->getIptablesInputRule('', '', 'DROP');
70
71
            // Кастомизация правил firewall.
72
            $arr_commands_custom = [];
73
            $out                 = [];
74
            Util::fileWriteContent('/etc/firewall_additional', '');
75
76
            $catPath     = Util::which('cat');
77
            $grepPath    = Util::which('grep');
78
            $busyboxPath = Util::which('busybox');
79
            $awkPath     = Util::which('awk');
80
            Util::mwExec(
81
                "{$catPath} /etc/firewall_additional | {$grepPath} -v '|' | {$grepPath} -v '&'| {$grepPath} '^iptables' | {$busyboxPath} {$awkPath} -F ';' '{print $1}'",
82
                $arr_commands_custom
83
            );
84
            if (Util::isSystemctl()) {
85
                Util::mwMkdir('/etc/iptables');
86
                file_put_contents('/etc/iptables/iptables.mikopbx', implode("\n", $arr_command));
87
                file_put_contents(
88
                    '/etc/iptables/iptables.mikopbx',
89
                    "\n" . implode("\n", $arr_commands_custom),
90
                    FILE_APPEND
91
                );
92
                $systemctlPath = Util::which('systemctl');
93
                Util::mwExec("{$systemctlPath} restart mikopbx_iptables");
94
            } else {
95
                Util::mwExecCommands($arr_command, $out, 'firewall');
96
                Util::mwExecCommands($arr_commands_custom, $out, 'firewall_additional');
97
            }
98
        }
99
100
        // Setup fail2ban
101
        if ($this->fail2ban->fail2ban_enable) {
102
            $this->fail2ban->writeConfig();
103
            $this->fail2ban->fail2banStart();
104
        } else {
105
            $this->fail2ban->fail2banStop();
106
        }
107
    }
108
109
    /**
110
     *  Flush all firewall rules
111
     */
112
    private function dropAllRules(): void
113
    {
114
        $iptablesPath = Util::which('iptables');
115
        Util::mwExec("{$iptablesPath} -F");
116
        Util::mwExec("{$iptablesPath} -X");
117
    }
118
119
    /**
120
     * Makes iptables rule string
121
     *
122
     * @param string $dport
123
     * @param string $other_data
124
     * @param string $action
125
     *
126
     * @return string
127
     */
128
    private function getIptablesInputRule($dport = '', $other_data = '', $action = 'ACCEPT'): string
129
    {
130
        $data_port = '';
131
        if (trim($dport) !== '') {
132
            $data_port = '--dport ' . $dport;
133
        }
134
        $other_data = trim($other_data);
135
136
        return "iptables -A INPUT $other_data $data_port -j $action";
137
    }
138
139
    /**
140
     * Makes iptables rules
141
     *
142
     * @param $arr_command
143
     */
144
    private function addFirewallRules(&$arr_command): void
145
    {
146
        /** @var \MikoPBX\Common\Models\FirewallRules $result */
147
        /** @var \MikoPBX\Common\Models\FirewallRules $rule */
148
        $result = FirewallRules::find();
149
        foreach ($result as $rule) {
150
            if ($rule->portfrom !== $rule->portto && trim($rule->portto) !== '') {
151
                $port = "{$rule->portfrom}:{$rule->portto}";
152
            } else {
153
                $port = $rule->portfrom;
154
            }
155
            /** @var \MikoPBX\Common\Models\NetworkFilters $network_filter */
156
            $network_filter = NetworkFilters::findFirst($rule->networkfilterid);
157
            if ($network_filter === null) {
158
                Util::sysLogMsg('Firewall', "network_filter_id not found {$rule->networkfilterid}");
159
                continue;
160
            }
161
            if ('0.0.0.0/0' === $network_filter->permit && $rule->action !== 'allow') {
162
                continue;
163
            }
164
            $other_data = "-p {$rule->protocol}";
165
            $other_data .= ($network_filter === null) ? '' : ' -s ' . $network_filter->permit;
166
            if ($rule->protocol === 'icmp') {
167
                $port       = '';
168
                $other_data .= ' --icmp-type echo-request';
169
            }
170
171
            $action        = ($rule->action === 'allow') ? 'ACCEPT' : 'DROP';
172
            $arr_command[] = $this->getIptablesInputRule($port, $other_data, $action);
173
        }
174
        // Allow all local connections
175
        $arr_command[] = $this->getIptablesInputRule('', '-s 127.0.0.1 ', 'ACCEPT');
176
    }
177
178
    /**
179
     * Updates firewall rules according to default template
180
     *
181
     */
182
    public static function updateFirewallRules(): void
183
    {
184
        // Store current conditions
185
        $currentRules  = [];
186
        $firewallRules = FirewallRules::find();
187
        foreach ($firewallRules as $firewallRule) {
188
            $currentRules[$firewallRule->networkfilterid][$firewallRule->category] =
189
                        [
190
                            'action'      => $firewallRule->action,
191
                            'description' => $firewallRule->description
192
                        ];
193
        }
194
        // Delete outdated records
195
        $firewallRules->delete();
196
197
        $defaultRules   = FirewallRules::getDefaultRules();
198
        $networkFilters = NetworkFilters::find();
199
200
        foreach ($networkFilters as $networkFilter) {
201
            foreach ($defaultRules as $key => $value) {
202
                foreach ($value['rules'] as $rule) {
203
                    $newRule                  = new FirewallRules();
204
                    $newRule->networkfilterid = $networkFilter->id;
205
                    $newRule->protocol        = $rule['protocol'];
206
                    $newRule->portfrom        = $rule['portfrom'];
207
                    $newRule->portto          = $rule['portto'];
208
                    $newRule->category        = $key;
209
210
                    if (array_key_exists($key, $currentRules[$networkFilter->id])) {
211
                        $newRule->action = $currentRules[$networkFilter->id][$key]['action'];
212
                    } else {
213
                        $newRule->action = 'block';
214
                    }
215
                    $newRule->description = $currentRules[$networkFilter->id][$key]['description'];
216
                    $newRule->save();
217
                }
218
            }
219
        }
220
    }
221
}
222