@@ -12,6 +12,7 @@ import bubble.model.cloud.BubbleNetwork; | |||
import bubble.model.device.BubbleDeviceType; | |||
import bubble.model.device.Device; | |||
import bubble.server.BubbleConfiguration; | |||
import bubble.service.cloud.DeviceIdService; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.hibernate.criterion.Order; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
@@ -34,6 +35,7 @@ public class DeviceDAO extends AccountOwnedEntityDAO<Device> { | |||
@Autowired private BubbleConfiguration configuration; | |||
@Autowired private AccountDAO accountDAO; | |||
@Autowired private AppDataDAO dataDAO; | |||
@Autowired private DeviceIdService deviceIdService; | |||
@Override public Order getDefaultSortOrder() { return ORDER_CTIME_ASC; } | |||
@@ -90,6 +92,7 @@ public class DeviceDAO extends AccountOwnedEntityDAO<Device> { | |||
} | |||
final Device updated = super.update(device); | |||
ensureSpareDevice(device.getAccount(), device.getNetwork(), true); | |||
deviceIdService.setDeviceSecurityLevel(updated); | |||
return updated; | |||
} | |||
@@ -6,10 +6,10 @@ import static bubble.ApiConstants.enumFromString; | |||
public enum DeviceSecurityLevel { | |||
// disabled, // todo: when we can identify client IP in dnscrypt-proxy, this setting will disabled DNS domain blocking | |||
basic, | |||
maximum, | |||
standard, | |||
maximum; | |||
basic, | |||
disabled; | |||
@JsonCreator public static DeviceSecurityLevel fromString (String v) { return enumFromString(DeviceSecurityLevel.class, v); } | |||
@@ -6,10 +6,10 @@ package bubble.resources.app; | |||
import bubble.dao.app.AppSiteDAO; | |||
import bubble.model.account.Account; | |||
import bubble.model.app.config.AppDataDriver; | |||
import bubble.model.app.config.AppDataView; | |||
import bubble.model.app.AppSite; | |||
import bubble.model.app.BubbleApp; | |||
import bubble.model.app.config.AppDataDriver; | |||
import bubble.model.app.config.AppDataView; | |||
import bubble.model.device.Device; | |||
import bubble.resources.account.AccountOwnedTemplateResource; | |||
import bubble.service.cloud.DeviceIdService; | |||
@@ -5,9 +5,9 @@ | |||
package bubble.resources.app; | |||
import bubble.model.account.Account; | |||
import bubble.model.app.BubbleApp; | |||
import bubble.model.app.config.AppDataDriver; | |||
import bubble.model.app.config.AppDataView; | |||
import bubble.model.app.BubbleApp; | |||
import bubble.model.device.Device; | |||
import bubble.service.cloud.DeviceIdService; | |||
import lombok.extern.slf4j.Slf4j; | |||
@@ -35,7 +35,8 @@ import static org.cobbzilla.util.daemon.ZillaRuntime.die; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.empty; | |||
import static org.cobbzilla.util.http.HttpContentTypes.CONTENT_TYPE_ANY; | |||
import static org.cobbzilla.util.json.JsonUtil.json; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.*; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.userPrincipal; | |||
@Path(PROXY_ENDPOINT) | |||
@Service @Slf4j | |||
@@ -14,6 +14,7 @@ import bubble.model.cloud.BubbleNode; | |||
import bubble.model.cloud.CloudService; | |||
import bubble.server.BubbleConfiguration; | |||
import bubble.service.boot.SelfNodeService; | |||
import bubble.service.cloud.DeviceIdService; | |||
import bubble.service.cloud.NetworkMonitorService; | |||
import bubble.service.stream.AppPrimerService; | |||
import lombok.extern.slf4j.Slf4j; | |||
@@ -107,7 +108,7 @@ public class NodeInitializerListener extends RestServerLifecycleListenerBase<Bub | |||
} | |||
} | |||
// ensure default devices exist, cert_validation_host is set, and apps are primed | |||
// ensure default devices exist, cert_validation_host is set, apps are primed and device security levels are set | |||
if (thisNode != null) { | |||
final BubbleNetwork thisNetwork = c.getThisNetwork(); | |||
if (thisNetwork != null && thisNetwork.getInstallType() == AnsibleInstallType.node) { | |||
@@ -119,6 +120,7 @@ public class NodeInitializerListener extends RestServerLifecycleListenerBase<Bub | |||
for (Account a : accountDAO.findAll()) { | |||
c.getBean(DeviceDAO.class).ensureSpareDevice(a.getUuid(), thisNode.getNetwork(), false); | |||
} | |||
c.getBean(DeviceIdService.class).initDeviceSecurityLevels(); | |||
} | |||
} | |||
@@ -1,113 +1,17 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.service.cloud; | |||
import bubble.dao.account.AccountDAO; | |||
import bubble.dao.device.DeviceDAO; | |||
import bubble.model.device.Device; | |||
import bubble.server.BubbleConfiguration; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.util.collection.ExpirationMap; | |||
import org.cobbzilla.util.io.FileUtil; | |||
import org.cobbzilla.util.io.FilenamePrefixFilter; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.net.InetAddress; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import java.util.Map; | |||
import static bubble.ApiConstants.HOME_DIR; | |||
import static java.util.concurrent.TimeUnit.MINUTES; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.*; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; | |||
import static org.cobbzilla.wizard.server.RestServerBase.reportError; | |||
public interface DeviceIdService { | |||
@Service @Slf4j | |||
public class DeviceIdService { | |||
Device findDeviceByIp(String ip); | |||
public static final File WG_DEVICES_DIR = new File(HOME_DIR, "wg_devices"); | |||
List<String> findIpsByDevice(String deviceUuid); | |||
public static final String IP_FILE_PREFIX = "ip_"; | |||
public static final FilenamePrefixFilter IP_FILE_FILTER = new FilenamePrefixFilter(IP_FILE_PREFIX); | |||
void initDeviceSecurityLevels(); | |||
public static final String DEVICE_FILE_PREFIX = "device_"; | |||
@Autowired private DeviceDAO deviceDAO; | |||
@Autowired private BubbleConfiguration configuration; | |||
@Autowired private AccountDAO accountDAO; | |||
private final Map<String, Device> deviceCache = new ExpirationMap<>(MINUTES.toMillis(10)); | |||
public Device findDeviceByIp (String ipAddr) { | |||
if (!WG_DEVICES_DIR.exists()) { | |||
if (configuration.testMode()) return findTestDevice(ipAddr); | |||
reportError("findDeviceByIp: err.deviceDir.notFound"); | |||
return null; | |||
} | |||
return deviceCache.computeIfAbsent(ipAddr, ip -> { | |||
try { | |||
// try the simple case first | |||
final File ipFile = new File(WG_DEVICES_DIR, "ip_" + ip); | |||
if (ipFile.exists() && ipFile.length() > 0) { | |||
final String deviceUuid = FileUtil.toString(ipFile).trim(); | |||
return deviceDAO.findByUuid(deviceUuid); | |||
} | |||
// walk through each ip file, finding one whose name semantically matches the given IP | |||
// this may be required for IPv6 since there can be multiple string representation of the same IP | |||
final String[] ipFiles = WG_DEVICES_DIR.list(IP_FILE_FILTER); | |||
if (ipFiles == null || ipFile.length() == 0) return null; | |||
final InetAddress addr = InetAddress.getByName(ip); | |||
for (String f : ipFiles) { | |||
try { | |||
if (InetAddress.getByName(f.substring(IP_FILE_PREFIX.length())).equals(addr)) { | |||
final String deviceUuid = FileUtil.toString(new File(WG_DEVICES_DIR, f)).trim(); | |||
return deviceDAO.findByUuid(deviceUuid); | |||
} | |||
} catch (Exception e) { | |||
log.warn("findDeviceByIp("+ip+"): error handling IP file: "+f+": "+shortError(e)); | |||
} | |||
} | |||
log.warn("findDeviceByIp("+ip+"): no devices found for IP: "+ip); | |||
return null; | |||
} catch (IOException e) { | |||
return die("findDeviceByIp("+ip+") error: "+e, e); | |||
} | |||
}); | |||
} | |||
public List<String> findIpsByDevice(String deviceUuid) { | |||
if (!WG_DEVICES_DIR.exists()) throw invalidEx("err.deviceDir.notFound"); | |||
final File deviceFile = new File(WG_DEVICES_DIR, DEVICE_FILE_PREFIX+deviceUuid); | |||
if (!deviceFile.exists() || deviceFile.length() == 0) return Collections.emptyList(); | |||
try { | |||
return FileUtil.toStringList(deviceFile); | |||
} catch (IOException e) { | |||
return die("findIpsByDevice("+deviceUuid+") error: "+e, e); | |||
} | |||
} | |||
private Device findTestDevice(String ipAddr) { | |||
final String adminUuid = accountDAO.getFirstAdmin().getUuid(); | |||
final List<Device> adminDevices = deviceDAO.findByAccount(adminUuid); | |||
if (empty(adminDevices)) { | |||
log.warn("findDeviceByIp("+ipAddr+") test mode and no admin devices, returning dummy device"); | |||
return new Device().setAccount(adminUuid).setName("dummy"); | |||
} else { | |||
log.warn("findDeviceByIp("+ipAddr+") test mode, returning and possibly initializing first admin device"); | |||
final Device device = adminDevices.get(0); | |||
return device.uninitialized() ? deviceDAO.update(device.initTotpKey()) : device; | |||
} | |||
} | |||
void setDeviceSecurityLevel(Device device); | |||
} |
@@ -0,0 +1,131 @@ | |||
/** | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
package bubble.service.cloud; | |||
import bubble.dao.account.AccountDAO; | |||
import bubble.dao.device.DeviceDAO; | |||
import bubble.model.device.Device; | |||
import bubble.server.BubbleConfiguration; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.util.collection.ExpirationMap; | |||
import org.cobbzilla.util.io.FileUtil; | |||
import org.cobbzilla.util.io.FilenamePrefixFilter; | |||
import org.cobbzilla.wizard.cache.redis.RedisService; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.net.InetAddress; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import java.util.Map; | |||
import static bubble.ApiConstants.HOME_DIR; | |||
import static java.util.concurrent.TimeUnit.MINUTES; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.*; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; | |||
import static org.cobbzilla.wizard.server.RestServerBase.reportError; | |||
@Service @Slf4j | |||
public class StandardDeviceIdService implements DeviceIdService { | |||
public static final File WG_DEVICES_DIR = new File(HOME_DIR, "wg_devices"); | |||
public static final String IP_FILE_PREFIX = "ip_"; | |||
public static final FilenamePrefixFilter IP_FILE_FILTER = new FilenamePrefixFilter(IP_FILE_PREFIX); | |||
public static final String DEVICE_FILE_PREFIX = "device_"; | |||
// used in dnscrypt-proxy and mitmproxy to check device security level | |||
public static final String REDIS_KEY_DEVICE_SECURITY_LEVEL_PREFIX = "bubble_device_security_level_"; | |||
@Autowired private DeviceDAO deviceDAO; | |||
@Autowired private AccountDAO accountDAO; | |||
@Autowired private RedisService redis; | |||
@Autowired private BubbleConfiguration configuration; | |||
private final Map<String, Device> deviceCache = new ExpirationMap<>(MINUTES.toMillis(10)); | |||
@Override public Device findDeviceByIp (String ipAddr) { | |||
if (!WG_DEVICES_DIR.exists()) { | |||
if (configuration.testMode()) return findTestDevice(ipAddr); | |||
reportError("findDeviceByIp: err.deviceDir.notFound"); | |||
return null; | |||
} | |||
return deviceCache.computeIfAbsent(ipAddr, ip -> { | |||
try { | |||
// try the simple case first | |||
final File ipFile = new File(WG_DEVICES_DIR, "ip_" + ip); | |||
if (ipFile.exists() && ipFile.length() > 0) { | |||
final String deviceUuid = FileUtil.toString(ipFile).trim(); | |||
return deviceDAO.findByUuid(deviceUuid); | |||
} | |||
// walk through each ip file, finding one whose name semantically matches the given IP | |||
// this may be required for IPv6 since there can be multiple string representation of the same IP | |||
final String[] ipFiles = WG_DEVICES_DIR.list(IP_FILE_FILTER); | |||
if (ipFiles == null || ipFile.length() == 0) return null; | |||
final InetAddress addr = InetAddress.getByName(ip); | |||
for (String f : ipFiles) { | |||
try { | |||
if (InetAddress.getByName(f.substring(IP_FILE_PREFIX.length())).equals(addr)) { | |||
final String deviceUuid = FileUtil.toString(new File(WG_DEVICES_DIR, f)).trim(); | |||
return deviceDAO.findByUuid(deviceUuid); | |||
} | |||
} catch (Exception e) { | |||
log.warn("findDeviceByIp("+ip+"): error handling IP file: "+f+": "+shortError(e)); | |||
} | |||
} | |||
log.warn("findDeviceByIp("+ip+"): no devices found for IP: "+ip); | |||
return null; | |||
} catch (IOException e) { | |||
return die("findDeviceByIp("+ip+") error: "+e, e); | |||
} | |||
}); | |||
} | |||
@Override public List<String> findIpsByDevice(String deviceUuid) { | |||
if (!WG_DEVICES_DIR.exists()) throw invalidEx("err.deviceDir.notFound"); | |||
final File deviceFile = new File(WG_DEVICES_DIR, DEVICE_FILE_PREFIX+deviceUuid); | |||
if (!deviceFile.exists() || deviceFile.length() == 0) return Collections.emptyList(); | |||
try { | |||
return FileUtil.toStringList(deviceFile); | |||
} catch (IOException e) { | |||
return die("findIpsByDevice("+deviceUuid+") error: "+e, e); | |||
} | |||
} | |||
@Override public void initDeviceSecurityLevels() { | |||
for (Device device : deviceDAO.findAll()) { | |||
if (device.uninitialized()) continue; | |||
setDeviceSecurityLevel(device); | |||
} | |||
} | |||
@Override public void setDeviceSecurityLevel(Device device) { | |||
for (String ip : findIpsByDevice(device.getUuid())) { | |||
redis.set_plaintext(REDIS_KEY_DEVICE_SECURITY_LEVEL_PREFIX+ip, device.getSecurityLevel().name()); | |||
} | |||
} | |||
private Device findTestDevice(String ipAddr) { | |||
final String adminUuid = accountDAO.getFirstAdmin().getUuid(); | |||
final List<Device> adminDevices = deviceDAO.findByAccount(adminUuid); | |||
if (empty(adminDevices)) { | |||
log.warn("findDeviceByIp("+ipAddr+") test mode and no admin devices, returning dummy device"); | |||
return new Device().setAccount(adminUuid).setName("dummy"); | |||
} else { | |||
log.warn("findDeviceByIp("+ipAddr+") test mode, returning and possibly initializing first admin device"); | |||
final Device device = adminDevices.get(0); | |||
return device.uninitialized() ? deviceDAO.update(device.initTotpKey()) : device; | |||
} | |||
} | |||
} |
@@ -0,0 +1,22 @@ | |||
package bubble.service_dbfilter; | |||
import bubble.model.device.Device; | |||
import bubble.service.cloud.DeviceIdService; | |||
import org.springframework.stereotype.Service; | |||
import java.util.List; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.notSupported; | |||
@Service | |||
public class DbFilterDeviceIdService implements DeviceIdService { | |||
@Override public Device findDeviceByIp(String ip) { return notSupported("findDeviceByIp"); } | |||
@Override public List<String> findIpsByDevice(String deviceUuid) { return notSupported("findIpsByDevice"); } | |||
@Override public void initDeviceSecurityLevels() { notSupported("initDeviceSecurityLevels"); } | |||
@Override public void setDeviceSecurityLevel(Device device) { notSupported("setDeviceSecurityLevel"); } | |||
} |
@@ -501,9 +501,8 @@ device_security_level_standard=Standard | |||
device_security_level_standard_description=Standard protection. Unrecognized traffic is allowed. VPN apps enabled. | |||
device_security_level_basic=Basic | |||
device_security_level_basic_description=Basic protection. Unrecognized traffic is allowed. VPN apps disabled. | |||
# todo: when we can identify client IP in dnscrypt-proxy, this setting will disabled DNS domain blocking | |||
#device_security_level_disabled=Disabled | |||
#device_security_level_disabled_description=No protection. Unrecognized traffic is allowed. VPN apps disabled. | |||
device_security_level_disabled=Off | |||
device_security_level_disabled_description=No protection. Unrecognized traffic is allowed. VPN apps disabled. | |||
# Cert types | |||
message_os_uninitialized=Uninitialized | |||
@@ -13,7 +13,7 @@ | |||
get_url: | |||
url: https://github.com/getbubblenow/bubble-dist/raw/master/algo/master.zip | |||
dest: /tmp/algo.zip | |||
checksum: sha256:357e613833385626e88564c97f0b5726f49686c33a774be9a7766bd1a1249915 | |||
checksum: sha256:5a4e72d9671a38ff3ce9b5d6724c05222b343ddc408d690f1f511577d2673122 | |||
- name: Unzip algo master.zip | |||
unarchive: | |||
@@ -33,26 +33,22 @@ import subprocess | |||
REDIS_DNS_PREFIX = 'bubble_dns_' | |||
REDIS_PASSTHRU_PREFIX = 'bubble_passthru_' | |||
REDIS_CLIENT_CERT_STATUS_PREFIX = 'bubble_cert_status_' | |||
REDIS_KEY_DEVICE_SECURITY_LEVEL_PREFIX = 'bubble_device_security_level_' # defined in StandardDeviceIdService | |||
REDIS_PASSTHRU_DURATION = 60 * 60 # 1 hour timeout on passthru | |||
REDIS = redis.Redis(host='127.0.0.1', port=6379, db=0) | |||
FORCE_PASSTHRU = {'passthru': True} | |||
cert_validation_host = None | |||
local_ips = None | |||
def get_ip_cert_status(client_addr): | |||
status = REDIS.get(REDIS_CLIENT_CERT_STATUS_PREFIX+client_addr) | |||
if status is None: | |||
return None | |||
enabled = status.decode() == "True" | |||
return enabled | |||
def set_ip_cert_status(client_addr, enabled): | |||
REDIS.set(REDIS_CLIENT_CERT_STATUS_PREFIX+client_addr, str(enabled)) | |||
bubble_log('set_ip_cert_status: set '+client_addr+' = '+str(enabled)) | |||
def get_device_security_level(client_addr): | |||
level = REDIS.get(REDIS_KEY_DEVICE_SECURITY_LEVEL_PREFIX+client_addr) | |||
if level is None: | |||
return 'maximum' | |||
return level.decode() | |||
def get_local_ips(): | |||
@@ -64,17 +60,6 @@ def get_local_ips(): | |||
return local_ips | |||
def get_cert_validation_host(): | |||
global cert_validation_host | |||
if cert_validation_host is None: | |||
cert_validation_host = REDIS.get('certValidationHost') | |||
if cert_validation_host is not None: | |||
cert_validation_host = cert_validation_host.decode() | |||
# bubble_log('get_cert_validation_host: initialized to '+cert_validation_host) | |||
# bubble_log('get_cert_validation_host: returning '+cert_validation_host) | |||
return cert_validation_host | |||
def passthru_cache_prefix(client_addr, server_addr): | |||
return REDIS_PASSTHRU_PREFIX + client_addr + '_' + server_addr | |||
@@ -103,29 +88,15 @@ class TlsFeedback(TlsLayer): | |||
fqdns = fqdns_for_addr(server_address) | |||
try: | |||
super(TlsFeedback, self)._establish_tls_with_client() | |||
if fqdns and get_cert_validation_host() in fqdns: | |||
# bubble_log('_establish_tls_with_client: TLS success for '+repr(server_address)+', enabling SSL interception for client '+client_address) | |||
set_ip_cert_status(client_address, True) | |||
except TlsProtocolException as e: | |||
cache_key = passthru_cache_prefix(client_address, server_address) | |||
bubble_log('_establish_tls_with_client: TLS error for '+repr(server_address)+', enabling passthru for client '+client_address+' with cache_key='+cache_key) | |||
if fqdns and get_cert_validation_host() in fqdns: | |||
set_ip_cert_status(client_address, False) | |||
else: | |||
redis_set(cache_key, json.dumps({'fqdns': fqdns, 'addr': server_address, 'passthru': True}), ex=REDIS_PASSTHRU_DURATION) | |||
redis_set(cache_key, json.dumps({'fqdns': fqdns, 'addr': server_address, 'passthru': True}), ex=REDIS_PASSTHRU_DURATION) | |||
raise e | |||
def check_bubble_passthru(client_addr, addr, fqdns): | |||
cert_status = get_ip_cert_status(client_addr) | |||
if cert_status is not None and not cert_status: | |||
bubble_log('check_bubble_passthru: returning True because cert_status for '+client_addr+' was False') | |||
return {'fqdns': fqdns, 'addr': addr, 'passthru': True} | |||
else: | |||
bubble_log('check_bubble_passthru: request is NOT for cert_validation_host: '+cert_validation_host+", it is for one of fqdn="+repr(fqdns)+", checking bubble_passthru...") | |||
passthru = bubble_passthru(client_addr, addr, fqdns) | |||
if passthru is None or passthru: | |||
bubble_log('check_bubble_passthru: bubble_passthru returned '+repr(passthru)+' for FQDN/addr '+repr(fqdns)+'/'+repr(addr)+', returning True') | |||
@@ -139,9 +110,6 @@ def should_passthru(client_addr, addr, fqdns): | |||
if addr in get_local_ips(): | |||
# bubble_log('should_passthru: local ip is always passthru: '+addr) | |||
return {'fqdns': fqdns, 'addr': addr, 'passthru': True} | |||
else: | |||
# bubble_log('should_passthru: addr ('+addr+') is not a local ip: '+repr(get_local_ips())) | |||
pass | |||
cache_key = passthru_cache_prefix(client_addr, addr) | |||
prefix = 'should_passthru: ip='+repr(addr)+' (fqdns='+repr(fqdns)+') cache_key='+cache_key+': ' | |||
@@ -163,25 +131,38 @@ def should_passthru(client_addr, addr, fqdns): | |||
def next_layer(next_layer): | |||
if isinstance(next_layer, TlsLayer) and next_layer._client_tls: | |||
client_address = next_layer.client_conn.address[0] | |||
server_address = next_layer.server_conn.address[0] | |||
client_addr = next_layer.client_conn.address[0] | |||
server_addr = next_layer.server_conn.address[0] | |||
fqdns = fqdns_for_addr(server_address) | |||
validation_host = get_cert_validation_host() | |||
if fqdns and validation_host in fqdns: | |||
bubble_log('next_layer: never passing thru (always getting feedback) for cert_validation_host='+validation_host) | |||
next_layer.__class__ = TlsFeedback | |||
fqdns = fqdns_for_addr(server_addr) | |||
no_fqdns = fqdns is None or len(fqdns) == 0 | |||
security_level = get_device_security_level(client_addr) | |||
if server_addr in get_local_ips(): | |||
bubble_log('next_layer: enabling passthru for LOCAL server='+server_addr+' regardless of security_level='+security_level+' for client='+client_addr) | |||
passthru = FORCE_PASSTHRU | |||
elif security_level == 'disabled' or security_level == 'basic': | |||
bubble_log('next_layer: enabling passthru for server='+server_addr+' because security_level='+security_level+' for client='+client_addr) | |||
passthru = FORCE_PASSTHRU | |||
elif security_level == 'standard' and no_fqdns: | |||
bubble_log('next_layer: enabling passthru for server='+server_addr+' because no FQDN found and security_level='+security_level+' for client='+client_addr) | |||
passthru = FORCE_PASSTHRU | |||
elif security_level == 'maximum' and no_fqdns: | |||
bubble_log('next_layer: disabling passthru (no TlsFeedback) for server='+server_addr+' because no FQDN found and security_level='+security_level+' for client='+client_addr) | |||
return | |||
else: | |||
bubble_log('next_layer: checking should_passthru for client_address='+client_address) | |||
passthru = should_passthru(client_address, server_address, fqdns) | |||
if passthru is None or passthru['passthru']: | |||
# bubble_log('next_layer: TLS passthru for ' + repr(next_layer.server_conn.address)) | |||
if passthru is not None and 'fqdns' in passthru: | |||
bubble_activity_log(client_address, server_address, 'tls_passthru', passthru['fqdns']) | |||
next_layer_replacement = RawTCPLayer(next_layer.ctx, ignore=True) | |||
next_layer.reply.send(next_layer_replacement) | |||
else: | |||
# bubble_log('next_layer: NO PASSTHRU (getting feedback) for client_address='+client_address+', server_address='+server_address) | |||
bubble_activity_log(client_address, server_address, 'tls_intercept', passthru['fqdns']) | |||
next_layer.__class__ = TlsFeedback | |||
bubble_log('next_layer: checking should_passthru for server='+server_addr+', client='+client_addr+' with security_level='+security_level) | |||
passthru = should_passthru(client_addr, server_addr, fqdns) | |||
if passthru is None or passthru['passthru']: | |||
bubble_log('next_layer: enabling passthru for ' + repr(next_layer.server_conn.address)) | |||
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) | |||
else: | |||
bubble_log('next_layer: disabling passthru (with TlsFeedback) for client_addr='+client_addr+', server_addr='+server_addr) | |||
bubble_activity_log(client_addr, server_addr, 'tls_intercept', fqdns) | |||
next_layer.__class__ = TlsFeedback |