I am attempting to use Python3 to iterate through a list of IP addresses, and then block them using firewalld.
Note: I am a complete novice with Python, so please excuse any simple errors.
import subprocess
with open("ips.txt") as ipList:
ips = ipList.readlines()
for ip in ips:
process = subprocess.Popen(['firewall-cmd',
'--permanent',
'--add-rich-rule=\'rule family=\"ipv4\" source address=\"{0}\" reject\''.format(ip.rstrip())
])
I'm using format.rstrip to remove the line breaks after each IP address in the list.
When running the script I receive the following error;
root@mediaserver:~# python3 block.py
Error: INVALID_RULE: internal error in _lexer(): rule family="ipv4" source address="1.56.0.0/13" reject
Error: INVALID_RULE: internal error in _lexer(): rule family="ipv4" source address="1.48.0.0/15" reject
This error message iterates through all of the IP blocks in my list.
If I run the firewall-cmd outside of my script I do not receive any error messages and the rule is properly added.
root@mediaserver:~# firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="223.198.0.0/15" reject'
success
root@mediaserver:~# firewall-cmd --reload
success
root@mediaserver:~# firewall-cmd --zone=public --list-all
public (default, active)
interfaces: eth0
sources:
services: dhcpv6-client ssh
ports:
masquerade: no
forward-ports:
icmp-blocks:
rich rules:
rule family="ipv4" source address="223.198.0.0/15" reject
root@mediaserver:~# iptables -L IN_public_deny
Chain IN_public_deny (1 references)
target prot opt source destination
REJECT all -- 223.198.0.0/15 anywhere reject-with icmp-port-unreachable
root@mediaserver:~# which python3
/usr/bin/python3
root@mediaserver:~# firewall-cmd --version
0.3.7
I think the issue could be related to how I've escaped the characters in my python script, but as far as I can tell, they are being escaped correctly. If there is any additional debug info I could provide please let me know.
The solution was multi-part. We organized the formatting by declaring part of the command argument and using line continuation to split everything apart. This helps keep everything organized and reduces character escaping errors. Additionally, we switched from Popen to Run as Popen was excessive for this usage, and added the shell=True value to our subprocess.
import subprocess
with open("ips.txt") as ip_list:
ips = ip_list.readlines()
ips = (ip.strip() for ip in ips)
rules = ('rule family="ipv4" source address="{0}" reject'.format(ip) for ip in ips)
for rule in rules:
process = subprocess.run("firewall-cmd "
"--permanent "
" --add-rich-rule=\'{0}\'".format(rule),
shell=True)