20
20
#
21
21
22
22
from __future__ import annotations
23
+
24
+ import subprocess
25
+ import sys
26
+
23
27
import dbus
24
28
import qubesdb
25
29
from typing import List
@@ -85,7 +89,7 @@ def install_firewall_rules(dns):
85
89
qubesdb_dns .append (IPv4Address (ns_maybe .decode ("ascii" , "strict" )))
86
90
except (UnicodeDecodeError , ValueError ):
87
91
pass
88
- res = [
92
+ preamble = [
89
93
'add table ip qubes' ,
90
94
# Add the chain so that the subsequent delete will work. If the chain already
91
95
# exists this is a harmless no-op.
@@ -95,6 +99,8 @@ def install_firewall_rules(dns):
95
99
# atomic operation, so there is no period where neither chain is present or
96
100
# where both are present.
97
101
'delete chain ip qubes dnat-dns' ,
102
+ ]
103
+ rules = [
98
104
'table ip qubes {' ,
99
105
'chain dnat-dns {' ,
100
106
'type nat hook prerouting priority dstnat; policy accept;' ,
@@ -105,7 +111,7 @@ def install_firewall_rules(dns):
105
111
# Or maybe user wants to enforce DNS-Over-HTTPS.
106
112
# Drop IPv4 DNS requests to qubesdb_dns addresses.
107
113
for vm_nameserver in qubesdb_dns :
108
- res += [
114
+ rules += [
109
115
f"ip daddr { vm_nameserver } udp dport 53 drop" ,
110
116
f"ip daddr { vm_nameserver } tcp dport 53 drop" ,
111
117
]
@@ -115,12 +121,25 @@ def install_firewall_rules(dns):
115
121
dns_resolved = dns_resolved + dns_resolved
116
122
for vm_nameserver , dest in zip (qubesdb_dns , dns_resolved ):
117
123
dns_ = str (dest )
118
- res += [
124
+ rules += [
119
125
f"ip daddr { vm_nameserver } udp dport 53 dnat to { dns_ } " ,
120
126
f"ip daddr { vm_nameserver } tcp dport 53 dnat to { dns_ } " ,
121
127
]
122
- res += ["}\n }\n " ]
123
- os .execvp ("nft" , ("nft" , "--" , "\n " .join (res )))
128
+ rules += ["}" , "}" ]
129
+
130
+ # check if new rules are the same as the old ones - if so, don't reload
131
+ # and return that info via exit code
132
+ try :
133
+ old_rules = subprocess .check_output (
134
+ ["nft" , "list" , "chain" , "ip" , "qubes" , "dnat-dns" ]).decode ().splitlines ()
135
+ except subprocess .CalledProcessError :
136
+ old_rules = []
137
+ old_rules = [line .strip () for line in old_rules ]
138
+
139
+ if old_rules == rules :
140
+ sys .exit (100 )
141
+
142
+ os .execvp ("nft" , ("nft" , "--" , "\n " .join (preamble + rules )))
124
143
125
144
if __name__ == '__main__' :
126
145
install_firewall_rules (get_dns_resolved ())
0 commit comments