Bump version and prepare to package.

This commit is contained in:
Manuel Amador (Rudd-O) 2018-11-18 08:36:19 +00:00
parent 4e522c56a7
commit efdda01dd0
4 changed files with 265 additions and 10 deletions

View File

@ -20,3 +20,4 @@ srpm: dist
install:
install -Dm 755 src/usr/bin/qvm-static-ip -t $(DESTDIR)/$(BINDIR)/
install -Dm 644 src/usr/lib64/python2.7/site-packages/qubes/modules/*.py -t $(DESTDIR)/$(LIBDIR)/python2.7/site-packages/qubes/modules
install -Dm 644 src/usr/lib64/python2.7/site-packages/qubes/modules/qubes-appvm-firewall -t $(DESTDIR)/$(LIBDIR)/python2.7/site-packages/qubes/modules

View File

@ -3,7 +3,7 @@
%define mybuildnumber %{?build_number}%{?!build_number:1}
Name: qubes-network-server
Version: 0.0.6
Version: 0.0.7
Release: %{mybuildnumber}%{?dist}
Summary: Turn your Qubes OS into a network server
BuildArch: noarch
@ -38,6 +38,7 @@ make install DESTDIR=$RPM_BUILD_ROOT BINDIR=%{_bindir} LIBDIR=%{_libdir}
%files
%attr(0755, root, root) %{_bindir}/qvm-static-ip
%attr(0644, root, root) %{_libdir}/python2.7/site-packages/qubes/modules/*.py*
%attr(0644, root, root) %{_libdir}/python2.7/site-packages/qubes/modules/qubes-appvm-firewall
%doc README.md TODO
%changelog

View File

@ -104,15 +104,13 @@ class QubesVm(OriginalQubesVm):
else:
return None
def start(self, verbose = False, preparing_dvm = False, start_guid = True,
notify_function = None, mem_required = None):
if dry_run:
return
xid = OriginalQubesVm.start(self, verbose, preparing_dvm, start_guid, notify_function, mem_required)
if not preparing_dvm:
self.adjust_proxy_arp(verbose=verbose, notify_function=notify_function)
self.adjust_own_firewall_rules()
return xid
def start_qrexec_daemon(self, verbose=False, notify_function=None):
ret = OriginalQubesVm.start_qrexec_daemon(self, verbose=verbose, notify_function=notify_function)
if self.type not in ['AppVM', 'HVM']:
self.deploy_appvm_firewall(verbose=verbose, notify_function=notify_function)
self.adjust_proxy_arp(verbose=verbose, notify_function=notify_function)
self.adjust_own_firewall_rules()
return ret
def unpause(self):
self.log.debug('unpause()')
@ -397,4 +395,42 @@ class QubesVm(OriginalQubesVm):
finally:
f.close()
def deploy_appvm_firewall(self, verbose = False, notify_function=None):
def n(msg):
if notify_function:
notify_function("info", msg)
elif verbose:
print >> sys.stderr, "-->", msg
n("Deploying AppVM firewall...")
appvm_firewall_path = os.path.join(
os.path.dirname(__file__),
"qubes-appvm-firewall"
)
pill = textwrap.dedent(
"""
set -e
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT
cat > "$tmp" << "EOF"
%s
EOF
chmod +x "$tmp"
"$tmp" deploy
"""
) % open(appvm_firewall_path).read()
try:
p = self.run("bash", user="root", gui=False, wait=True, passio_popen=True, autostart=False)
p.stdin.write(pill)
p.stdin.close()
out = p.stdout.read()
retcode = p.wait()
except Exception as e:
n("Could not deploy the AppVM firewall on the VM: %s" % e)
if retcode != 0:
n("Could not deploy the AppVM firewall on the VM (return status %s): %s" % (retcode, out))
register_qubes_vm_class(QubesVm)

View File

@ -0,0 +1,217 @@
#!/usr/bin/env python
import collections
import logging
import os
import shutil
import subprocess
import sys
UNITPATH = "/usr/lib/systemd/system/qubes-appvm-firewall.service"
DEPPATH = "/run/fortress/qubes-appvm-firewall"
KEY = '/qubes-fortress-iptables-rules'
CHAIN = 'FORTRESS-INPUT'
class ReadError(Exception):
pass
def watch(key):
subprocess.check_call(['qubesdb-watch', key])
def read(key):
try:
return subprocess.check_output(['qubesdb-read', '-r', key])
except subprocess.CalledProcessError as e:
logging.error("error reading key %s: %s", key, e)
raise ReadError()
class Table(object):
header = None
chains = None
rules = None
footer = None
original_chains = None
original_rules = None
def __init__(self, text):
lines = text.splitlines(True)
self.header = ''
self.chains = collections.OrderedDict()
self.rules = []
self.footer = ''
mode = "header"
for line in lines:
if mode == "header":
if line.startswith(":"):
self.chains.update([line[1:].split(" ", 1)])
mode = "chains"
else:
self.header += line
elif mode == "chains":
if line.startswith("-"):
self.rules.append(line)
mode = "rules"
else:
self.chains.update([line[1:].split(" ", 1)])
elif mode == "rules":
if line.startswith("COMMIT"):
self.footer += line
mode = "footer"
else:
self.rules.append(line)
else: # mode == "footer":
self.footer += line
self.original_chains = collections.OrderedDict(self.chains.items())
self.original_rules = list(self.rules)
def __str__(self):
return self.render()
def render(self, old=False):
if old:
chains = self.original_chains
rules = self.original_rules
else:
chains = self.chains
rules = self.rules
return (
self.header
+ "".join(":%s %s" % x for x in chains.items())
+ "".join(rules)
+ self.footer
)
def dirty(self):
return self.render() != self.render(True)
def ensure_chain_present(self, name):
if name not in self.chains:
logging.info("Adding chain %s", name)
self.chains[name] = '- [0:0]\n'
def clear_chain(self, name):
for n, rule in reversed(list(enumerate(self.rules))):
if rule.startswith("-A %s " % name):
self.rules.pop(n)
def add_rule(self, rule, after_rule):
original_after_rule = after_rule
if not rule.endswith("\n"):
rule += "\n"
if not after_rule.endswith("\n"):
after_rule += "\n"
inserted = False
if rule in self.rules:
return
for n, exrule in enumerate(self.rules):
if exrule == after_rule:
logging.info("Inserting rule %s", rule.strip())
self.rules.insert(n + 1, rule)
inserted = True
break
if not inserted:
logging.error("Could not insert rule %s", rule.strip())
raise KeyError(original_after_rule)
def replace_rules(self, chain, ruletext):
for rule in ruletext.splitlines():
if not rule.strip(): continue
if not rule.startswith("-A %s " % chain):
raise ValueError(
"rule %s is not for chain %s" % (
rule.strip(),
chain,
)
)
self.ensure_chain_present(chain)
self.clear_chain(chain)
for rule in ruletext.splitlines():
if rule.startswith("-A %s " % chain):
self.rules.append(rule + "\n")
def commit(self):
if not self.dirty():
return
text = self.render()
cmd = ['iptables-restore']
p = subprocess.Popen(
cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
out, _ = p.communicate(text)
w = p.wait()
if w != 0:
logging.error("Rule changes commit failed with status %s: %s", w, out)
raise subprocess.CalledProcessError(w, cmd, out)
self.original_chains = collections.OrderedDict(self.chains.items())
self.original_rules = list(self.rules)
logging.info("Rule changes committed")
@classmethod
def filter_from_iptables(klass):
r = subprocess.check_output(['iptables-save', '-t', 'filter'])
t = klass(r)
return t
def deploy():
if not os.path.isdir(os.path.dirname(DEPPATH)):
os.makedirs(os.path.dirname(DEPPATH))
shutil.copyfile(__file__, DEPPATH)
os.chmod(DEPPATH, 0755)
service = '''[Unit]
Description=Qubes AppVM firewall updater
After=qubes-iptables.service qubes-firewall.service
Before=qubes-network.service network.target
[Service]
Type=simple
ExecStart=%s main
''' % DEPPATH
if not os.path.isfile(UNITPATH) or open(UNITPATH, "rb").read() != service:
open(UNITPATH, "wb").write(service)
subprocess.check_call(['systemctl', '--system', 'daemon-reload'])
subprocess.check_call(['systemctl', 'restart', os.path.basename(UNITPATH)])
def main():
logging.basicConfig(level=logging.INFO)
t = Table.filter_from_iptables()
t.ensure_chain_present(CHAIN)
t.add_rule('-A INPUT -j %s' % CHAIN, '-A INPUT -i lo -j ACCEPT')
try:
newrules = read(KEY)
t.replace_rules(CHAIN, newrules)
except ReadError:
# Key may not exist at this time.
logging.warning("Qubes DB key %s does not yet exist", KEY)
t.commit()
logging.info("Startup complete")
while True:
watch(KEY)
try:
newrules = read(KEY)
except ReadError:
# Key may have been deleted.
logging.warning("Qubes DB key %s could not be read", KEY)
continue
logging.info("Rule changes detected")
try:
t.replace_rules(CHAIN, newrules)
t.commit()
except Exception:
logging.exception("Rule changes could not be committed")
try:
cmd = sys.argv[1]
except IndexError:
cmd = 'main'
cmd = locals()[cmd]
sys.exit(cmd())