mirror of
https://github.com/Rudd-O/qubes-network-server.git
synced 2025-03-01 14:22:35 +01:00
Bump version and prepare to package.
This commit is contained in:
parent
4e522c56a7
commit
efdda01dd0
1
Makefile
1
Makefile
@ -20,3 +20,4 @@ srpm: dist
|
|||||||
install:
|
install:
|
||||||
install -Dm 755 src/usr/bin/qvm-static-ip -t $(DESTDIR)/$(BINDIR)/
|
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/*.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
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
%define mybuildnumber %{?build_number}%{?!build_number:1}
|
%define mybuildnumber %{?build_number}%{?!build_number:1}
|
||||||
|
|
||||||
Name: qubes-network-server
|
Name: qubes-network-server
|
||||||
Version: 0.0.6
|
Version: 0.0.7
|
||||||
Release: %{mybuildnumber}%{?dist}
|
Release: %{mybuildnumber}%{?dist}
|
||||||
Summary: Turn your Qubes OS into a network server
|
Summary: Turn your Qubes OS into a network server
|
||||||
BuildArch: noarch
|
BuildArch: noarch
|
||||||
@ -38,6 +38,7 @@ make install DESTDIR=$RPM_BUILD_ROOT BINDIR=%{_bindir} LIBDIR=%{_libdir}
|
|||||||
%files
|
%files
|
||||||
%attr(0755, root, root) %{_bindir}/qvm-static-ip
|
%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/*.py*
|
||||||
|
%attr(0644, root, root) %{_libdir}/python2.7/site-packages/qubes/modules/qubes-appvm-firewall
|
||||||
%doc README.md TODO
|
%doc README.md TODO
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
@ -104,15 +104,13 @@ class QubesVm(OriginalQubesVm):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def start(self, verbose = False, preparing_dvm = False, start_guid = True,
|
def start_qrexec_daemon(self, verbose=False, notify_function=None):
|
||||||
notify_function = None, mem_required = None):
|
ret = OriginalQubesVm.start_qrexec_daemon(self, verbose=verbose, notify_function=notify_function)
|
||||||
if dry_run:
|
if self.type not in ['AppVM', 'HVM']:
|
||||||
return
|
self.deploy_appvm_firewall(verbose=verbose, notify_function=notify_function)
|
||||||
xid = OriginalQubesVm.start(self, verbose, preparing_dvm, start_guid, notify_function, mem_required)
|
self.adjust_proxy_arp(verbose=verbose, notify_function=notify_function)
|
||||||
if not preparing_dvm:
|
self.adjust_own_firewall_rules()
|
||||||
self.adjust_proxy_arp(verbose=verbose, notify_function=notify_function)
|
return ret
|
||||||
self.adjust_own_firewall_rules()
|
|
||||||
return xid
|
|
||||||
|
|
||||||
def unpause(self):
|
def unpause(self):
|
||||||
self.log.debug('unpause()')
|
self.log.debug('unpause()')
|
||||||
@ -397,4 +395,42 @@ class QubesVm(OriginalQubesVm):
|
|||||||
finally:
|
finally:
|
||||||
f.close()
|
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)
|
register_qubes_vm_class(QubesVm)
|
||||||
|
217
src/usr/lib64/python2.7/site-packages/qubes/modules/qubes-appvm-firewall
Executable file
217
src/usr/lib64/python2.7/site-packages/qubes/modules/qubes-appvm-firewall
Executable 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())
|
Loading…
x
Reference in New Issue
Block a user