def _apply_policy(policy, devname, devaddr): """Create network interfaces, routes and firewall rules per policy. :param `str` devname: Local device name to use for the tunnel. :param `str` devaddr: IP address to use on the local device for the tunnel. """ tun_devname = _utils.wg_dev_create( unique_id=policy['session_id'], tun_localaddr=policy['local_ip'], tun_remoteaddr=policy['remote_ip'], ll_devname=devname, ll_localaddr=devaddr, ll_remoteaddr=policy['endpoint_ip'], ) # Add firewall rules. _utils.wg_firewall_client_init(tun_devname, policy['endpoints']) # No traffic should be *routed* from the WarpGate interface. netdev.dev_conf_forwarding_set(tun_devname, False) # Bring up the device. netdev.link_set_up(tun_devname) # Add routes found in the policy _utils.wg_route_create( tun_devname, policy['local_ip'], policy['remote_ip'], policy['routes'] )
def on_create_request(self, rsrc_id, rsrc_data): """ :returns ``dict``: Network IP `vip`, network device `veth`, IP gateway `gateway`. """ with lc.LogContext(_LOGGER, rsrc_id, adapter_cls=lc.ContainerAdapter) as log: log.debug('req: %r', rsrc_data) app_unique_name = rsrc_id environment = rsrc_data['environment'] assert environment in _SET_BY_ENVIRONMENT, \ 'Unknown environment: %r' % environment veth0, veth1 = _device_from_rsrc_id(app_unique_name) if app_unique_name not in self._devices: # VIPs allocation (the owner is the resource link) ip = self._vips.alloc(rsrc_id) self._devices[app_unique_name] = {'ip': ip} else: # Re-read what IP we assigned before ip = self._devices[app_unique_name]['ip'] if 'device' not in self._devices[app_unique_name]: # Create the interface pair netdev.link_add_veth(veth0, veth1) # Configure the links netdev.link_set_mtu(veth0, self.ext_mtu) netdev.link_set_mtu(veth1, self.ext_mtu) # Tag the interfaces netdev.link_set_alias(veth0, rsrc_id) netdev.link_set_alias(veth1, rsrc_id) # Add interface to the bridge netdev.bridge_addif(self._TMBR_DEV, veth0) netdev.link_set_up(veth0) # We keep veth1 down until inside the container # Record the new device in our state self._devices[app_unique_name] = _device_info(veth0) self._devices[app_unique_name].update({ 'ip': ip, 'environment': environment, }) # We can now mark ip traffic as belonging to the requested # environment. _add_mark_rule(ip, environment) result = { 'vip': ip, 'veth': veth1, 'gateway': self._TM_IP, 'external_ip': self.ext_ip, } return result
def test_link_set_up(self): """Test of device up.""" netdev.link_set_up('foo') treadmill.subproc.check_call.assert_called_with([ 'ip', 'link', 'set', 'dev', 'foo', 'up', ], )
def initialize(self, service_dir): super(NetworkResourceService, self).initialize(service_dir) # The <svcroot>/vips directory is used to allocate/de-allocate # container vips. vips_dir = os.path.join(service_dir, self._VIPS_DIR) # Initialize vips self._vips = vipfile.VipMgr(vips_dir, self._service_rsrc_dir) # Clear all environment assignments here. They will be re-assigned # below. for containers_set in set(_SET_BY_ENVIRONMENT.values()): iptables.create_set(containers_set, set_type='hash:ip', family='inet', hashsize=1024, maxelem=65536) need_init = False try: netdev.link_set_up(self._TM_DEV0) netdev.link_set_up(self._TM_DEV1) netdev.link_set_up(self._TMBR_DEV) except subproc.CalledProcessError: need_init = True if need_init: # Reset the bridge self._bridge_initialize() # These two are also done here because they are idempotent # Disable bridge forward delay netdev.bridge_setfd(self._TMBR_DEV, 0) # Enable route_localnet so that we can redirect traffic from the # container to the node's loopback address. netdev.dev_conf_route_localnet_set(self._TM_DEV0, True) # Read bridge status self._bridge_mtu = netdev.dev_mtu(self._TMBR_DEV) # Read current status self._devices = {} for device in netdev.bridge_brif(self._TMBR_DEV): # Ignore local device that is used pass external traffic into the # Treadmill container network. if device == self._TM_DEV1: continue dev_info = _device_info(device) self._devices[dev_info['alias']] = dev_info # Read the currently assigned vIPs for (ip, resource) in self._vips.list(): self._devices.setdefault(resource, {})['ip'] = ip # Mark all the above information as stale for device in self._devices: self._devices[device]['stale'] = True
def _bridge_initialize(self): """Reset/initialize the Treadmill node bridge. """ try: # FIXME(boysson): This is for migration when TM_DEV0 used to be a # bridge. netdev.link_set_down(self._TM_DEV0) netdev.bridge_delete(self._TM_DEV0) except subproc.CalledProcessError: pass try: netdev.link_set_down(self._TM_DEV0) netdev.link_del_veth(self._TM_DEV0) except subproc.CalledProcessError: pass try: netdev.link_set_down(self._TMBR_DEV) netdev.bridge_delete(self._TMBR_DEV) except subproc.CalledProcessError: pass netdev.bridge_create(self._TMBR_DEV) netdev.bridge_setfd(self._TMBR_DEV, 0) netdev.link_add_veth(self._TM_DEV0, self._TM_DEV1) netdev.link_set_mtu(self._TM_DEV0, self.ext_mtu) netdev.link_set_mtu(self._TM_DEV1, self.ext_mtu) netdev.bridge_addif(self._TMBR_DEV, self._TM_DEV1) # Force the bridge MAC address to the Treadmill device. This # prevents the bridge's MAC from changing when adding/removing # container interfaces. # (Default Linux bridge behavior is to set the bridge's MAC to be # lowest of it's ports). tm_mac = netdev.dev_mac(self._TM_DEV1) netdev.link_set_addr(self._TMBR_DEV, tm_mac) # Bring up the bridge interface netdev.link_set_up(self._TMBR_DEV) netdev.link_set_up(self._TM_DEV1) netdev.addr_add( addr='{ip}/16'.format(ip=self._TM_IP), devname=self._TM_DEV0 ) # Enable route_localnet so that we can redirect traffic from the # container to the node's loopback address. netdev.dev_conf_route_localnet_set(self._TM_DEV0, True) # Bring up the TM interface netdev.link_set_up(self._TM_DEV0)
def _process_request(self, client_principal, client_addr, policy_name): """Process a single request. """ # XXX: Add error handling? _LOGGER.info('Request %r:%r (from %r)', client_principal, policy_name, client_addr) # See if we have a policy. We use the client_principal as namespace for # the policy lookup. namespace = urlparse.quote(client_principal.lower(), safe='@') # Create a session session = _get_policy( repository=self._policies_dir, namespace=namespace, name=policy_name ) if not session: _LOGGER.warning('Nonexistent policy %r', policy_name) return { '_denied': 'no such policy' } # XXX: Find a better scheme to select session id session['id'] = random.randint(0, (2**32) - 1) session_name = _session_fname(session) # Assign an IP from the network gateway_ip = None network_idx = None for cidr, network in self._networks.items(): try: client_ip = network['pool'].alloc(session_name) except Exception: # pylint: disable=broad-except # FIXME: add proper exception handling continue gateway_ip = network['gateway_ip'] network_cidr = cidr break else: _LOGGER.critical('Could not assign an IP for %r', session_name) return { '_error': 'no capacity' } # Setup the interface tun_devname = _utils.wg_dev_create( unique_id=session['id'], tun_localaddr=gateway_ip, tun_remoteaddr=client_ip, ll_devname=self._endpoint_dev, ll_localaddr=self._endpoint_ip, ll_remoteaddr=client_addr ) session['interface'] = tun_devname session['client_ip'] = client_ip session['gateway_ip'] = gateway_ip session['network'] = network_cidr # Setup the firewall XXX # Enable forwarding netdev.dev_conf_forwarding_set(tun_devname, True) self._session = session with open(os.path.join(self._sessions_dir, session_name), 'w') as f: json.dump(session, fp=f, indent=4) # Bring up the interface netdev.link_set_up(tun_devname) # The reply contains the reverse tunnel settings for the client return { 'local_ip': client_ip, 'remote_ip': gateway_ip, 'endpoint_ip': self._endpoint_ip, 'routes': session['routes'], 'endpoints': session['endpoints'], 'session_id': session['id'], }