mirror of
				https://github.com/Rudd-O/qubes-network-server.git
				synced 2025-10-31 03:29:03 +01:00 
			
		
		
		
	Initial commit.
This commit is contained in:
		
						commit
						7ad6b81670
					
				
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | |||||||
|  | *.pyc | ||||||
|  | *~ | ||||||
|  | *.tar.gz | ||||||
|  | *.rpm | ||||||
							
								
								
									
										20
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | |||||||
|  | BINDIR=/usr/bin | ||||||
|  | LIBDIR=/usr/lib64 | ||||||
|  | DESTDIR= | ||||||
|  | 
 | ||||||
|  | clean: | ||||||
|  | 	find -name '*.pyc' -o -name '*~' -print0 | xargs -0 rm -f | ||||||
|  | 	rm -f *.tar.gz *.rpm | ||||||
|  | 
 | ||||||
|  | dist: clean | ||||||
|  | 	DIR=qubes-network-server-`awk '/^Version:/ {print $$2}' qubes-network-server.spec` && FILENAME=$$DIR.tar.gz && tar cvzf "$$FILENAME" --exclude "$$FILENAME" --exclude .git --exclude .gitignore -X .gitignore --transform="s|^|$$DIR/|" --show-transformed * | ||||||
|  | 
 | ||||||
|  | rpm: dist | ||||||
|  | 	T=`mktemp -d` && rpmbuild --define "_topdir $$T" -ta qubes-network-server-`awk '/^Version:/ {print $$2}' qubes-network-server.spec`.tar.gz || { rm -rf "$$T"; exit 1; } && mv "$$T"/RPMS/*/* "$$T"/SRPMS/* . || { rm -rf "$$T"; exit 1; } && rm -rf "$$T" | ||||||
|  | 
 | ||||||
|  | srpm: dist | ||||||
|  | 	T=`mktemp -d` && rpmbuild --define "_topdir $$T" -ts qubes-network-server-`awk '/^Version:/ {print $$2}' qubes-network-server.spec`.tar.gz || { rm -rf "$$T"; exit 1; } && mv "$$T"/SRPMS/* . || { rm -rf "$$T"; exit 1; } && rm -rf "$$T" | ||||||
|  | 
 | ||||||
|  | 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 | ||||||
							
								
								
									
										121
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,121 @@ | |||||||
|  | #Qubes network server | ||||||
|  | 
 | ||||||
|  | This software lets you turn your Qubes OS machine into a network server. | ||||||
|  | 
 | ||||||
|  | ##Enhanced networking model | ||||||
|  | 
 | ||||||
|  | The traditional Qubes OS networking model contemplates a client-only | ||||||
|  | use case.  User VMs (AppVMs or StandaloneVMs) are attached to ProxyVMs, | ||||||
|  | which give the user control over outbound connections taking place from | ||||||
|  | user VMs.  ProxyVMs in turn attach to NetVMs, which provide outbound | ||||||
|  | connectivity for ProxyVMs and other user VMs alike. | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | No provision is made for running a server in a virtualized environment, | ||||||
|  | such that the server's ports are accessible by (a) other VMs (b) machines | ||||||
|  | beyond the perimeter of the NetVM.  To the extent that such a thing is | ||||||
|  | possible, it is only possible by painstakingly maintaining firewall rules | ||||||
|  | for multiple VMs, which need to carefully override the existing firewall | ||||||
|  | rules, and require careful thought not to open the system to unexpected | ||||||
|  | attack vectors.  The Qubes OS user interface provides no help either. | ||||||
|  | 
 | ||||||
|  | Qubes network server changes all that. | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | With the Qubes network server software, it becomes possible to make | ||||||
|  | network servers in user VMs available to other machines, be them | ||||||
|  | peer VMs in the same Qubes OS system or machines connected to | ||||||
|  | a physical link shared by a NetVM.  You get actual, full, GUI control | ||||||
|  | over network traffic, both exiting the VM and entering the VM, with | ||||||
|  | exactly the same Qubes OS user experience you are used to. | ||||||
|  | 
 | ||||||
|  | This is all, of course, opt-in, so the standard Qubes OS network security | ||||||
|  | model remains in effect until you decide to share network servers. | ||||||
|  | 
 | ||||||
|  | ##Usage | ||||||
|  | 
 | ||||||
|  | Once installed (see below), usage of the software is straightforward. | ||||||
|  | 
 | ||||||
|  | To illustrate, we'll proceed with an example VM `httpserver` which | ||||||
|  | is meant to be a standalone VM that contains files, being served by | ||||||
|  | a running HTTP server (port 80) within it.  This VM is attached to | ||||||
|  | a ProxyVM `server-proxy`, which in turn is connected to a NetVM | ||||||
|  | `sys-net`, with IP address `192.168.1.4` on a local network | ||||||
|  | `192.168.1.0/24`.  Our goal will be to make `httpserver` accessible | ||||||
|  | to your laptop on the same physical network, which we'll assume has | ||||||
|  | IP address `192.168.1.8`. | ||||||
|  | 
 | ||||||
|  | ###Assign a static address to `httpserver` | ||||||
|  | 
 | ||||||
|  | First step is to assign an address — let's make it `192.168.1.6` — | ||||||
|  | to `httpserver`: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | qvm-static-ip -s httpserver static_ip 192.168.1.6 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ###Restart `httpserver` | ||||||
|  | 
 | ||||||
|  | Due to limitations in this release of the code, you must power off | ||||||
|  | the `httpserver` VM and then power it back on. | ||||||
|  | 
 | ||||||
|  | ###Set firewall rules on `httpserver` | ||||||
|  | 
 | ||||||
|  | Launch the Qubes Manager preferences window for the `httpserver` VM. | ||||||
|  | Go to the *Firewall rules* tab and select *Deny network access | ||||||
|  | except...* from the top area.  *Allow ICMP traffic* but deny | ||||||
|  | *DNS queries*. | ||||||
|  | 
 | ||||||
|  | Finally, add a new network rule (use the plus button).  On the | ||||||
|  | *Address* box, you're going to write `from-192.168.1.8`.  Select | ||||||
|  | the *TCP* protocol, and type `80` on the *Service* box.  Click OK. | ||||||
|  | 
 | ||||||
|  | Note the trick here — any address whose text begins with | ||||||
|  | `from-` gets transformed into an incoming traffic rule, as opposed | ||||||
|  | to the standard rules that control only outbound traffic. | ||||||
|  | 
 | ||||||
|  | Back on the main dialog, click *OK*. | ||||||
|  | 
 | ||||||
|  | ###That's it! | ||||||
|  | 
 | ||||||
|  | You'll be able to ping, from your laptop, the address `192.168.1.6`. | ||||||
|  | You will also be able to point your browser at it, and it will | ||||||
|  | render the served pages from the HTTP server running directly on | ||||||
|  | `httpserver`. | ||||||
|  | 
 | ||||||
|  | Save from ICMP, no other port or protocol will be allowed for | ||||||
|  | inbound connections. | ||||||
|  | 
 | ||||||
|  | You'll also note that `httpserver` has received no permission to | ||||||
|  | engage in any sort of outbound network traffic. | ||||||
|  | 
 | ||||||
|  | ##Disabling network server | ||||||
|  | 
 | ||||||
|  | Two-step process.  Step one: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | qvm-static-ip -s httpserver static_ip none | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Step two: power the VM off, then start it back up. | ||||||
|  | 
 | ||||||
|  | ##Installation | ||||||
|  | 
 | ||||||
|  | Installation is extremely easy: | ||||||
|  | 
 | ||||||
|  | * Prepare an RPM with the `make rpm` command on the local | ||||||
|  |   directory of your clone. | ||||||
|  | * Copy the prepared RPM to the dom0 of your Qubes OS | ||||||
|  |   machine. | ||||||
|  | * Install the RPM with `rpm -ivh`. | ||||||
|  | 
 | ||||||
|  | Qubes OS does not provide any facility to copy files from | ||||||
|  | a VM to the dom0.  To work around this, you can use `qvm-run`: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | qvm-run --pass-io vmwiththerpm 'cat /home/user/path/to/qubes-network-server*rpm' > qns.rpm | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | This lets you fetch the RPM file to the dom0, and save it as `qns.rpm`. | ||||||
							
								
								
									
										
											BIN
										
									
								
								doc/Qubes network server model.dia
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								doc/Qubes network server model.dia
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								doc/Qubes network server model.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								doc/Qubes network server model.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 9.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								doc/Standard Qubes OS network model.dia
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								doc/Standard Qubes OS network model.dia
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								doc/Standard Qubes OS network model.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								doc/Standard Qubes OS network model.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 10 KiB | 
							
								
								
									
										36
									
								
								qubes-network-server.spec
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								qubes-network-server.spec
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | |||||||
|  | %define debug_package %{nil} | ||||||
|  | 
 | ||||||
|  | Name:           qubes-network-server | ||||||
|  | Version:        0.0.1 | ||||||
|  | Release:        1%{?dist} | ||||||
|  | Summary:        Turn your Qubes OS into a network server | ||||||
|  | 
 | ||||||
|  | License:        GPLv3+ | ||||||
|  | URL:            https://github.com/Rudd-O/qubes-network-server | ||||||
|  | Source0:	Source0: https://github.com/Rudd-O/%{name}/archive/{%version}.tar.gz#/%{name}-%{version}.tar.gz | ||||||
|  | 
 | ||||||
|  | BuildRequires:  go | ||||||
|  | 
 | ||||||
|  | %description | ||||||
|  | This package lets you turn your Qubes OS into a network server. | ||||||
|  | 
 | ||||||
|  | %prep | ||||||
|  | %setup -q | ||||||
|  | 
 | ||||||
|  | %build | ||||||
|  | # variables must be kept in sync with install | ||||||
|  | make DESTDIR=$RPM_BUILD_ROOT BINDIR=%{_bindir} LIBDIR=%{_libdir} | ||||||
|  | 
 | ||||||
|  | %install | ||||||
|  | rm -rf $RPM_BUILD_ROOT | ||||||
|  | # variables must be kept in sync with build | ||||||
|  | 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* | ||||||
|  | %doc README.md | ||||||
|  | 
 | ||||||
|  | %changelog | ||||||
|  | * Tue Oct 11 2016 Manuel Amador (Rudd-O) <rudd-o@rudd-o.com> | ||||||
|  | - Initial release | ||||||
							
								
								
									
										170
									
								
								src/usr/bin/qvm-static-ip
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								src/usr/bin/qvm-static-ip
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,170 @@ | |||||||
|  | #!/usr/bin/python2 | ||||||
|  | # -*- encoding: utf8 -*- | ||||||
|  | # | ||||||
|  | # The Qubes OS Project, http://www.qubes-os.org | ||||||
|  | # | ||||||
|  | # Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com> | ||||||
|  | # | ||||||
|  | # This program is free software; you can redistribute it and/or | ||||||
|  | # modify it under the terms of the GNU General Public License | ||||||
|  | # as published by the Free Software Foundation; either version 2 | ||||||
|  | # of the License, or (at your option) any later version. | ||||||
|  | # | ||||||
|  | # This program is distributed in the hope that it will be useful, | ||||||
|  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | # GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | # You should have received a copy of the GNU General Public License | ||||||
|  | # along with this program; if not, write to the Free Software | ||||||
|  | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | ||||||
|  | # | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | from qubes.qubes import QubesVmCollection | ||||||
|  | from qubes.qubes import QubesVmLabels | ||||||
|  | from qubes.qubes import QubesHost | ||||||
|  | from qubes.qubes import system_path | ||||||
|  | from optparse import OptionParser | ||||||
|  | import subprocess | ||||||
|  | import os | ||||||
|  | import sys | ||||||
|  | import re | ||||||
|  | from qubes.qubes import vmm | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def do_list(vm): | ||||||
|  |     label_width = 19 | ||||||
|  |     fmt="{{0:<{0}}}: {{1}}".format(label_width) | ||||||
|  | 
 | ||||||
|  |     print fmt.format ("name", vm.name) | ||||||
|  |     if hasattr(vm, 'static_ip'): | ||||||
|  |         print fmt.format("static_ip", str(vm.static_ip) if vm.static_ip else "unset") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def do_get(vms, vm, prop): | ||||||
|  |     if not hasattr(vm, prop): | ||||||
|  |         print >>sys.stderr, "VM '{}' has no attribute '{}'".format(vm.name, | ||||||
|  |                                                                    prop) | ||||||
|  |         return | ||||||
|  |     if getattr(vm, prop, None) is None: | ||||||
|  |         # not set or set to None | ||||||
|  |         return | ||||||
|  |     else: | ||||||
|  |         print str(getattr(vm, prop)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def set_static_ip(vms, vm, args): | ||||||
|  |     if len (args) != 1: | ||||||
|  |         print >> sys.stderr, "Missing value ('static_ip')!" | ||||||
|  |         return False | ||||||
|  | 
 | ||||||
|  |     arg = args[0] | ||||||
|  |     if not arg or arg == "none" or arg == "None" or arg == "unset": | ||||||
|  |         arg = None | ||||||
|  |     # TODO(ruddo): validate the argument! | ||||||
|  | 
 | ||||||
|  |     setattr(vm, "static_ip", arg) | ||||||
|  |     return True | ||||||
|  | 
 | ||||||
|  | properties = { | ||||||
|  |     "static_ip": set_static_ip, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def do_set(vms, vm, property, args): | ||||||
|  |     if property not in properties.keys(): | ||||||
|  |         print >> sys.stderr, "ERROR: Wrong property name: '{0}'".format(property) | ||||||
|  |         return False | ||||||
|  | 
 | ||||||
|  |     if not hasattr(vm, property): | ||||||
|  |         print >> sys.stderr, "ERROR: Property '{0}' not available for this VM".format(property) | ||||||
|  |         return False | ||||||
|  | 
 | ||||||
|  |     try: | ||||||
|  |         return properties[property](vms, vm, args) | ||||||
|  |     except Exception as err: | ||||||
|  |         print >> sys.stderr, "ERROR: %s" % str(err) | ||||||
|  |         return False | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def main(): | ||||||
|  |     usage = "usage: %prog -l [options] <vm-name>\n"\ | ||||||
|  |             "usage: %prog -g [options] <vm-name> <property>\n"\ | ||||||
|  |             "usage: %prog -s [options] <vm-name> <property> [...]\n"\ | ||||||
|  |             "List/set networking-related per-VM properties." | ||||||
|  | 
 | ||||||
|  |     parser = OptionParser (usage) | ||||||
|  |     parser.add_option("-l", "--list", action="store_true", dest="do_list", | ||||||
|  |                       default=False) | ||||||
|  |     parser.add_option("-s", "--set", action="store_true", dest="do_set", | ||||||
|  |                       default=False) | ||||||
|  |     parser.add_option ("-g", "--get", action="store_true", dest="do_get", | ||||||
|  |                        default=False) | ||||||
|  |     parser.add_option("--force-root", action="store_true", dest="force_root", | ||||||
|  |                       default=False, | ||||||
|  |                       help="Force to run, even with root privileges") | ||||||
|  |     parser.add_option ("--offline-mode", dest="offline_mode", | ||||||
|  |                        action="store_true", default=False, | ||||||
|  |                        help="Offline mode") | ||||||
|  | 
 | ||||||
|  |     (options, args) = parser.parse_args () | ||||||
|  |     if (len (args) < 1): | ||||||
|  |         parser.error ("You must provide at least the vmname!") | ||||||
|  | 
 | ||||||
|  |     vmname = args[0] | ||||||
|  | 
 | ||||||
|  |     if hasattr(os, "geteuid") and os.geteuid() == 0: | ||||||
|  |         if not options.force_root: | ||||||
|  |             print >> sys.stderr, "*** Running this tool as root is strongly discouraged, this will lead you in permissions problems." | ||||||
|  |             print >> sys.stderr, "Retry as unprivileged user." | ||||||
|  |             print >> sys.stderr, "... or use --force-root to continue anyway." | ||||||
|  |             exit(1) | ||||||
|  | 
 | ||||||
|  |     if options.do_list + options.do_set + options.do_get > 1: | ||||||
|  |         print >> sys.stderr, "You can provide at most one of -l, -g and -s at " \ | ||||||
|  |                              "the same time!" | ||||||
|  |         exit(1) | ||||||
|  | 
 | ||||||
|  |     if options.offline_mode: | ||||||
|  |         vmm.offline_mode = True | ||||||
|  | 
 | ||||||
|  |     if options.do_set: | ||||||
|  |         qvm_collection = QubesVmCollection() | ||||||
|  |         qvm_collection.lock_db_for_writing() | ||||||
|  |         qvm_collection.load() | ||||||
|  |     else: | ||||||
|  |         qvm_collection = QubesVmCollection() | ||||||
|  |         qvm_collection.lock_db_for_reading() | ||||||
|  |         qvm_collection.load() | ||||||
|  |         qvm_collection.unlock_db() | ||||||
|  | 
 | ||||||
|  |     vm = qvm_collection.get_vm_by_name(vmname) | ||||||
|  |     if vm is None or vm.qid not in qvm_collection: | ||||||
|  |         print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(vmname) | ||||||
|  |         exit(1) | ||||||
|  | 
 | ||||||
|  |     if options.do_set: | ||||||
|  |         if len (args) < 2: | ||||||
|  |             print >> sys.stderr, "You must specify the property you wish to set..." | ||||||
|  |             print >> sys.stderr, "Available properties:" | ||||||
|  |             for p in properties.keys(): | ||||||
|  |                 if hasattr(vm, p): | ||||||
|  |                     print >> sys.stderr, "--> '{0}'".format(p) | ||||||
|  |             exit (1) | ||||||
|  | 
 | ||||||
|  |         property = args[1] | ||||||
|  |         if do_set(qvm_collection, vm, property, args[2:]): | ||||||
|  |             qvm_collection.save() | ||||||
|  |             qvm_collection.unlock_db() | ||||||
|  |         else: | ||||||
|  |             qvm_collection.unlock_db() | ||||||
|  |             exit(1) | ||||||
|  | 
 | ||||||
|  |     elif options.do_get or len(args) == 2: | ||||||
|  |         do_get(qvm_collection, vm, args[1]) | ||||||
|  |     else: | ||||||
|  |         # do_list | ||||||
|  |         do_list(vm) | ||||||
|  | 
 | ||||||
|  | main() | ||||||
| @ -0,0 +1,401 @@ | |||||||
|  | #!/usr/bin/python2 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # | ||||||
|  | # The Qubes OS Project, http://www.qubes-os.org | ||||||
|  | # | ||||||
|  | # Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com> | ||||||
|  | # Copyright (C) 2013  Marek Marczykowski <marmarek@invisiblethingslab.com> | ||||||
|  | # | ||||||
|  | # This program is free software; you can redistribute it and/or | ||||||
|  | # modify it under the terms of the GNU General Public License | ||||||
|  | # as published by the Free Software Foundation; either version 2 | ||||||
|  | # of the License, or (at your option) any later version. | ||||||
|  | # | ||||||
|  | # This program is distributed in the hope that it will be useful, | ||||||
|  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | # GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | # You should have received a copy of the GNU General Public License | ||||||
|  | # along with this program; if not, write to the Free Software | ||||||
|  | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | ||||||
|  | # | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | import datetime | ||||||
|  | import base64 | ||||||
|  | import hashlib | ||||||
|  | import fcntl | ||||||
|  | import logging | ||||||
|  | import lxml.etree | ||||||
|  | import os | ||||||
|  | import pipes | ||||||
|  | import re | ||||||
|  | import shutil | ||||||
|  | import subprocess | ||||||
|  | import sys | ||||||
|  | import textwrap | ||||||
|  | import time | ||||||
|  | import uuid | ||||||
|  | import xml.parsers.expat | ||||||
|  | import signal | ||||||
|  | from qubes import qmemman | ||||||
|  | from qubes import qmemman_algo | ||||||
|  | import libvirt | ||||||
|  | 
 | ||||||
|  | from qubes.qubes import QubesException | ||||||
|  | from qubes.qubes import QubesVm as OriginalQubesVm | ||||||
|  | from qubes.qubes import register_qubes_vm_class | ||||||
|  | from qubes.qubes import dry_run | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | fw_encap = textwrap.dedent(""" | ||||||
|  |     mkdir -p /run/fortress/firewall | ||||||
|  |     f=$(mktemp --tmpdir=/run/fortress/firewall) | ||||||
|  |     cat > "$f" | ||||||
|  |     chmod +x "$f" | ||||||
|  |     bash -e "$f" | ||||||
|  |     ret=$? | ||||||
|  |     rm -f "$f" | ||||||
|  |     exit $ret | ||||||
|  | """) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def locked(programtext): | ||||||
|  |     if not programtext.strip(): | ||||||
|  |         return programtext | ||||||
|  |     return "(\nflock 200\n" + programtext + "\n) 200>/var/run/xen-hotplug/vif-lock\n" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def logger(programtext): | ||||||
|  |     if not programtext.strip(): | ||||||
|  |         return programtext | ||||||
|  |     return "exec 1> >(logger -s -t fortress) 2>&1\n" + programtext | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class QubesVm(OriginalQubesVm): | ||||||
|  | 
 | ||||||
|  |     def get_attrs_config(self): | ||||||
|  |         attrs = OriginalQubesVm.get_attrs_config(self) | ||||||
|  |         attrs["static_ip"] = { | ||||||
|  |             "attr": "static_ip", | ||||||
|  |             "default": None, | ||||||
|  |             "order": 70, | ||||||
|  |             "save": lambda: str(getattr(self, "static_ip")) if getattr(self, "static_ip") is not None else 'none' | ||||||
|  |         } | ||||||
|  |         return attrs | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def ip(self): | ||||||
|  |         if self.netvm is not None: | ||||||
|  |             if getattr(self, "static_ip") is not None: | ||||||
|  |                 return getattr(self, "static_ip") | ||||||
|  |             return self.netvm.get_ip_for_vm(self.qid) | ||||||
|  |         else: | ||||||
|  |             return None | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def netmask(self): | ||||||
|  |         if self.netvm is not None: | ||||||
|  |             if getattr(self, "static_ip") is not None: | ||||||
|  |                 # Netmasks for VMs that have a static IP are always host-only. | ||||||
|  |                 return "255.255.255.255" | ||||||
|  |             return self.netvm.netmask | ||||||
|  |         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 unpause(self): | ||||||
|  |         self.log.debug('unpause()') | ||||||
|  |         if dry_run: | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         if not self.is_paused(): | ||||||
|  |             raise QubesException ("VM not paused!") | ||||||
|  | 
 | ||||||
|  |         self.libvirt_domain.resume() | ||||||
|  |         self.adjust_proxy_arp() | ||||||
|  |         self.adjust_own_firewall_rules() | ||||||
|  | 
 | ||||||
|  |     def attach_network(self, verbose = False, wait = True, netvm = None): | ||||||
|  |         self.log.debug('attach_network(netvm={!r})'.format(netvm)) | ||||||
|  |         if dry_run: | ||||||
|  |             return | ||||||
|  |         ret = OriginalQubesVm.attach_network(self, verbose, wait, netvm) | ||||||
|  |         self.adjust_proxy_arp(verbose) | ||||||
|  |         return ret | ||||||
|  | 
 | ||||||
|  |     def adjust_proxy_arp(self, verbose = False, notify_function=None): | ||||||
|  | 
 | ||||||
|  |         def collect_downstream_vms(vm, vif): | ||||||
|  |             if not hasattr(vm, "connected_vms"): | ||||||
|  |                 return list() | ||||||
|  |             vms_below_me = list(vm.connected_vms.values()) | ||||||
|  |             vms_below_me = [(vm, vif if vif else vm.vif) for vm in vms_below_me] | ||||||
|  |             for v, vif in vms_below_me: | ||||||
|  |                 vms_below_me.extend(collect_downstream_vms(v, vif)) | ||||||
|  |             return vms_below_me | ||||||
|  | 
 | ||||||
|  |         def addroute(ip, dev, netmask): | ||||||
|  |             # This function adds routes and proxy ARP entries for the IP pointed at the | ||||||
|  |             # device that the VM (IP) is behind. | ||||||
|  |             dev = dev.replace("+", "0") | ||||||
|  |             return "\n".join([ | ||||||
|  |                 "if ! ip route | grep -qF %s\\ dev\\ %s ; then" % (pipes.quote(ip), pipes.quote(dev)), | ||||||
|  |                 "ip route replace %s/%s dev %s metric 20001" % (pipes.quote(ip), pipes.quote(netmask), pipes.quote(dev)), | ||||||
|  |                 "fi", | ||||||
|  |                 "echo 1 > /proc/sys/net/ipv4/conf/%s/forwarding" % (pipes.quote(dev),), | ||||||
|  |                 "echo 1 > /proc/sys/net/ipv4/conf/%s/proxy_arp" % (pipes.quote(dev),), | ||||||
|  |                 "for dev in `ip link | awk -F ':' '/^[0-9]+: (eth|en|wl)/ { print $2 }'`", | ||||||
|  |                 "do", | ||||||
|  |                 "    ip neigh add proxy %s dev $dev" % (pipes.quote(ip),), | ||||||
|  |                 "done", | ||||||
|  |             ]) | ||||||
|  | 
 | ||||||
|  |         class addfwrule(object): | ||||||
|  |             rules = None | ||||||
|  |             addrule = textwrap.dedent(""" | ||||||
|  |             declare -A savedrules | ||||||
|  |             addrule() { | ||||||
|  |                 local table="$1" | ||||||
|  |                 local chain="$2" | ||||||
|  |                 local rule="$3" | ||||||
|  |                 local before="$4" | ||||||
|  | 
 | ||||||
|  |                 if [ "${savedrules[$table]}" == "" ] ; then | ||||||
|  |                     savedrules["$table"]=$(iptables-save -t "$table") | ||||||
|  |                 fi | ||||||
|  | 
 | ||||||
|  |                 if echo "${savedrules[$table]}" | grep -q :"${chain}" ; then | ||||||
|  |                     true | ||||||
|  |                 else | ||||||
|  |                     savedrules["$table"]=$( | ||||||
|  |                         echo "${savedrules[$table]}" | while read x | ||||||
|  |                         do | ||||||
|  |                             echo "$x" | ||||||
|  |                             if [ "$x" == '*'"$table" ] | ||||||
|  |                             then | ||||||
|  |                                 echo "${table}: new chain ${chain}" >&2 | ||||||
|  |                                 echo ":${chain} - [0:0]" | ||||||
|  |                             fi | ||||||
|  |                         done | ||||||
|  |                     ) | ||||||
|  |                 fi | ||||||
|  | 
 | ||||||
|  |                 if [ "x$before" == "x" ] ; then | ||||||
|  |                     before=COMMIT | ||||||
|  |                 elif [ "x$before" == "xbeginning" ] ; then | ||||||
|  |                     before=beginning | ||||||
|  |                 else | ||||||
|  |                     before="-A $chain $before" | ||||||
|  |                 fi | ||||||
|  | 
 | ||||||
|  |                 if [ "$before" != "beginning" ] && echo "${savedrules[$table]}" | grep -qF -- "-A $chain $rule" ; then | ||||||
|  |                     return | ||||||
|  |                 fi | ||||||
|  | 
 | ||||||
|  |                 local echoed=false | ||||||
|  |                 savedrules["$table"]=$( | ||||||
|  |                     echo "${savedrules[$table]}" | while read x | ||||||
|  |                     do | ||||||
|  |                         if [ "beginning" == "$before" -a "$echoed" == "false" ] && echo "$x" | grep -q '^-A ' | ||||||
|  |                         then | ||||||
|  |                             echo "${table}: adding rule -A ${chain} ${rule} to the beginning" >&2 | ||||||
|  |                             echo "-A $chain $rule" | ||||||
|  |                             echoed=true | ||||||
|  |                         elif [ "$x" == "$before" ] | ||||||
|  |                         then | ||||||
|  |                             echo "${table}: adding rule -A ${chain} ${rule} before ${before}" >&2 | ||||||
|  |                             echo "-A $chain $rule" | ||||||
|  |                         fi | ||||||
|  |                         if [ "beginning" == "$before" -a "$x" == "-A $chain $rule" ] | ||||||
|  |                         then | ||||||
|  |                             true | ||||||
|  |                         else | ||||||
|  |                             echo "$x" | ||||||
|  |                         fi | ||||||
|  |                     done | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |             flushrules() { | ||||||
|  |                 local table="$1" | ||||||
|  |                 local chain="$2" | ||||||
|  | 
 | ||||||
|  |                 if [ "${savedrules[$table]}" == "" ] ; then | ||||||
|  |                     savedrules["$table"]=$(iptables-save -t "$table") | ||||||
|  |                 fi | ||||||
|  | 
 | ||||||
|  |                 savedrules["$table"]=$( | ||||||
|  |                     echo "${savedrules[$table]}" | while read x | ||||||
|  |                     do | ||||||
|  |                         if echo "$x" | grep -q "^-A $chain " ; then | ||||||
|  |                             echo "${table}: flushing rule $x" >&2 | ||||||
|  |                         else | ||||||
|  |                             echo "$x" | ||||||
|  |                         fi | ||||||
|  |                     done | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |             addfwrules() { | ||||||
|  |                 # This function creates the FORTRESS-ALLOW-FORWARD filter chain | ||||||
|  |                 # and adds rules permitting forwarding of traffic | ||||||
|  |                 # sent by the VM and destined to the VM. | ||||||
|  |                 local ipnetmask="$1" | ||||||
|  |                 addrule filter FORWARD "-j FORTRESS-ALLOW-FORWARD" "-i vif+ -o vif+ -j DROP" | ||||||
|  |                 addrule filter FORTRESS-ALLOW-FORWARD "-s $ipnetmask -j ACCEPT" | ||||||
|  |                 addrule filter FORTRESS-ALLOW-FORWARD "-d $ipnetmask -j ACCEPT" | ||||||
|  |             } | ||||||
|  |             addprrules() { | ||||||
|  |                 # This function creates the FORTRESS-SKIP-MASQ nat chain | ||||||
|  |                 # and the FORTRESS-ANTISPOOF raw chain | ||||||
|  |                 # and adds rules defeating masquerading and anti-spoofing | ||||||
|  |                 # for the IP (machine) so long as it comes from / goes to | ||||||
|  |                 # the VIF that the machine is behind. | ||||||
|  |                 local ipnetmask="$1" | ||||||
|  |                 local vif="$2" | ||||||
|  |                 addrule nat POSTROUTING "-j FORTRESS-SKIP-MASQ" "-j MASQUERADE" | ||||||
|  |                 addrule nat FORTRESS-SKIP-MASQ "-s $ipnetmask -j ACCEPT" | ||||||
|  |                 addrule nat FORTRESS-SKIP-MASQ "-d $ipnetmask -j ACCEPT" | ||||||
|  |                 addrule raw PREROUTING "-j FORTRESS-ANTISPOOF" beginning | ||||||
|  |                 addrule raw FORTRESS-ANTISPOOF "-s $ipnetmask -j ACCEPT" | ||||||
|  |             } | ||||||
|  |             commitrules() { | ||||||
|  |                 for table in "${!savedrules[@]}" ; do | ||||||
|  |                     echo "${savedrules[$table]}" | iptables-restore -T "$table" | ||||||
|  |                 done | ||||||
|  |             } | ||||||
|  |             flushrules filter FORTRESS-ALLOW-FORWARD | ||||||
|  |             flushrules nat FORTRESS-SKIP-MASQ | ||||||
|  |             flushrules raw FORTRESS-ANTISPOOF | ||||||
|  |             """) | ||||||
|  | 
 | ||||||
|  |             def _add(self, ip, dev, netmask, typ): | ||||||
|  |                 netmask = sum([bin(int(x)).count('1') for x in netmask.split('.')]) | ||||||
|  |                 dev = dev.replace("+", "0") | ||||||
|  |                 text = self.addrule | ||||||
|  |                 if typ == "forward": | ||||||
|  |                     text += "addfwrules %s/%s\n" % (pipes.quote(ip), netmask) | ||||||
|  |                 elif typ == "postrouting": | ||||||
|  |                     text += "addprrules %s/%s %s\n" % (pipes.quote(ip), netmask, pipes.quote(dev)) | ||||||
|  |                 self.addrule = "" | ||||||
|  |                 if not self.rules: | ||||||
|  |                     self.rules = [] | ||||||
|  |                 self.rules.append(text) | ||||||
|  | 
 | ||||||
|  |             def addfw(self, ip, dev, netmask): | ||||||
|  |                 return self._add(ip, dev, netmask, "forward") | ||||||
|  | 
 | ||||||
|  |             def addpr(self, ip, dev, netmask): | ||||||
|  |                 return self._add(ip, dev, netmask, "postrouting") | ||||||
|  | 
 | ||||||
|  |             def commit(self): | ||||||
|  |                 if not self.rules: | ||||||
|  |                     return self.addrule + "\ncommitrules\n" | ||||||
|  |                 return "\n".join(self.rules) + "\ncommitrules\n" | ||||||
|  | 
 | ||||||
|  |         programs = [] | ||||||
|  |         staticipvms = [] | ||||||
|  |         ruler = addfwrule() | ||||||
|  | 
 | ||||||
|  |         # For every VM downstream of mine. | ||||||
|  |         for vm, vif in collect_downstream_vms(self, None): | ||||||
|  |             # If the VM is running, and it has an associated VIF | ||||||
|  |             # and it has a static IP: | ||||||
|  |             if vm.static_ip and vif and vm.is_running(): | ||||||
|  |                 staticipvms.append(vm.name) | ||||||
|  |                 # Add ip neighs of and routes to the VM. | ||||||
|  |                 # pointed at the VIF that the VM is behind. | ||||||
|  |                 programs.append(addroute(vm.ip, vif, vm.netmask)) | ||||||
|  |                 # Add prerouting and postrouting rules for the VM | ||||||
|  |                 # that defeat masquerading and anti-spoofing. | ||||||
|  |                 ruler.addpr(vm.ip, vif, vm.netmask) | ||||||
|  |                 # If I am a NetVM, then, additionally. | ||||||
|  |                 if self.type == "NetVM": | ||||||
|  |                     # Add filter rules for the VM | ||||||
|  |                     # that allow it to communicate with other VMs. | ||||||
|  |                     ruler.addfw(vm.ip, vif, vm.netmask) | ||||||
|  |         if ruler.commit(): | ||||||
|  |             programs.append(ruler.commit()) | ||||||
|  | 
 | ||||||
|  |         if not programs: | ||||||
|  |             pass | ||||||
|  |         elif not self.is_running() or self.is_paused(): | ||||||
|  |             msg = "Not running routing programs on %s (VM is paused or off)" % (self.name,) | ||||||
|  |             if notify_function: | ||||||
|  |                 notify_function("info", msg) | ||||||
|  |             elif verbose: | ||||||
|  |                 print >> sys.stderr, "-->", msg | ||||||
|  |         else: | ||||||
|  |             programs = logger(locked("\n".join(programs))) | ||||||
|  |             if not staticipvms: | ||||||
|  |                 msg = "Enabling preliminary routing configuration on %s" % (self.name,) | ||||||
|  |             else: | ||||||
|  |                 msg = "Enabling routing of %s on %s" % (", ".join(staticipvms), self.name) | ||||||
|  |             if notify_function: | ||||||
|  |                 notify_function("info", msg) | ||||||
|  |             elif verbose: | ||||||
|  |                 print >> sys.stderr, "-->", msg | ||||||
|  |                 # for x in programs.splitlines(False): | ||||||
|  |                 #     print >> sys.stderr, "---->", x | ||||||
|  |             p = self.run(fw_encap, user="root", gui=False, wait=True, passio_popen=True, autostart=False) | ||||||
|  |             p.stdin.write(programs) | ||||||
|  |             p.stdin.close() | ||||||
|  |             p.stdout.read() | ||||||
|  |             retcode = p.wait() | ||||||
|  |             if retcode: | ||||||
|  |                 msg = "Routing commands on %s failed with return code %s" % (self.name, retcode) | ||||||
|  |                 if notify_function: | ||||||
|  |                     notify_function("error", msg) | ||||||
|  |                 elif verbose: | ||||||
|  |                     print >> sys.stderr, "-->", msg | ||||||
|  | 
 | ||||||
|  |         if self.netvm: | ||||||
|  |             self.netvm.adjust_proxy_arp( | ||||||
|  |                 verbose=verbose, | ||||||
|  |                 notify_function=notify_function | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |     def adjust_own_firewall_rules(self, ruleset_script=None): | ||||||
|  |         ruleset_script_path = os.path.join( | ||||||
|  |             os.path.dirname(self.firewall_conf), | ||||||
|  |             "firewall.conf.sh" | ||||||
|  |         ) | ||||||
|  |         f = open(ruleset_script_path, "a+b") | ||||||
|  |         fcntl.flock(f.fileno(), fcntl.LOCK_EX) | ||||||
|  |         try: | ||||||
|  |             if ruleset_script: | ||||||
|  |                 f.seek(0) | ||||||
|  |                 f.truncate(0) | ||||||
|  |                 f.write(ruleset_script) | ||||||
|  |                 f.flush() | ||||||
|  |             else: | ||||||
|  |                 f.seek(0) | ||||||
|  |                 ruleset_script = f.read() | ||||||
|  | 
 | ||||||
|  |             if ruleset_script: | ||||||
|  |                 try: | ||||||
|  |                     ruleset_script = logger(locked(ruleset_script)) | ||||||
|  |                     p = self.run(fw_encap, user="root", gui=False, wait=True, passio_popen=True, autostart=False) | ||||||
|  |                     p.stdin.write(ruleset_script) | ||||||
|  |                     p.stdin.close() | ||||||
|  |                     p.stdout.read() | ||||||
|  |                     retcode = p.wait() | ||||||
|  |                     f.seek(0) | ||||||
|  |                     f.truncate(0) | ||||||
|  |                     f.flush() | ||||||
|  |                 except QubesException, e: | ||||||
|  |                     pass | ||||||
|  | 
 | ||||||
|  |         finally: | ||||||
|  |             f.close() | ||||||
|  | 
 | ||||||
|  | register_qubes_vm_class(QubesVm) | ||||||
| @ -0,0 +1,46 @@ | |||||||
|  | #!/usr/bin/python2 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # | ||||||
|  | # The Qubes OS Project, http://www.qubes-os.org | ||||||
|  | # | ||||||
|  | # Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com> | ||||||
|  | # Copyright (C) 2013  Marek Marczykowski <marmarek@invisiblethingslab.com> | ||||||
|  | # | ||||||
|  | # This program is free software; you can redistribute it and/or | ||||||
|  | # modify it under the terms of the GNU General Public License | ||||||
|  | # as published by the Free Software Foundation; either version 2 | ||||||
|  | # of the License, or (at your option) any later version. | ||||||
|  | # | ||||||
|  | # This program is distributed in the hope that it will be useful, | ||||||
|  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | # GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | # You should have received a copy of the GNU General Public License | ||||||
|  | # along with this program; if not, write to the Free Software | ||||||
|  | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | ||||||
|  | # | ||||||
|  | # | ||||||
|  | import sys | ||||||
|  | import libvirt | ||||||
|  | 
 | ||||||
|  | from qubes.qubes import QubesNetVm as OriginalQubesNetVm | ||||||
|  | from qubes.qubes import register_qubes_vm_class,vmm,dry_run | ||||||
|  | from qubes.qubes import defaults,system_path,vm_files | ||||||
|  | from qubes.qubes import QubesVmCollection,QubesException | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class QubesNetVm(OriginalQubesNetVm): | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def netmask(self): | ||||||
|  |         if getattr(self, "static_ip"): | ||||||
|  |             return "255.255.255.255" | ||||||
|  |         return self.__netmask | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def network(self): | ||||||
|  |         return self.__network | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | register_qubes_vm_class(QubesNetVm) | ||||||
| @ -0,0 +1,183 @@ | |||||||
|  | #!/usr/bin/python2 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # | ||||||
|  | # The Qubes OS Project, http://www.qubes-os.org | ||||||
|  | # | ||||||
|  | # Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com> | ||||||
|  | # Copyright (C) 2013  Marek Marczykowski <marmarek@invisiblethingslab.com> | ||||||
|  | # | ||||||
|  | # This program is free software; you can redistribute it and/or | ||||||
|  | # modify it under the terms of the GNU General Public License | ||||||
|  | # as published by the Free Software Foundation; either version 2 | ||||||
|  | # of the License, or (at your option) any later version. | ||||||
|  | # | ||||||
|  | # This program is distributed in the hope that it will be useful, | ||||||
|  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | # GNU General Public License for more details. | ||||||
|  | # | ||||||
|  | # You should have received a copy of the GNU General Public License | ||||||
|  | # along with this program; if not, write to the Free Software | ||||||
|  | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | ||||||
|  | # | ||||||
|  | # | ||||||
|  | from datetime import datetime | ||||||
|  | 
 | ||||||
|  | import sys | ||||||
|  | import libvirt | ||||||
|  | import pipes | ||||||
|  | 
 | ||||||
|  | from qubes.qubes import QubesProxyVm as OriginalQubesProxyVm | ||||||
|  | from qubes.qubes import register_qubes_vm_class,vmm,dry_run | ||||||
|  | from qubes.qubes import defaults,system_path,vm_files | ||||||
|  | from qubes.qubes import QubesVmCollection,QubesException | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | yum_proxy_ip = '10.137.255.254' | ||||||
|  | yum_proxy_port = '8082' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class QubesProxyVm(OriginalQubesProxyVm): | ||||||
|  | 
 | ||||||
|  |     def write_iptables_qubesdb_entry(self): | ||||||
|  |         self.qdb.rm("/qubes-iptables-domainrules/") | ||||||
|  |         iptables =  "# Generated by Qubes Core on {0}\n".format(datetime.now().ctime()) | ||||||
|  |         iptables += "*filter\n" | ||||||
|  |         iptables += ":INPUT DROP [0:0]\n" | ||||||
|  |         iptables += ":FORWARD DROP [0:0]\n" | ||||||
|  |         iptables += ":OUTPUT ACCEPT [0:0]\n" | ||||||
|  |         iptables += ":PR-QBS-FORWARD - [0:0]\n" | ||||||
|  | 
 | ||||||
|  |         # Strict INPUT rules | ||||||
|  |         iptables += "-A INPUT -i vif+ -p udp -m udp --dport 68 -j DROP\n" | ||||||
|  |         iptables += "-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED " \ | ||||||
|  |                     "-j ACCEPT\n" | ||||||
|  |         iptables += "-A INPUT -p icmp -j ACCEPT\n" | ||||||
|  |         iptables += "-A INPUT -i lo -j ACCEPT\n" | ||||||
|  |         iptables += "-A INPUT -j REJECT --reject-with icmp-host-prohibited\n" | ||||||
|  | 
 | ||||||
|  |         iptables += "-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED " \ | ||||||
|  |                     "-j ACCEPT\n" | ||||||
|  |         # Allow dom0 networking | ||||||
|  |         iptables += "-A FORWARD -i vif0.0 -j ACCEPT\n" | ||||||
|  |         # Engage in firewalling for VMs | ||||||
|  |         iptables += "-A FORWARD -j PR-QBS-FORWARD\n" | ||||||
|  |         # Deny inter-VMs networking | ||||||
|  |         iptables += "-A FORWARD -i vif+ -o vif+ -j DROP\n" | ||||||
|  |         iptables += "COMMIT\n" | ||||||
|  |         self.qdb.write("/qubes-iptables-header", iptables) | ||||||
|  | 
 | ||||||
|  |         vms = [vm for vm in self.connected_vms.values()] | ||||||
|  |         vms_rulesets = [] | ||||||
|  |         for vm in vms: | ||||||
|  |             vm_iptables = "" | ||||||
|  | 
 | ||||||
|  |             iptables="*filter\n" | ||||||
|  |             conf = vm.get_firewall_conf() | ||||||
|  | 
 | ||||||
|  |             xid = vm.get_xid() | ||||||
|  |             if xid < 0: # VM not active ATM | ||||||
|  |                 continue | ||||||
|  | 
 | ||||||
|  |             ip = vm.ip | ||||||
|  |             if ip is None: | ||||||
|  |                 continue | ||||||
|  | 
 | ||||||
|  |             # Anti-spoof rules are added by vif-script (vif-route-qubes), here we trust IP address | ||||||
|  | 
 | ||||||
|  |             accept_action = "ACCEPT" | ||||||
|  |             reject_action = "REJECT --reject-with icmp-host-prohibited" | ||||||
|  | 
 | ||||||
|  |             if conf["allow"]: | ||||||
|  |                 default_action = accept_action | ||||||
|  |                 rules_action = reject_action | ||||||
|  |             else: | ||||||
|  |                 default_action = reject_action | ||||||
|  |                 rules_action = accept_action | ||||||
|  | 
 | ||||||
|  |             for rule in conf["rules"]: | ||||||
|  |                 if getattr(vm, "static_ip", None) and rule["address"].startswith("from-"): | ||||||
|  |                     ruletext = "-s {0} -d {1}".format(rule["address"][len("from-"):], ip) | ||||||
|  |                     if rule["netmask"] != 32: | ||||||
|  |                         ruletext += "/{0}".format(rule["netmask"]) | ||||||
|  | 
 | ||||||
|  |                     if rule["proto"] is not None and rule["proto"] != "any": | ||||||
|  |                         ruletext += " -p {0}".format(rule["proto"]) | ||||||
|  |                         if rule["portBegin"] is not None and rule["portBegin"] > 0: | ||||||
|  |                             ruletext += " --dport {0}".format(rule["portBegin"]) | ||||||
|  |                             if rule["portEnd"] is not None and rule["portEnd"] > rule["portBegin"]: | ||||||
|  |                                 ruletext += ":{0}".format(rule["portEnd"]) | ||||||
|  | 
 | ||||||
|  |                     ruletext += " -j {0}\n".format(rules_action) | ||||||
|  |                     iptables += "-A PR-QBS-FORWARD " + ruletext | ||||||
|  |                     vm_iptables += "-A FORTRESS-INPUT " + ruletext | ||||||
|  |                     continue | ||||||
|  | 
 | ||||||
|  |                 iptables += "-A PR-QBS-FORWARD -s {0} -d {1}".format(ip, rule["address"]) | ||||||
|  |                 if rule["netmask"] != 32: | ||||||
|  |                     iptables += "/{0}".format(rule["netmask"]) | ||||||
|  | 
 | ||||||
|  |                 if rule["proto"] is not None and rule["proto"] != "any": | ||||||
|  |                     iptables += " -p {0}".format(rule["proto"]) | ||||||
|  |                     if rule["portBegin"] is not None and rule["portBegin"] > 0: | ||||||
|  |                         iptables += " --dport {0}".format(rule["portBegin"]) | ||||||
|  |                         if rule["portEnd"] is not None and rule["portEnd"] > rule["portBegin"]: | ||||||
|  |                             iptables += ":{0}".format(rule["portEnd"]) | ||||||
|  | 
 | ||||||
|  |                 iptables += " -j {0}\n".format(rules_action) | ||||||
|  | 
 | ||||||
|  |             if conf["allowDns"] and self.netvm is not None: | ||||||
|  |                 # PREROUTING does DNAT to NetVM DNSes, so we need self.netvm. | ||||||
|  |                 # properties | ||||||
|  |                 iptables += "-A PR-QBS-FORWARD -s {0} -p udp -d {1} --dport 53 -j " \ | ||||||
|  |                             "ACCEPT\n".format(ip,self.netvm.gateway) | ||||||
|  |                 iptables += "-A PR-QBS-FORWARD -s {0} -p udp -d {1} --dport 53 -j " \ | ||||||
|  |                             "ACCEPT\n".format(ip,self.netvm.secondary_dns) | ||||||
|  |                 iptables += "-A PR-QBS-FORWARD -s {0} -p tcp -d {1} --dport 53 -j " \ | ||||||
|  |                             "ACCEPT\n".format(ip,self.netvm.gateway) | ||||||
|  |                 iptables += "-A PR-QBS-FORWARD -s {0} -p tcp -d {1} --dport 53 -j " \ | ||||||
|  |                             "ACCEPT\n".format(ip,self.netvm.secondary_dns) | ||||||
|  |             if conf["allowIcmp"]: | ||||||
|  |                 iptables += "-A PR-QBS-FORWARD -s {0} -p icmp -j ACCEPT\n".format(ip) | ||||||
|  |                 if getattr(vm, "static_ip", None): | ||||||
|  |                     iptables += "-A PR-QBS-FORWARD -d {0} -p icmp -j ACCEPT\n".format(ip) | ||||||
|  |                     vm_iptables += "-A FORTRESS-INPUT -d {0} -p icmp -j ACCEPT\n".format(ip) | ||||||
|  |             if conf["allowYumProxy"]: | ||||||
|  |                 iptables += "-A PR-QBS-FORWARD -s {0} -p tcp -d {1} --dport {2} -j ACCEPT\n".format(ip, yum_proxy_ip, yum_proxy_port) | ||||||
|  |             else: | ||||||
|  |                 iptables += "-A PR-QBS-FORWARD -s {0} -p tcp -d {1} --dport {2} -j DROP\n".format(ip, yum_proxy_ip, yum_proxy_port) | ||||||
|  | 
 | ||||||
|  |             iptables += "-A PR-QBS-FORWARD -s {0} -j {1}\n".format(ip, default_action) | ||||||
|  |             if getattr(vm, "static_ip", None): | ||||||
|  |                 iptables += "-A PR-QBS-FORWARD -d {0} -j {1}\n".format(ip, default_action) | ||||||
|  |                 vm_iptables += "-A FORTRESS-INPUT -d {0} -j {1}\n".format(ip, default_action) | ||||||
|  |                 vm_iptables += "COMMIT\n" | ||||||
|  |                 vms_rulesets.append((vm, vm_iptables)) | ||||||
|  |             iptables += "COMMIT\n" | ||||||
|  |             self.qdb.write("/qubes-iptables-domainrules/"+str(xid), iptables) | ||||||
|  | 
 | ||||||
|  |         # no need for ending -A PR-QBS-FORWARD -j DROP, cause default action is DROP | ||||||
|  | 
 | ||||||
|  |         self.write_netvm_domid_entry() | ||||||
|  | 
 | ||||||
|  |         self.rules_applied = None | ||||||
|  |         self.qdb.write("/qubes-iptables", 'reload') | ||||||
|  | 
 | ||||||
|  |         for vm, ruleset in vms_rulesets: | ||||||
|  |             shell_ruleset = "echo Adjusting firewall rules to: >&2\n" | ||||||
|  |             shell_ruleset += "echo %s >&2\n" % pipes.quote(ruleset.strip()) | ||||||
|  |             shell_ruleset += "data=$(iptables-save -t filter)\n" | ||||||
|  |             shell_ruleset += 'if ! echo "$data" | grep -q -- "^:FORTRESS-INPUT" ; then\n' | ||||||
|  |             shell_ruleset += '    data=$(echo "$data" | sed "s/^:INPUT/:FORTRESS-INPUT - [0:0]\\n\\0/")\n' | ||||||
|  |             shell_ruleset += "fi\n" | ||||||
|  |             shell_ruleset += 'if ! echo "$data" | grep -q -- "-A INPUT -j FORTRESS-INPUT" ; then\n' | ||||||
|  |             shell_ruleset += '    data=$(echo "$data" | sed -r "s|-A INPUT -i vif. -j REJECT --reject-with icmp-host-prohibited|-A INPUT -j FORTRESS-INPUT\\n\\0|")\n' | ||||||
|  |             shell_ruleset += "fi\n" | ||||||
|  |             shell_ruleset += 'data=$(echo "$data" | grep -v ^COMMIT$)\n' | ||||||
|  |             shell_ruleset += 'data=$(echo "$data" | grep -v -- "-A FORTRESS-INPUT")\n' | ||||||
|  |             shell_ruleset += 'data="$data\n"%s\n' % pipes.quote(ruleset) | ||||||
|  |             shell_ruleset += 'echo "$data" | iptables-restore -T filter\n' | ||||||
|  |             vm.adjust_own_firewall_rules(shell_ruleset) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | register_qubes_vm_class(QubesProxyVm) | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Manuel Amador (Rudd-O)
						Manuel Amador (Rudd-O)