How to patch kde-plasma/plasma-firewall-5.21.2 for UFW in Gentoo Linux with OpenRC
March 15, 2021 Leave a comment
Unfortunately plasma-firewall-5.21.2, a new Plasma frontend for firewalld and UFW, has been written only for Linux installations with systemd. However, I use OpenRC and syslog-ng in Gentoo Linux and wanted to try to get plasma-firewall to work on my laptop which uses UFW. I therefore set about patching plasma-firewall-5.21.2. I did not touch the firewalld part of plasma-firewall, as I do not use firewalld (and the plasma-firewall code for firewalld is more complicated). Below is what I did.
root # wget https://invent.kde.org/plasma/plasma-firewall/-/archive/Plasma/5.21/plasma-firewall-Plasma-5.21.tar.gz
root # tar -xzf plasma-firewall-Plasma-5.21.tar.gz
root # cp -pr plasma-firewall-Plasma-5.21 a
root # cp -pr plasma-firewall-Plasma-5.21 b
root # nano b/kcm/backends/ufw/ufwclient.cpp # Apply changes shown in Part 1 below.
root # nano b/kcm/backends/ufw/helper/helper.cpp # Apply changes shown in Part 2 below.
root # nano /usr/bin/print_ufw_messages # Create Bash script shown in Part 2 below.
root # chmod 755 /usr/bin/print_ufw_messages
root # nano b/kcm/backends/ufw/ufwlogmodel.cpp # Apply changes shown in Part 3 below.
root # diff -ruN a b > plasma-firewall-5.21.2-ufw.patch
root # mkdir -p /etc/portage/patches/kde-plasma/plasma-firewall-5.21.2
root # cp plasma-firewall-5.21.2-ufw.patch /etc/portage/patches/kde-plasma/plasma-firewall-5.21.2/
root # emerge -1v plasma-firewall
root # nano /etc/syslog-ng/syslog-ng.conf # Apply changes shown in Part 4 below.
You should now be able to use plasma-firewall for UFW in KDE Plasma’s ‘System Settings’ > ‘Firewall’ in the Network section, although I have not tried all the functions. Additionally, I believe there may be some outstanding bugs in the original 5.21.2 version of the Plasma module when using it with systemd.
Part 1
In /kcm/backends/ufw/ufwclient.cpp
change:
bool UfwClient::isCurrentlyLoaded() const { QProcess process; const QString name = "systemctl"; const QStringList args = {"status", "ufw"}; process.start(name, args); process.waitForFinished(); // systemctl returns 0 for status if the app is loaded, and 3 otherwise. qDebug() << "Ufw is loaded?" << (process.exitCode() == EXIT_SUCCESS); return process.exitCode() == EXIT_SUCCESS; }
to:
bool UfwClient::isCurrentlyLoaded() const { QProcess process; const QString name = "rc-service"; const QStringList args = {"--exists", "ufw"}; process.start(name, args); process.waitForFinished(); // "rc-service --exists" returns 0 if the app is loaded, and -1 otherwise. qDebug() << "Ufw is loaded?" << (process.exitCode() == EXIT_SUCCESS); return process.exitCode() == EXIT_SUCCESS; }
Part 2
In /kcm/backends/ufw/helper/helper.cpp
change:
QStringList getLogFromSystemd(const QString &lastLine) { QString program = "journalctl"; QStringList arguments {"-xb","-n", "100","-g", "UFW"}; QProcess myProcess; myProcess.start(program, arguments); myProcess.waitForFinished(); auto resultString = QString(myProcess.readAllStandardOutput()); auto resultList = resultString.split("\n"); // Example Line from Systemd: // Dec 06 17:42:45 tomatoland kernel: [UFW BLOCK] IN=wlan0 OUT= MAC= SRC=192.168.50.181 DST=224.0.0.252 LEN=56 TOS=0x00 // PREC=0x00 TTL=255 ID=52151 PROTO=UDP SPT=5355 DPT=5355 LEN=36 // We need to remove everything up to the space after ']'. QStringList result; for(const QString& line : resultList) { if (!lastLine.isEmpty() && line == lastLine) { result.clear(); continue; } result.append(line); } return result; }
to:
QStringList getLogFromSystemd(const QString &lastLine) { QString program = "print_ufw_messages"; QStringList arguments {"UFW", "100"}; QProcess myProcess; myProcess.start(program, arguments); myProcess.waitForFinished(); auto resultString = QString(myProcess.readAllStandardOutput()); auto resultList = resultString.split("\n"); // Example line from /var/log/messages populated by sylog-ng: // Mar 6 00:10:19 localhost kernel: [UFW BLOCK] IN=wlan0 OUT= MAC=00:12:5b:8a:83:6d:b7:2a:da:59:d4:10:09:00 SRC=192.168.1.27 // DST=192.168.1.139 LEN=52 TOS=0x00 PREC=0x00 TTL=64 ID=41659 DF PROTO=TCP SPT=445 DPT=52140 WINDOW=260 RES=0x00 ACK URGP=0 // We need to remove everything up to the space after ']'. QStringList result; for(const QString& line : resultList) { if (!lastLine.isEmpty() && line == lastLine) { result.clear(); continue; } result.append(line); } return result; }
where the program print_ufw_messages
is a user-created Bash script /usr/bin/print_ufw_messages
(-rwxr-xr-x root.root
) containing:
#!/bin/bash awk '{if (/localhost syslog-ng/ && /syslog-ng starting up/ && !/COMMAND/) {chunk=""} else {chunk=chunk $0 RS}} END {printf "%s", chunk}' /var/log/messages | grep "$1" | head -n "$2" | grep -v print_ufw_messages
Part 3
During my investigations into how to modify the plasma-firewall-5.21.2 source code, I discovered a bug in the source code. In /kcm/backends/ufw/ufwlogmodel.cpp
change:
for (const QString& key : {"IN", "SRC", "DST", "PROTO", "STP", "DPT"}) {
to:
for (const QString& key : {"IN", "SRC", "DST", "PROTO", "SPT", "DPT"}) {
i.e. “STP
” needs to be changed to “SPT
“.
Part 4
I am not sure if this makes a difference to plasma-firewall
(which was coded assuming systemd-journald is installed), but the default date format for messages in /var/log/messages
printed by syslog-ng
has only one digit in the day of the month when it is less than the 10th day of the month. For example:
Mar 9 03:09:39 clevow230ss syslog-ng[23735]: syslog-ng starting up; version='3.30.1'
However, systemd-journalctl always outputs two-digit days of the month, and I think (but am not certain) the following date format might be needed in order for the existing code in /kcm/backends/ufw/ufwlogmodel.cpp
to parse the syslog-ng
output correctly:
Mar 09 03:09:39 clevow230ss syslog-ng[23735]: syslog-ng starting up; version='3.30.1'
Therefore edit /etc/syslog-ng/syslog-ng.conf
and add a template:
template template_date_format { template("${MONTH_ABBREV} ${DAY} ${HOUR}:${MIN}:${SEC} ${HOST} ${MSGHDR}${MSG}\n"); template_escape(no); };
and change the line:
destination messages { file("/var/log/messages"); };
to:
destination messages { file("/var/log/messages" template(template_date_format)); };
Then restart syslog-ng:
root # rc-service syslog-ng restart
From now on the day of the month is always two digits (01
, 02
,…31
) in /var/log/messages
.