Score:0

PHP exec(iptables --help) works but exec(iptables --command) doesn't work

pe flag

In PHP 7.3.33 and Apache 2.4 I want to run iptables from PHP (web server enviroment) in order to block/unblock IPs, from command line, this works fine:

/usr/sbin/iptables --insert INPUT --source example.com --jump DROP

But if I try from PHP with this code:

exec('/usr/sbin/iptables --insert INPUT --source example.com --jump DROP', $return);
print_r($return);

I get a blank result and iptables rule is not added:

Array
(
)

However, iptables path seems correct and iptables runs, because the --help command returns content, so why doesn't the above command work then ?

exec('/usr/sbin/iptables --help', $return);
print_r($return);
Array
(
    [0] => iptables v1.8.4
    [1] => 
    [2] => Usage: iptables -[ACD] chain rule-specification [options]
    [3] =>  iptables -I chain [rulenum] rule-specification [options]
    [4] =>  iptables -R chain rulenum rule-specification [options]
    [5] =>  iptables -D chain rulenum [options]
    [6] =>  iptables -[LS] [chain [rulenum]] [options]
    [7] =>  iptables -[FZ] [chain] [options]
    [8] =>  iptables -[NX] chain
    [9] =>  iptables -E old-chain-name new-chain-name
    [10] =>     iptables -P chain target [options]
    [11] =>     iptables -h (print this help information)
    [12] => 
    [13] => Commands:
    [14] => Either long or short options are allowed.
    [15] =>   --append  -A chain        Append to chain
    [16] =>   --check   -C chain        Check for the existence of a rule
    [17] =>   --delete  -D chain        Delete matching rule from chain
    [18] =>   --delete  -D chain rulenum
    [19] =>                 Delete rule rulenum (1 = first) from chain
    [20] =>   --insert  -I chain [rulenum]
    [21] =>                 Insert in chain as rulenum (default 1=first)
    [22] =>   --replace -R chain rulenum
    [23] =>                 Replace rule rulenum (1 = first) in chain
    [24] =>   --list    -L [chain [rulenum]]
    [25] =>                 List the rules in a chain or all chains
    [26] =>   --list-rules -S [chain [rulenum]]
    [27] =>                 Print the rules in a chain or all chains
    [28] =>   --flush   -F [chain]      Delete all rules in  chain or all chains
    [29] =>   --zero    -Z [chain [rulenum]]
    [30] =>                 Zero counters in chain or all chains
    [31] =>   --new     -N chain        Create a new user-defined chain
    [32] =>   --delete-chain
    [33] =>          -X [chain]     Delete a user-defined chain
    [34] =>   --policy  -P chain target
    [35] =>                 Change policy on chain to target
    [36] =>   --rename-chain
    [37] =>          -E old-chain new-chain
    [38] =>                 Change chain name, (moving any references)
    [39] => Options:
    [40] =>     --ipv4  -4      Nothing (line is ignored by ip6tables-restore)
    [41] =>     --ipv6  -6      Error (line is ignored by iptables-restore)
    [42] => [!] --proto -p proto    protocol: by number or name, eg. `tcp'
    [43] => [!] --source    -s address[/mask][...]
    [44] =>                 source specification
    [45] => [!] --destination -d address[/mask][...]
    [46] =>                 destination specification
    [47] => [!] --in-interface -i input name[+]
    [48] =>                 network interface name ([+] for wildcard)
    [49] =>  --jump -j target
    [50] =>                 target for rule (may load target extension)
    [51] =>   --goto      -g chain
    [52] =>                    jump to chain with no return
    [53] =>   --match   -m match
    [54] =>                 extended match (may load extension)
    [55] =>   --numeric -n      numeric output of addresses and ports
    [56] => [!] --out-interface -o output name[+]
    [57] =>                 network interface name ([+] for wildcard)
    [58] =>   --table   -t table    table to manipulate (default: `filter')
    [59] =>   --verbose -v      verbose mode
    [60] =>   --wait    -w [seconds]    maximum wait to acquire xtables lock before give up
    [61] =>   --wait-interval -W [usecs]    wait time to try to acquire xtables lock
    [62] =>                 default is 1 second
    [63] =>   --line-numbers        print line numbers when listing
    [64] =>   --exact   -x      expand numbers (display exact values)
    [65] => [!] --fragment  -f      match second or further fragments only
    [66] =>   --modprobe=<command>      try to insert modules using this command
    [67] =>   --set-counters PKTS BYTES set the counter during insert/append
    [68] => [!] --version   -V      print package version.
)
Score:1
pt flag

As what user are you running the iptables command? If you run iptables as a non-root user, it will print an error message to stderr and return an error code, but when you write:

exec('/usr/sbin/iptables --insert INPUT --source example.com --jump DROP', $return);

You are (a) only capturing stdout and (b) you're not checking the error code. That code runs just fine for me if I run it from the command line as root. E.g, if in iptables.php I have this content:

<?php

exec('iptables --insert INPUT --source example.com --jump DROP', $output, $retval);
echo "return code: $retval\n";
echo "output:\n";
print_r($output);
?>

I get as output:

return code: 0
output:
Array
(
)

And I see the rule has been created:

# iptables -S INPUT
-P INPUT ACCEPT
-A INPUT -s 93.184.216.34/32 -j DROP

But if I run the same thing as a non-root user:

$ php iptables.php
iptables v1.8.8 (nf_tables): Could not fetch rule set generation id: Permission denied (you must be root)

return code: 4
output:
Array
(
)

Note that the error message was printed directly on the console and was not cpatured by PHP, so we see an empty output array -- but we do see an error return code. Depending on how you're running the code, you may not be able to see the error message (if you're running this via a web server, the error message may show up in the server error logs).

adrianTNT avatar
pe flag
yes, this was exactly the case and I remember `4` was the code returned but not the above message in web server enviroment (as you said) I thought if the `iptables --help` runs fine it has the right permissions, but no, iptables needs more permissions when adding/deleting rules.
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.