Browse Source

qubes/vm/net: add feature of hiding real IP from the VM

This helps hiding VM IP for anonymous VMs (Whonix) even when some
application leak it. VM will know only some fake IP, which should be set
to something as common as possible.
The feature is mostly implemented at (Proxy)VM side using NAT in
separate network namespace. Core here is only passing arguments to it.
It is designed the way that multiple VMs can use the same IP and still
do not interfere with each other. Even more: it is possible to address
each of them (using their "native" IP), even when multiple of them share
the same "fake" IP.

Original approach (marmarek/old-qubes-core-admin#2) used network script
arguments by appending them to script name, but libxl in Xen >= 4.6
fixed that side effect and it isn't possible anymore. So use QubesDB
instead.

From user POV, this adds 3 "features":
 - net/fake-ip - IP address visible in the VM
 - net/fake-gateway - default gateway in the VM
 - net/fake-netmask - network mask
The feature is enabled if net/fake-ip is set (to some IP address) and is
different than VM native IP. All of those "features" can be set on
template, to affect all of VMs.
Firewall rules etc in (Proxy)VM should still be applied to VM "native"
IP.

Fixes QubesOS/qubes-issues#1143
Marek Marczykowski-Górecki 7 years ago
parent
commit
2c6c476410
2 changed files with 37 additions and 3 deletions
  1. 34 0
      qubes/vm/mix/net.py
  2. 3 3
      qubes/vm/qubesvm.py

+ 34 - 0
qubes/vm/mix/net.py

@@ -85,6 +85,27 @@ class NetVMMixin(qubes.events.Emitter):
         else:
             return self.get_ip_for_vm(self)
 
+    @qubes.tools.qvm_ls.column(width=15)
+    @property
+    def visible_ip(self):
+        '''IP address of this domain as seen by the domain.'''
+        return self.features.check_with_template('net/fake-ip', None) or \
+            self.ip
+
+    @qubes.tools.qvm_ls.column(width=15)
+    @property
+    def visible_gateway(self):
+        '''Default gateway of this domain as seen by the domain.'''
+        return self.features.check_with_template('net/fake-gateway', None) or \
+            self.netvm.gateway
+
+    @qubes.tools.qvm_ls.column(width=15)
+    @property
+    def visible_netmask(self):
+        '''Netmask as seen by the domain.'''
+        return self.features.check_with_template('net/fake-netmask', None) or \
+            self.netvm.netmask
+
     #
     # used in netvms (provides_network=True)
     # those properties and methods are most likely accessed as vm.netvm.<prop>
@@ -274,6 +295,19 @@ class NetVMMixin(qubes.events.Emitter):
         # signal its done
         self.qdb.write(base_dir[:-1], '')
 
+        # add info about remapped IPs (VM IP hidden from the VM itself)
+        mapped_ip_base = '/mapped-ip/{}'.format(vm.ip)
+        if vm.visible_ip:
+            self.qdb.write(mapped_ip_base + '/visible-ip', vm.visible_ip)
+        else:
+            self.qdb.rm(mapped_ip_base + '/visible-ip')
+        if vm.visible_gateway:
+            self.qdb.write(mapped_ip_base + '/visible-gateway',
+                vm.visible_gateway)
+        else:
+            self.qdb.rm(mapped_ip_base + '/visible-gateway')
+
+
     @qubes.events.handler('property-del:netvm')
     def on_property_del_netvm(self, event, prop, old_netvm=None):
         ''' Sets the the NetVM to default NetVM '''

+ 3 - 3
qubes/vm/qubesvm.py

@@ -1474,9 +1474,9 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
                 self.qdb.write('/qubes-netvm-{}-dns'.format(i), addr)
 
         if self.netvm is not None:
-            self.qdb.write('/qubes-ip', self.ip)
-            self.qdb.write('/qubes-netmask', self.netvm.netmask)
-            self.qdb.write('/qubes-gateway', self.netvm.gateway)
+            self.qdb.write('/qubes-ip', self.visible_ip)
+            self.qdb.write('/qubes-netmask', self.visible_netmask)
+            self.qdb.write('/qubes-gateway', self.visible_gateway)
 
             for i, addr in zip(('primary', 'secondary'), self.dns):
                 self.qdb.write('/qubes-{}-dns'.format(i), addr)