@@ -33,8 +33,9 @@ from logging import INFO, DEBUG, WARNING, ERROR, CRITICAL
import traceback
from bubble_api import bubble_conn_check, bubble_activity_log, REDIS, redis_set, \
is_bubble_request, is_sage_request, is_not_from_vpn
is_bubble_request, is_sage_request, is_not_from_vpn, is_flex_domain, bubble_get_flex_router, original_flex_ip
from bubble_config import bubble_host, bubble_host_alias, bubble_sage_host, bubble_sage_ip4, bubble_sage_ip6, cert_validation_host
from bubble_flex_passthru import BubbleFlexPassthruLayer
bubble_log = logging.getLogger(__name__)
@@ -182,36 +183,26 @@ def check_bubble_connection(client_addr, server_addr, fqdns, security_level):
if security_level['level'] == SEC_MAX:
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('check_bubble_connection: bubble API returned ' + str(check_response) +' for FQDN/addr ' + str(fqdns) +'/' + str(server_addr) + ', security_level=maximum, returning Block')
return {'fqdns': fqdns, 'addr': server_addr, 'passthru': False, 'flex': False, ' block': True, 'reason': 'bubble_error'}
return {'fqdns': fqdns, 'addr': server_addr, 'passthru': False, 'block': True, 'reason': 'bubble_error'}
else:
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('check_bubble_connection: bubble API returned ' + str(check_response) +' for FQDN/addr ' + str(fqdns) +'/' + str(server_addr) + ', returning True')
return {'fqdns': fqdns, 'addr': server_addr, 'passthru': True, 'flex': False, ' reason': 'bubble_error'}
return {'fqdns': fqdns, 'addr': server_addr, 'passthru': True, 'reason': 'bubble_error'}
elif check_response == 'passthru':
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('check_bubble_connection: bubble API returned ' + str(check_response) +' for FQDN/addr ' + str(fqdns) +'/' + str(server_addr) + ', returning True')
return {'fqdns': fqdns, 'addr': server_addr, 'passthru': True, 'flex': False, ' reason': 'bubble_passthru'}
return {'fqdns': fqdns, 'addr': server_addr, 'passthru': True, 'reason': 'bubble_passthru'}
elif check_response == 'block':
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('check_bubble_connection: bubble API returned ' + str(check_response) +' for FQDN/addr ' + str(fqdns) +'/' + str(server_addr) + ', returning Block')
return {'fqdns': fqdns, 'addr': server_addr, 'passthru': False, 'flex': False, 'block': True, 'reason': 'bubble_block'}
elif check_response == 'passthru_flex':
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('check_bubble_connection: bubble API returned ' + str(check_response) +' for FQDN/addr ' + str(fqdns) +'/' + str(server_addr) + ', returning True')
return {'fqdns': fqdns, 'addr': server_addr, 'passthru': True, 'flex': True, 'reason': 'bubble_passthru_flex'}
elif check_response == 'noop_flex':
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('check_bubble_connection: bubble API returned ' + str(check_response) +' for FQDN/addr ' + str(fqdns) +'/' + str(server_addr) + ', returning True')
return {'fqdns': fqdns, 'addr': server_addr, 'passthru': False, 'flex': True, 'reason': 'bubble_no_passthru_flex'}
return {'fqdns': fqdns, 'addr': server_addr, 'passthru': False, 'block': True, 'reason': 'bubble_block'}
else:
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('check_bubble_connection: bubble API returned ' + str(check_response) +' for FQDN/addr ' + str(fqdns) +'/' + str(server_addr) + ', returning False')
return {'fqdns': fqdns, 'addr': server_addr, 'passthru': False, 'flex': False, ' reason': 'bubble_no_passthru'}
return {'fqdns': fqdns, 'addr': server_addr, 'passthru': False, 'reason': 'bubble_no_passthru'}
def check_connection(client_addr, server_addr, fqdns, security_level):
@@ -240,28 +231,50 @@ def check_connection(client_addr, server_addr, fqdns, security_level):
return check_response
def next_layer(next_layer):
if isinstance(next_layer, TlsLayer) and next_layer._client_tls:
client_hello = net_tls.ClientHello.from_file(next_layer.client_conn.rfile)
client_addr = next_layer.client_conn.address[0]
server_addr = next_layer.server_conn.address[0]
def check_passthru_flex(client_addr, server_addr, fqdns):
if fqdns:
for fqdn in fqdns:
if is_flex_domain(client_addr, fqdn):
return True
else:
return is_flex_domain(client_addr, server_addr)
def passthru_flex_port(client_addr, fqdns):
router = bubble_get_flex_router(client_addr)
if router is None or 'auth' not in router:
if bubble_log.isEnabledFor(INFO):
bubble_log.info('apply_passthru_flex: no flex router for fqdn(s): '+repr(fqdns))
elif 'port' in router:
return router['port']
else:
if bubble_log.isEnabledFor(WARNING):
bubble_log.warning('apply_passthru_flex: flex router found but has no port ('+repr(router)+') for fqdn(s): '+repr(fqdns))
return None
def next_layer(layer):
if isinstance(layer, TlsLayer) and layer._client_tls:
client_hello = net_tls.ClientHello.from_file(layer.client_conn.rfile)
client_addr = layer.client_conn.address[0]
server_addr = layer.server_conn.address[0]
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('next_layer: STARTING: client='+ client_addr+' server='+server_addr)
if client_hello.sni:
fqdn = client_hello.sni.decode()
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('next_layer: using fqdn in SNI: '+ fqdn)
fqdns = [ fqdn ]
fqdns = [fqdn]
else:
fqdns = fqdns_for_addr(server_addr)
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('next_layer: NO fqdn in sni, using fqdns from DNS: '+ str(fqdns))
next_layer.fqdns = fqdns
layer.fqdns = fqdns
no_fqdns = fqdns is None or len(fqdns) == 0
security_level = get_device_security_level(client_addr, fqdns)
next_layer.security_level = security_level
next_layer.do_block = False
called_check_api = False
layer.security_level = security_level
layer.do_block = False
check_for_flex = False
if is_bubble_request(server_addr, fqdns):
if bubble_log.isEnabledFor(INFO):
bubble_log.info('next_layer: enabling passthru for LOCAL bubble='+server_addr+' (bubble_host ('+bubble_host+') in fqdns or bubble_host_alias ('+bubble_host_alias+') in fqdns) regardless of security_level='+repr(security_level)+' for client='+client_addr+', fqdns='+repr(fqdns))
@@ -277,7 +290,7 @@ def next_layer(next_layer):
if bubble_log.isEnabledFor(WARNING):
bubble_log.warning('next_layer: enabling block for non-VPN client='+client_addr+', fqdns='+str(fqdns))
bubble_activity_log(client_addr, server_addr, 'conn_block_non_vpn', fqdns)
next_ layer.__class__ = TlsBlock
layer.__class__ = TlsBlock
return
elif security_level['level'] == SEC_OFF:
@@ -304,34 +317,43 @@ def next_layer(next_layer):
if bubble_log.isEnabledFor(INFO):
bubble_log.info('next_layer: calling check_connection for server='+server_addr+', fqdns='+str(fqdns)+', client='+client_addr+' with security_level='+repr(security_level))
check = check_connection(client_addr, server_addr, fqdns, security_level)
called_check_api = True
if check is None or ('passthru' in check and check['passthru'] and ('flex' not in check or not check['flex']) ):
if check is None or ('passthru' in check and check['passthru']):
if bubble_log.isEnabledFor(INFO):
bubble_log.info('next_layer: enabling passthru for server=' + server_addr+', fqdns='+str(fqdns))
bubble_activity_log(client_addr, server_addr, 'tls_passthru', fqdns)
next_layer_replacement = RawTCPLayer(next_layer.ctx, ignore=True)
next_layer.reply.send(next_layer_replacement)
flex_port = None
if check_passthru_flex(client_addr, server_addr, fqdns):
if bubble_log.isEnabledFor(DEBUG):
bubble_log.debug('next_layer: applying flex passthru for server=' + server_addr+', fqdns='+str(fqdns))
flex_port = passthru_flex_port(client_addr, fqdns)
if flex_port:
layer_replacement = BubbleFlexPassthruLayer(layer.ctx, ('127.0.0.1', flex_port), fqdns[0], 443)
layer.reply.send(layer_replacement)
if flex_port is None:
layer_replacement = RawTCPLayer(layer.ctx, ignore=True)
layer.reply.send(layer_replacement)
elif 'block' in check and check['block']:
if bubble_log.isEnabledFor(INFO):
bubble_log.info('next_layer: enabling block for server=' + server_addr+', fqdns='+str(fqdns))
bubble_activity_log(client_addr, server_addr, 'conn_block', fqdns)
if show_block_stats(client_addr, fqdns) and security_level['level'] != SEC_BASIC:
next_layer.do_block = True
next_layer.__class__ = TlsFeedback
layer.do_block = True
layer.__class__ = TlsFeedback
else:
next_ layer.__class__ = TlsBlock
layer.__class__ = TlsBlock
elif security_level['level'] == SEC_BASIC:
if bubble_log.isEnabledFor(INFO):
bubble_log.info('next_layer: check='+repr(check)+' but security_level='+repr(security_level)+', enabling passthru for server=' + server_addr+', fqdns='+str(fqdns))
bubble_activity_log(client_addr, server_addr, 'tls_passthru', fqdns)
next_layer_replacement = RawTCPLayer(next_layer.ctx, ignore=True)
next_layer.reply.send(next_layer_replacement)
# todo
layer_replacement = RawTCPLayer(layer.ctx, ignore=True)
layer.reply.send(layer_replacement)
else:
if bubble_log.isEnabledFor(INFO):
bubble_log.info('next_layer: disabling passthru (with TlsFeedback) for client_addr='+client_addr+', server_addr='+server_addr+', fqdns='+str(fqdns))
bubble_activity_log(client_addr, server_addr, 'tls_intercept', fqdns)
next_ layer.__class__ = TlsFeedback
layer.__class__ = TlsFeedback