feat(mitmproxy): Add transparent mode, filtering addon, and whitelist
- Add nftables transparent mode support with automatic REDIRECT rules - Create SecuBox Python filter addon for CDN/Media/Ad tracking - Add whitelist/bypass configuration for IPs and domains - Expand UCI config with transparent, whitelist, filtering sections - Update RPCD backend with new config methods and firewall control - Update LuCI settings view with all new configuration options - Add new API methods: firewall_setup, firewall_clear, list management Features: - Transparent proxy with nftables integration - CDN tracking (Cloudflare, Akamai, Fastly, etc.) - Media streaming tracking (YouTube, Netflix, Spotify) - Ad/tracker blocking - IP and domain whitelist bypass Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
4e5d5275f9
commit
fe222d542c
@ -12,6 +12,26 @@ var callGetConfig = rpc.declare({
|
|||||||
method: 'get_config'
|
method: 'get_config'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var callGetTransparentConfig = rpc.declare({
|
||||||
|
object: 'luci.mitmproxy',
|
||||||
|
method: 'get_transparent_config'
|
||||||
|
});
|
||||||
|
|
||||||
|
var callGetWhitelistConfig = rpc.declare({
|
||||||
|
object: 'luci.mitmproxy',
|
||||||
|
method: 'get_whitelist_config'
|
||||||
|
});
|
||||||
|
|
||||||
|
var callGetFilteringConfig = rpc.declare({
|
||||||
|
object: 'luci.mitmproxy',
|
||||||
|
method: 'get_filtering_config'
|
||||||
|
});
|
||||||
|
|
||||||
|
var callGetAllConfig = rpc.declare({
|
||||||
|
object: 'luci.mitmproxy',
|
||||||
|
method: 'get_all_config'
|
||||||
|
});
|
||||||
|
|
||||||
var callGetStats = rpc.declare({
|
var callGetStats = rpc.declare({
|
||||||
object: 'luci.mitmproxy',
|
object: 'luci.mitmproxy',
|
||||||
method: 'get_stats'
|
method: 'get_stats'
|
||||||
@ -20,7 +40,7 @@ var callGetStats = rpc.declare({
|
|||||||
var callGetRequests = rpc.declare({
|
var callGetRequests = rpc.declare({
|
||||||
object: 'luci.mitmproxy',
|
object: 'luci.mitmproxy',
|
||||||
method: 'get_requests',
|
method: 'get_requests',
|
||||||
params: ['limit']
|
params: ['limit', 'category']
|
||||||
});
|
});
|
||||||
|
|
||||||
var callGetTopHosts = rpc.declare({
|
var callGetTopHosts = rpc.declare({
|
||||||
@ -49,12 +69,34 @@ var callServiceRestart = rpc.declare({
|
|||||||
method: 'service_restart'
|
method: 'service_restart'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var callFirewallSetup = rpc.declare({
|
||||||
|
object: 'luci.mitmproxy',
|
||||||
|
method: 'firewall_setup'
|
||||||
|
});
|
||||||
|
|
||||||
|
var callFirewallClear = rpc.declare({
|
||||||
|
object: 'luci.mitmproxy',
|
||||||
|
method: 'firewall_clear'
|
||||||
|
});
|
||||||
|
|
||||||
var callSetConfig = rpc.declare({
|
var callSetConfig = rpc.declare({
|
||||||
object: 'luci.mitmproxy',
|
object: 'luci.mitmproxy',
|
||||||
method: 'set_config',
|
method: 'set_config',
|
||||||
params: ['key', 'value']
|
params: ['key', 'value']
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var callAddToList = rpc.declare({
|
||||||
|
object: 'luci.mitmproxy',
|
||||||
|
method: 'add_to_list',
|
||||||
|
params: ['key', 'value']
|
||||||
|
});
|
||||||
|
|
||||||
|
var callRemoveFromList = rpc.declare({
|
||||||
|
object: 'luci.mitmproxy',
|
||||||
|
method: 'remove_from_list',
|
||||||
|
params: ['key', 'value']
|
||||||
|
});
|
||||||
|
|
||||||
var callClearData = rpc.declare({
|
var callClearData = rpc.declare({
|
||||||
object: 'luci.mitmproxy',
|
object: 'luci.mitmproxy',
|
||||||
method: 'clear_data'
|
method: 'clear_data'
|
||||||
@ -73,14 +115,45 @@ return baseclass.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getStats: function() {
|
getTransparentConfig: function() {
|
||||||
return callGetStats().catch(function() {
|
return callGetTransparentConfig().catch(function() {
|
||||||
return { total_requests: 0, unique_hosts: 0, flow_file_size: 0 };
|
return { enabled: false };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getRequests: function(limit) {
|
getWhitelistConfig: function() {
|
||||||
return callGetRequests(limit || 50).catch(function() {
|
return callGetWhitelistConfig().catch(function() {
|
||||||
|
return { enabled: true, bypass_ip: [], bypass_domain: [] };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getFilteringConfig: function() {
|
||||||
|
return callGetFilteringConfig().catch(function() {
|
||||||
|
return { enabled: false };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getAllConfig: function() {
|
||||||
|
return callGetAllConfig().catch(function() {
|
||||||
|
return { main: {}, transparent: {}, whitelist: {}, filtering: {} };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getStats: function() {
|
||||||
|
return callGetStats().catch(function() {
|
||||||
|
return {
|
||||||
|
total_requests: 0,
|
||||||
|
unique_hosts: 0,
|
||||||
|
flow_file_size: 0,
|
||||||
|
cdn_requests: 0,
|
||||||
|
media_requests: 0,
|
||||||
|
blocked_ads: 0
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getRequests: function(limit, category) {
|
||||||
|
return callGetRequests(limit || 50, category || 'all').catch(function() {
|
||||||
return { requests: [] };
|
return { requests: [] };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -109,10 +182,26 @@ return baseclass.extend({
|
|||||||
return callServiceRestart();
|
return callServiceRestart();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
firewallSetup: function() {
|
||||||
|
return callFirewallSetup();
|
||||||
|
},
|
||||||
|
|
||||||
|
firewallClear: function() {
|
||||||
|
return callFirewallClear();
|
||||||
|
},
|
||||||
|
|
||||||
setConfig: function(key, value) {
|
setConfig: function(key, value) {
|
||||||
return callSetConfig(key, value);
|
return callSetConfig(key, value);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addToList: function(key, value) {
|
||||||
|
return callAddToList(key, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeFromList: function(key, value) {
|
||||||
|
return callRemoveFromList(key, value);
|
||||||
|
},
|
||||||
|
|
||||||
clearData: function() {
|
clearData: function() {
|
||||||
return callClearData();
|
return callClearData();
|
||||||
},
|
},
|
||||||
@ -121,14 +210,15 @@ return baseclass.extend({
|
|||||||
var self = this;
|
var self = this;
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
self.getStatus(),
|
self.getStatus(),
|
||||||
self.getConfig(),
|
self.getAllConfig(),
|
||||||
self.getStats(),
|
self.getStats(),
|
||||||
self.getTopHosts(10),
|
self.getTopHosts(10),
|
||||||
self.getCaInfo()
|
self.getCaInfo()
|
||||||
]).then(function(results) {
|
]).then(function(results) {
|
||||||
return {
|
return {
|
||||||
status: results[0],
|
status: results[0],
|
||||||
config: results[1],
|
config: results[1].main || results[1],
|
||||||
|
allConfig: results[1],
|
||||||
stats: results[2],
|
stats: results[2],
|
||||||
topHosts: results[3],
|
topHosts: results[3],
|
||||||
caInfo: results[4]
|
caInfo: results[4]
|
||||||
|
|||||||
@ -45,9 +45,11 @@ return view.extend({
|
|||||||
var m, s, o;
|
var m, s, o;
|
||||||
|
|
||||||
m = new form.Map('mitmproxy', _('mitmproxy Settings'),
|
m = new form.Map('mitmproxy', _('mitmproxy Settings'),
|
||||||
_('Configure the mitmproxy HTTPS interception proxy.'));
|
_('Configure the mitmproxy HTTPS interception proxy with transparent mode and filtering options.'));
|
||||||
|
|
||||||
// Main settings
|
// =====================================================================
|
||||||
|
// Main Proxy Configuration
|
||||||
|
// =====================================================================
|
||||||
s = m.section(form.TypedSection, 'mitmproxy', _('Proxy Configuration'));
|
s = m.section(form.TypedSection, 'mitmproxy', _('Proxy Configuration'));
|
||||||
s.anonymous = true;
|
s.anonymous = true;
|
||||||
s.addremove = false;
|
s.addremove = false;
|
||||||
@ -59,18 +61,13 @@ return view.extend({
|
|||||||
|
|
||||||
o = s.option(form.ListValue, 'mode', _('Proxy Mode'),
|
o = s.option(form.ListValue, 'mode', _('Proxy Mode'),
|
||||||
_('How clients connect to the proxy'));
|
_('How clients connect to the proxy'));
|
||||||
o.value('transparent', _('Transparent - Intercept traffic automatically'));
|
|
||||||
o.value('regular', _('Regular - Clients must configure proxy settings'));
|
o.value('regular', _('Regular - Clients must configure proxy settings'));
|
||||||
|
o.value('transparent', _('Transparent - Intercept traffic automatically via nftables'));
|
||||||
o.value('upstream', _('Upstream - Forward to another proxy'));
|
o.value('upstream', _('Upstream - Forward to another proxy'));
|
||||||
o.default = 'transparent';
|
o.value('reverse', _('Reverse - Reverse proxy mode'));
|
||||||
|
o.default = 'regular';
|
||||||
|
|
||||||
o = s.option(form.Value, 'listen_host', _('Listen Address'),
|
o = s.option(form.Value, 'proxy_port', _('Proxy Port'),
|
||||||
_('IP address to bind the proxy to'));
|
|
||||||
o.default = '0.0.0.0';
|
|
||||||
o.placeholder = '0.0.0.0';
|
|
||||||
o.datatype = 'ipaddr';
|
|
||||||
|
|
||||||
o = s.option(form.Value, 'listen_port', _('Proxy Port'),
|
|
||||||
_('Port for HTTP/HTTPS interception'));
|
_('Port for HTTP/HTTPS interception'));
|
||||||
o.default = '8080';
|
o.default = '8080';
|
||||||
o.placeholder = '8080';
|
o.placeholder = '8080';
|
||||||
@ -88,10 +85,27 @@ return view.extend({
|
|||||||
o.placeholder = '8081';
|
o.placeholder = '8081';
|
||||||
o.datatype = 'port';
|
o.datatype = 'port';
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'data_path', _('Data Path'),
|
||||||
|
_('Directory for storing certificates and data'));
|
||||||
|
o.default = '/srv/mitmproxy';
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'memory_limit', _('Memory Limit'),
|
||||||
|
_('Maximum memory for the LXC container'));
|
||||||
|
o.default = '256M';
|
||||||
|
o.placeholder = '256M';
|
||||||
|
|
||||||
o = s.option(form.Flag, 'ssl_insecure', _('Allow Insecure SSL'),
|
o = s.option(form.Flag, 'ssl_insecure', _('Allow Insecure SSL'),
|
||||||
_('Accept invalid/self-signed SSL certificates from upstream servers'));
|
_('Accept invalid/self-signed SSL certificates from upstream servers'));
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'anticache', _('Anti-Cache'),
|
||||||
|
_('Strip cache headers to force fresh responses'));
|
||||||
|
o.default = '0';
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'anticomp', _('Anti-Compression'),
|
||||||
|
_('Disable compression to allow content inspection'));
|
||||||
|
o.default = '0';
|
||||||
|
|
||||||
o = s.option(form.ListValue, 'flow_detail', _('Log Detail Level'),
|
o = s.option(form.ListValue, 'flow_detail', _('Log Detail Level'),
|
||||||
_('Amount of detail in flow logs'));
|
_('Amount of detail in flow logs'));
|
||||||
o.value('0', _('Minimal'));
|
o.value('0', _('Minimal'));
|
||||||
@ -99,87 +113,141 @@ return view.extend({
|
|||||||
o.value('2', _('Full headers'));
|
o.value('2', _('Full headers'));
|
||||||
o.value('3', _('Full headers + body preview'));
|
o.value('3', _('Full headers + body preview'));
|
||||||
o.value('4', _('Full headers + full body'));
|
o.value('4', _('Full headers + full body'));
|
||||||
o.default = '2';
|
o.default = '1';
|
||||||
|
|
||||||
// Capture settings
|
o = s.option(form.Value, 'upstream_proxy', _('Upstream Proxy'),
|
||||||
|
_('Forward traffic to this upstream proxy (e.g., http://proxy:8080)'));
|
||||||
|
o.depends('mode', 'upstream');
|
||||||
|
o.placeholder = 'http://proxy:8080';
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'reverse_target', _('Reverse Target'),
|
||||||
|
_('Target server for reverse proxy mode (e.g., http://localhost:80)'));
|
||||||
|
o.depends('mode', 'reverse');
|
||||||
|
o.placeholder = 'http://localhost:80';
|
||||||
|
|
||||||
|
// =====================================================================
|
||||||
|
// Transparent Mode Settings
|
||||||
|
// =====================================================================
|
||||||
|
s = m.section(form.TypedSection, 'transparent', _('Transparent Mode'));
|
||||||
|
s.anonymous = true;
|
||||||
|
s.addremove = false;
|
||||||
|
s.tab('transparent', _('Firewall Settings'));
|
||||||
|
|
||||||
|
o = s.taboption('transparent', form.Flag, 'enabled', _('Enable Transparent Firewall'),
|
||||||
|
_('Automatically setup nftables rules to redirect traffic'));
|
||||||
|
o.default = '0';
|
||||||
|
|
||||||
|
o = s.taboption('transparent', form.Value, 'interface', _('Intercept Interface'),
|
||||||
|
_('Network interface to intercept traffic from'));
|
||||||
|
o.default = 'br-lan';
|
||||||
|
o.placeholder = 'br-lan';
|
||||||
|
o.depends('enabled', '1');
|
||||||
|
|
||||||
|
o = s.taboption('transparent', form.Flag, 'redirect_http', _('Redirect HTTP'),
|
||||||
|
_('Intercept plain HTTP traffic'));
|
||||||
|
o.default = '1';
|
||||||
|
o.depends('enabled', '1');
|
||||||
|
|
||||||
|
o = s.taboption('transparent', form.Flag, 'redirect_https', _('Redirect HTTPS'),
|
||||||
|
_('Intercept HTTPS traffic (requires CA certificate on clients)'));
|
||||||
|
o.default = '1';
|
||||||
|
o.depends('enabled', '1');
|
||||||
|
|
||||||
|
o = s.taboption('transparent', form.Value, 'http_port', _('HTTP Port'),
|
||||||
|
_('Source port to intercept for HTTP'));
|
||||||
|
o.default = '80';
|
||||||
|
o.datatype = 'port';
|
||||||
|
o.depends('redirect_http', '1');
|
||||||
|
|
||||||
|
o = s.taboption('transparent', form.Value, 'https_port', _('HTTPS Port'),
|
||||||
|
_('Source port to intercept for HTTPS'));
|
||||||
|
o.default = '443';
|
||||||
|
o.datatype = 'port';
|
||||||
|
o.depends('redirect_https', '1');
|
||||||
|
|
||||||
|
// =====================================================================
|
||||||
|
// Whitelist/Bypass Settings
|
||||||
|
// =====================================================================
|
||||||
|
s = m.section(form.TypedSection, 'whitelist', _('Whitelist / Bypass'));
|
||||||
|
s.anonymous = true;
|
||||||
|
s.addremove = false;
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'enabled', _('Enable Whitelist'),
|
||||||
|
_('Skip interception for whitelisted IPs and domains'));
|
||||||
|
o.default = '1';
|
||||||
|
|
||||||
|
o = s.option(form.DynamicList, 'bypass_ip', _('Bypass IP Addresses'),
|
||||||
|
_('IP addresses or CIDR ranges that bypass the proxy'));
|
||||||
|
o.placeholder = '192.168.1.0/24';
|
||||||
|
o.depends('enabled', '1');
|
||||||
|
|
||||||
|
o = s.option(form.DynamicList, 'bypass_domain', _('Bypass Domains'),
|
||||||
|
_('Domain patterns that bypass the proxy (for domain-based bypass, requires additional configuration)'));
|
||||||
|
o.placeholder = 'banking.com';
|
||||||
|
o.depends('enabled', '1');
|
||||||
|
|
||||||
|
// =====================================================================
|
||||||
|
// Filtering / CDN Tracking
|
||||||
|
// =====================================================================
|
||||||
|
s = m.section(form.TypedSection, 'filtering', _('Filtering & Analytics'));
|
||||||
|
s.anonymous = true;
|
||||||
|
s.addremove = false;
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'enabled', _('Enable Filtering Addon'),
|
||||||
|
_('Load the SecuBox filtering addon for CDN/Media tracking and ad blocking'));
|
||||||
|
o.default = '0';
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'log_requests', _('Log All Requests'),
|
||||||
|
_('Log request details to JSON file for analysis'));
|
||||||
|
o.default = '1';
|
||||||
|
o.depends('enabled', '1');
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'filter_cdn', _('Track CDN Traffic'),
|
||||||
|
_('Log and categorize CDN requests (Cloudflare, Akamai, Fastly, etc.)'));
|
||||||
|
o.default = '0';
|
||||||
|
o.depends('enabled', '1');
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'filter_media', _('Track Media Streaming'),
|
||||||
|
_('Log and categorize streaming media requests (YouTube, Netflix, Spotify, etc.)'));
|
||||||
|
o.default = '0';
|
||||||
|
o.depends('enabled', '1');
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'block_ads', _('Block Ads & Trackers'),
|
||||||
|
_('Block known advertising and tracking domains'));
|
||||||
|
o.default = '0';
|
||||||
|
o.depends('enabled', '1');
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'addon_script', _('Addon Script Path'),
|
||||||
|
_('Path to the Python filtering addon'));
|
||||||
|
o.default = '/etc/mitmproxy/addons/secubox_filter.py';
|
||||||
|
o.depends('enabled', '1');
|
||||||
|
|
||||||
|
// =====================================================================
|
||||||
|
// Capture Settings
|
||||||
|
// =====================================================================
|
||||||
s = m.section(form.TypedSection, 'capture', _('Capture Settings'));
|
s = m.section(form.TypedSection, 'capture', _('Capture Settings'));
|
||||||
s.anonymous = true;
|
s.anonymous = true;
|
||||||
s.addremove = false;
|
s.addremove = false;
|
||||||
|
|
||||||
o = s.option(form.Flag, 'save_flows', _('Save Flows'),
|
o = s.option(form.Flag, 'save_flows', _('Save Flows'),
|
||||||
_('Save captured flows to disk for later replay'));
|
_('Save captured flows to disk for later replay'));
|
||||||
o.default = '1';
|
|
||||||
|
|
||||||
o = s.option(form.Value, 'flow_file', _('Flow File'),
|
|
||||||
_('Path to save captured flows'));
|
|
||||||
o.default = '/tmp/mitmproxy/flows.bin';
|
|
||||||
o.depends('save_flows', '1');
|
|
||||||
|
|
||||||
o = s.option(form.Flag, 'capture_urls', _('Capture URLs'),
|
|
||||||
_('Log full URLs of requests'));
|
|
||||||
o.default = '1';
|
|
||||||
|
|
||||||
o = s.option(form.Flag, 'capture_cookies', _('Capture Cookies'),
|
|
||||||
_('Log cookie headers'));
|
|
||||||
o.default = '1';
|
|
||||||
|
|
||||||
o = s.option(form.Flag, 'capture_headers', _('Capture Headers'),
|
|
||||||
_('Log all HTTP headers'));
|
|
||||||
o.default = '1';
|
|
||||||
|
|
||||||
o = s.option(form.Flag, 'capture_body', _('Capture Body'),
|
|
||||||
_('Log request/response bodies (increases storage usage)'));
|
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
|
|
||||||
// Logging settings
|
o = s.option(form.Flag, 'capture_request_headers', _('Capture Request Headers'),
|
||||||
s = m.section(form.TypedSection, 'logging', _('Logging'));
|
_('Include request headers in logs'));
|
||||||
s.anonymous = true;
|
|
||||||
s.addremove = false;
|
|
||||||
|
|
||||||
o = s.option(form.Flag, 'enabled', _('Enable Request Logging'),
|
|
||||||
_('Log requests to file'));
|
|
||||||
o.default = '1';
|
o.default = '1';
|
||||||
|
|
||||||
o = s.option(form.Value, 'log_file', _('Log File'),
|
o = s.option(form.Flag, 'capture_response_headers', _('Capture Response Headers'),
|
||||||
_('Path to request log file'));
|
_('Include response headers in logs'));
|
||||||
o.default = '/tmp/mitmproxy/requests.log';
|
o.default = '1';
|
||||||
o.depends('enabled', '1');
|
|
||||||
|
|
||||||
o = s.option(form.ListValue, 'log_format', _('Log Format'),
|
o = s.option(form.Flag, 'capture_request_body', _('Capture Request Body'),
|
||||||
_('Format of log entries'));
|
_('Include request body in logs (increases storage usage)'));
|
||||||
o.value('json', _('JSON'));
|
|
||||||
o.value('text', _('Plain text'));
|
|
||||||
o.default = 'json';
|
|
||||||
o.depends('enabled', '1');
|
|
||||||
|
|
||||||
o = s.option(form.Value, 'max_size', _('Max Log Size (MB)'),
|
|
||||||
_('Rotate log when it reaches this size'));
|
|
||||||
o.default = '10';
|
|
||||||
o.datatype = 'uinteger';
|
|
||||||
o.depends('enabled', '1');
|
|
||||||
|
|
||||||
// Filter settings
|
|
||||||
s = m.section(form.TypedSection, 'filter', _('Filtering'));
|
|
||||||
s.anonymous = true;
|
|
||||||
s.addremove = false;
|
|
||||||
|
|
||||||
o = s.option(form.Flag, 'enabled', _('Enable Filtering'),
|
|
||||||
_('Enable content filtering'));
|
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
|
|
||||||
o = s.option(form.Flag, 'block_ads', _('Block Ads'),
|
o = s.option(form.Flag, 'capture_response_body', _('Capture Response Body'),
|
||||||
_('Block known advertising domains'));
|
_('Include response body in logs (increases storage usage)'));
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.depends('enabled', '1');
|
|
||||||
|
|
||||||
o = s.option(form.Flag, 'block_trackers', _('Block Trackers'),
|
|
||||||
_('Block known tracking domains'));
|
|
||||||
o.default = '0';
|
|
||||||
o.depends('enabled', '1');
|
|
||||||
|
|
||||||
o = s.option(form.DynamicList, 'ignore_host', _('Ignore Hosts'),
|
|
||||||
_('Hosts to pass through without interception'));
|
|
||||||
o.placeholder = '*.example.com';
|
|
||||||
|
|
||||||
var wrapper = E('div', { 'class': 'secubox-page-wrapper' });
|
var wrapper = E('div', { 'class': 'secubox-page-wrapper' });
|
||||||
wrapper.appendChild(SbHeader.render());
|
wrapper.appendChild(SbHeader.render());
|
||||||
|
|||||||
@ -12,13 +12,6 @@ CONF_DIR="$DATA_DIR"
|
|||||||
LOG_FILE="$DATA_DIR/requests.log"
|
LOG_FILE="$DATA_DIR/requests.log"
|
||||||
FLOW_FILE="$DATA_DIR/flows.bin"
|
FLOW_FILE="$DATA_DIR/flows.bin"
|
||||||
|
|
||||||
# JSON helpers
|
|
||||||
json_init() { echo "{"; }
|
|
||||||
json_close() { echo "}"; }
|
|
||||||
json_add_string() { printf '"%s":"%s"' "$1" "$2"; }
|
|
||||||
json_add_int() { printf '"%s":%d' "$1" "${2:-0}"; }
|
|
||||||
json_add_bool() { [ "$2" = "1" ] && printf '"%s":true' "$1" || printf '"%s":false' "$1"; }
|
|
||||||
|
|
||||||
# Get service status
|
# Get service status
|
||||||
get_status() {
|
get_status() {
|
||||||
local running=0
|
local running=0
|
||||||
@ -26,6 +19,7 @@ get_status() {
|
|||||||
local mode="unknown"
|
local mode="unknown"
|
||||||
local web_url=""
|
local web_url=""
|
||||||
local lxc_state=""
|
local lxc_state=""
|
||||||
|
local nft_active="false"
|
||||||
|
|
||||||
# Check LXC container status
|
# Check LXC container status
|
||||||
if command -v lxc-info >/dev/null 2>&1; then
|
if command -v lxc-info >/dev/null 2>&1; then
|
||||||
@ -50,10 +44,16 @@ get_status() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check nftables rules
|
||||||
|
if command -v nft >/dev/null 2>&1; then
|
||||||
|
nft list table inet mitmproxy >/dev/null 2>&1 && nft_active="true"
|
||||||
|
fi
|
||||||
|
|
||||||
local enabled=$(uci -q get mitmproxy.main.enabled || echo "0")
|
local enabled=$(uci -q get mitmproxy.main.enabled || echo "0")
|
||||||
local proxy_port=$(uci -q get mitmproxy.main.proxy_port || echo "8080")
|
local proxy_port=$(uci -q get mitmproxy.main.proxy_port || echo "8080")
|
||||||
local web_port=$(uci -q get mitmproxy.main.web_port || echo "8081")
|
local web_port=$(uci -q get mitmproxy.main.web_port || echo "8081")
|
||||||
local proxy_mode=$(uci -q get mitmproxy.main.mode || echo "regular")
|
local proxy_mode=$(uci -q get mitmproxy.main.mode || echo "regular")
|
||||||
|
local filtering_enabled=$(uci -q get mitmproxy.filtering.enabled || echo "0")
|
||||||
local router_ip=$(uci -q get network.lan.ipaddr || echo "192.168.1.1")
|
local router_ip=$(uci -q get network.lan.ipaddr || echo "192.168.1.1")
|
||||||
|
|
||||||
[ "$running" = "1" ] && [ "$mode" = "mitmweb" ] && web_url="http://${router_ip}:${web_port}"
|
[ "$running" = "1" ] && [ "$mode" = "mitmweb" ] && web_url="http://${router_ip}:${web_port}"
|
||||||
@ -69,12 +69,14 @@ get_status() {
|
|||||||
"proxy_port": $proxy_port,
|
"proxy_port": $proxy_port,
|
||||||
"web_port": $web_port,
|
"web_port": $web_port,
|
||||||
"web_url": "$web_url",
|
"web_url": "$web_url",
|
||||||
"ca_installed": $([ -f "$CONF_DIR/mitmproxy-ca-cert.pem" ] && echo "true" || echo "false")
|
"ca_installed": $([ -f "$CONF_DIR/mitmproxy-ca-cert.pem" ] && echo "true" || echo "false"),
|
||||||
|
"nft_active": $nft_active,
|
||||||
|
"filtering_enabled": $([ "$filtering_enabled" = "1" ] && echo "true" || echo "false")
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get configuration
|
# Get main configuration
|
||||||
get_config() {
|
get_config() {
|
||||||
local enabled=$(uci -q get mitmproxy.main.enabled || echo "0")
|
local enabled=$(uci -q get mitmproxy.main.enabled || echo "0")
|
||||||
local mode=$(uci -q get mitmproxy.main.mode || echo "regular")
|
local mode=$(uci -q get mitmproxy.main.mode || echo "regular")
|
||||||
@ -105,16 +107,102 @@ get_config() {
|
|||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Get transparent mode configuration
|
||||||
|
get_transparent_config() {
|
||||||
|
local enabled=$(uci -q get mitmproxy.transparent.enabled || echo "0")
|
||||||
|
local interface=$(uci -q get mitmproxy.transparent.interface || echo "br-lan")
|
||||||
|
local redirect_http=$(uci -q get mitmproxy.transparent.redirect_http || echo "1")
|
||||||
|
local redirect_https=$(uci -q get mitmproxy.transparent.redirect_https || echo "1")
|
||||||
|
local http_port=$(uci -q get mitmproxy.transparent.http_port || echo "80")
|
||||||
|
local https_port=$(uci -q get mitmproxy.transparent.https_port || echo "443")
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
{
|
||||||
|
"enabled": $([ "$enabled" = "1" ] && echo "true" || echo "false"),
|
||||||
|
"interface": "$interface",
|
||||||
|
"redirect_http": $([ "$redirect_http" = "1" ] && echo "true" || echo "false"),
|
||||||
|
"redirect_https": $([ "$redirect_https" = "1" ] && echo "true" || echo "false"),
|
||||||
|
"http_port": $http_port,
|
||||||
|
"https_port": $https_port
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get whitelist configuration
|
||||||
|
get_whitelist_config() {
|
||||||
|
local enabled=$(uci -q get mitmproxy.whitelist.enabled || echo "1")
|
||||||
|
|
||||||
|
# Get bypass_ip list
|
||||||
|
local bypass_ips=$(uci -q get mitmproxy.whitelist.bypass_ip 2>/dev/null | tr ' ' '\n' | while read ip; do
|
||||||
|
[ -n "$ip" ] && printf '"%s",' "$ip"
|
||||||
|
done | sed 's/,$//')
|
||||||
|
|
||||||
|
# Get bypass_domain list
|
||||||
|
local bypass_domains=$(uci -q get mitmproxy.whitelist.bypass_domain 2>/dev/null | tr ' ' '\n' | while read domain; do
|
||||||
|
[ -n "$domain" ] && printf '"%s",' "$domain"
|
||||||
|
done | sed 's/,$//')
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
{
|
||||||
|
"enabled": $([ "$enabled" = "1" ] && echo "true" || echo "false"),
|
||||||
|
"bypass_ip": [${bypass_ips}],
|
||||||
|
"bypass_domain": [${bypass_domains}]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get filtering configuration
|
||||||
|
get_filtering_config() {
|
||||||
|
local enabled=$(uci -q get mitmproxy.filtering.enabled || echo "0")
|
||||||
|
local log_requests=$(uci -q get mitmproxy.filtering.log_requests || echo "1")
|
||||||
|
local filter_cdn=$(uci -q get mitmproxy.filtering.filter_cdn || echo "0")
|
||||||
|
local filter_media=$(uci -q get mitmproxy.filtering.filter_media || echo "0")
|
||||||
|
local block_ads=$(uci -q get mitmproxy.filtering.block_ads || echo "0")
|
||||||
|
local addon_script=$(uci -q get mitmproxy.filtering.addon_script || echo "/etc/mitmproxy/addons/secubox_filter.py")
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
{
|
||||||
|
"enabled": $([ "$enabled" = "1" ] && echo "true" || echo "false"),
|
||||||
|
"log_requests": $([ "$log_requests" = "1" ] && echo "true" || echo "false"),
|
||||||
|
"filter_cdn": $([ "$filter_cdn" = "1" ] && echo "true" || echo "false"),
|
||||||
|
"filter_media": $([ "$filter_media" = "1" ] && echo "true" || echo "false"),
|
||||||
|
"block_ads": $([ "$block_ads" = "1" ] && echo "true" || echo "false"),
|
||||||
|
"addon_script": "$addon_script"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get all configuration in one call
|
||||||
|
get_all_config() {
|
||||||
|
cat <<EOF
|
||||||
|
{
|
||||||
|
"main": $(get_config),
|
||||||
|
"transparent": $(get_transparent_config),
|
||||||
|
"whitelist": $(get_whitelist_config),
|
||||||
|
"filtering": $(get_filtering_config)
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
# Get statistics
|
# Get statistics
|
||||||
get_stats() {
|
get_stats() {
|
||||||
local total_requests=0
|
local total_requests=0
|
||||||
local unique_hosts=0
|
local unique_hosts=0
|
||||||
local flow_size="0"
|
local flow_size="0"
|
||||||
|
local cdn_requests=0
|
||||||
|
local media_requests=0
|
||||||
|
local blocked_ads=0
|
||||||
|
|
||||||
if [ -f "$LOG_FILE" ]; then
|
if [ -f "$LOG_FILE" ]; then
|
||||||
total_requests=$(wc -l < "$LOG_FILE" 2>/dev/null || echo "0")
|
total_requests=$(wc -l < "$LOG_FILE" 2>/dev/null || echo "0")
|
||||||
if command -v jq >/dev/null 2>&1; then
|
# Use jsonfilter for parsing (OpenWrt native)
|
||||||
unique_hosts=$(jq -r '.request.host // .host // empty' "$LOG_FILE" 2>/dev/null | sort -u | wc -l)
|
if command -v jsonfilter >/dev/null 2>&1; then
|
||||||
|
unique_hosts=$(cat "$LOG_FILE" 2>/dev/null | while read line; do
|
||||||
|
echo "$line" | jsonfilter -e '@.request.host' 2>/dev/null
|
||||||
|
done | sort -u | wc -l)
|
||||||
|
cdn_requests=$(grep -c '"category":"cdn"' "$LOG_FILE" 2>/dev/null || echo "0")
|
||||||
|
media_requests=$(grep -c '"category":"media"' "$LOG_FILE" 2>/dev/null || echo "0")
|
||||||
|
blocked_ads=$(grep -c '"category":"blocked_ad"' "$LOG_FILE" 2>/dev/null || echo "0")
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -126,7 +214,10 @@ get_stats() {
|
|||||||
{
|
{
|
||||||
"total_requests": $total_requests,
|
"total_requests": $total_requests,
|
||||||
"unique_hosts": $unique_hosts,
|
"unique_hosts": $unique_hosts,
|
||||||
"flow_file_size": $flow_size
|
"flow_file_size": $flow_size,
|
||||||
|
"cdn_requests": $cdn_requests,
|
||||||
|
"media_requests": $media_requests,
|
||||||
|
"blocked_ads": $blocked_ads
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
@ -134,18 +225,24 @@ EOF
|
|||||||
# Get recent requests
|
# Get recent requests
|
||||||
get_requests() {
|
get_requests() {
|
||||||
local limit="${1:-50}"
|
local limit="${1:-50}"
|
||||||
|
local category="${2:-}"
|
||||||
|
|
||||||
if [ ! -f "$LOG_FILE" ]; then
|
if [ ! -f "$LOG_FILE" ]; then
|
||||||
echo '{"requests":[]}'
|
echo '{"requests":[]}'
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v jq >/dev/null 2>&1; then
|
# Filter by category if specified
|
||||||
echo '{"requests":'
|
if [ -n "$category" ] && [ "$category" != "all" ]; then
|
||||||
tail -"$limit" "$LOG_FILE" 2>/dev/null | jq -s '.' 2>/dev/null || echo '[]'
|
echo '{"requests":['
|
||||||
echo '}'
|
grep "\"category\":\"$category\"" "$LOG_FILE" 2>/dev/null | tail -"$limit" | \
|
||||||
|
awk 'BEGIN{first=1}{if(!first)printf ",";first=0;print}' 2>/dev/null || echo ""
|
||||||
|
echo ']}'
|
||||||
else
|
else
|
||||||
echo '{"requests":[]}'
|
echo '{"requests":['
|
||||||
|
tail -"$limit" "$LOG_FILE" 2>/dev/null | \
|
||||||
|
awk 'BEGIN{first=1}{if(!first)printf ",";first=0;print}' 2>/dev/null || echo ""
|
||||||
|
echo ']}'
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,13 +250,15 @@ get_requests() {
|
|||||||
get_top_hosts() {
|
get_top_hosts() {
|
||||||
local limit="${1:-20}"
|
local limit="${1:-20}"
|
||||||
|
|
||||||
if [ ! -f "$LOG_FILE" ] || ! command -v jq >/dev/null 2>&1; then
|
if [ ! -f "$LOG_FILE" ]; then
|
||||||
echo '{"hosts":[]}'
|
echo '{"hosts":[]}'
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo '{"hosts":['
|
echo '{"hosts":['
|
||||||
jq -r '.request.host // .host // "unknown"' "$LOG_FILE" 2>/dev/null | \
|
# Parse JSON using grep/sed for compatibility
|
||||||
|
grep -o '"host":"[^"]*"' "$LOG_FILE" 2>/dev/null | \
|
||||||
|
sed 's/"host":"//;s/"$//' | \
|
||||||
sort | uniq -c | sort -rn | head -"$limit" | \
|
sort | uniq -c | sort -rn | head -"$limit" | \
|
||||||
awk 'BEGIN{first=1} {
|
awk 'BEGIN{first=1} {
|
||||||
if(!first) printf ",";
|
if(!first) printf ",";
|
||||||
@ -189,6 +288,28 @@ service_restart() {
|
|||||||
get_status
|
get_status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Setup firewall rules
|
||||||
|
firewall_setup() {
|
||||||
|
/usr/sbin/mitmproxyctl firewall-setup 2>&1
|
||||||
|
local result=$?
|
||||||
|
if [ $result -eq 0 ]; then
|
||||||
|
echo '{"success":true,"message":"Firewall rules applied"}'
|
||||||
|
else
|
||||||
|
echo '{"success":false,"message":"Failed to apply firewall rules"}'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clear firewall rules
|
||||||
|
firewall_clear() {
|
||||||
|
/usr/sbin/mitmproxyctl firewall-clear 2>&1
|
||||||
|
local result=$?
|
||||||
|
if [ $result -eq 0 ]; then
|
||||||
|
echo '{"success":true,"message":"Firewall rules cleared"}'
|
||||||
|
else
|
||||||
|
echo '{"success":false,"message":"Failed to clear firewall rules"}'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Set configuration
|
# Set configuration
|
||||||
set_config() {
|
set_config() {
|
||||||
local key="$1"
|
local key="$1"
|
||||||
@ -199,6 +320,21 @@ set_config() {
|
|||||||
save_flows|capture_*)
|
save_flows|capture_*)
|
||||||
section="capture"
|
section="capture"
|
||||||
;;
|
;;
|
||||||
|
redirect_*|interface|http_port|https_port)
|
||||||
|
section="transparent"
|
||||||
|
;;
|
||||||
|
bypass_ip|bypass_domain)
|
||||||
|
section="whitelist"
|
||||||
|
;;
|
||||||
|
filter_*|log_requests|block_ads|addon_script)
|
||||||
|
section="filtering"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Handle boolean conversion
|
||||||
|
case "$value" in
|
||||||
|
true) value="1" ;;
|
||||||
|
false) value="0" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
uci set "mitmproxy.$section.$key=$value"
|
uci set "mitmproxy.$section.$key=$value"
|
||||||
@ -206,6 +342,28 @@ set_config() {
|
|||||||
echo '{"success":true}'
|
echo '{"success":true}'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Add to list (for bypass_ip, bypass_domain)
|
||||||
|
add_to_list() {
|
||||||
|
local key="$1"
|
||||||
|
local value="$2"
|
||||||
|
local section="whitelist"
|
||||||
|
|
||||||
|
uci add_list "mitmproxy.$section.$key=$value"
|
||||||
|
uci commit mitmproxy
|
||||||
|
echo '{"success":true}'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remove from list
|
||||||
|
remove_from_list() {
|
||||||
|
local key="$1"
|
||||||
|
local value="$2"
|
||||||
|
local section="whitelist"
|
||||||
|
|
||||||
|
uci del_list "mitmproxy.$section.$key=$value"
|
||||||
|
uci commit mitmproxy
|
||||||
|
echo '{"success":true}'
|
||||||
|
}
|
||||||
|
|
||||||
# Clear captured data
|
# Clear captured data
|
||||||
clear_data() {
|
clear_data() {
|
||||||
rm -f "$DATA_DIR"/*.log "$DATA_DIR"/*.bin 2>/dev/null
|
rm -f "$DATA_DIR"/*.log "$DATA_DIR"/*.bin 2>/dev/null
|
||||||
@ -249,14 +407,22 @@ case "$1" in
|
|||||||
{
|
{
|
||||||
"get_status": {},
|
"get_status": {},
|
||||||
"get_config": {},
|
"get_config": {},
|
||||||
|
"get_transparent_config": {},
|
||||||
|
"get_whitelist_config": {},
|
||||||
|
"get_filtering_config": {},
|
||||||
|
"get_all_config": {},
|
||||||
"get_stats": {},
|
"get_stats": {},
|
||||||
"get_requests": {"limit": 50},
|
"get_requests": {"limit": 50, "category": "all"},
|
||||||
"get_top_hosts": {"limit": 20},
|
"get_top_hosts": {"limit": 20},
|
||||||
"get_ca_info": {},
|
"get_ca_info": {},
|
||||||
"service_start": {},
|
"service_start": {},
|
||||||
"service_stop": {},
|
"service_stop": {},
|
||||||
"service_restart": {},
|
"service_restart": {},
|
||||||
|
"firewall_setup": {},
|
||||||
|
"firewall_clear": {},
|
||||||
"set_config": {"key": "string", "value": "string"},
|
"set_config": {"key": "string", "value": "string"},
|
||||||
|
"add_to_list": {"key": "string", "value": "string"},
|
||||||
|
"remove_from_list": {"key": "string", "value": "string"},
|
||||||
"clear_data": {}
|
"clear_data": {}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
@ -269,13 +435,26 @@ EOF
|
|||||||
get_config)
|
get_config)
|
||||||
get_config
|
get_config
|
||||||
;;
|
;;
|
||||||
|
get_transparent_config)
|
||||||
|
get_transparent_config
|
||||||
|
;;
|
||||||
|
get_whitelist_config)
|
||||||
|
get_whitelist_config
|
||||||
|
;;
|
||||||
|
get_filtering_config)
|
||||||
|
get_filtering_config
|
||||||
|
;;
|
||||||
|
get_all_config)
|
||||||
|
get_all_config
|
||||||
|
;;
|
||||||
get_stats)
|
get_stats)
|
||||||
get_stats
|
get_stats
|
||||||
;;
|
;;
|
||||||
get_requests)
|
get_requests)
|
||||||
read -r input
|
read -r input
|
||||||
limit=$(echo "$input" | jsonfilter -e '@.limit' 2>/dev/null || echo "50")
|
limit=$(echo "$input" | jsonfilter -e '@.limit' 2>/dev/null || echo "50")
|
||||||
get_requests "$limit"
|
category=$(echo "$input" | jsonfilter -e '@.category' 2>/dev/null || echo "all")
|
||||||
|
get_requests "$limit" "$category"
|
||||||
;;
|
;;
|
||||||
get_top_hosts)
|
get_top_hosts)
|
||||||
read -r input
|
read -r input
|
||||||
@ -294,12 +473,30 @@ EOF
|
|||||||
service_restart)
|
service_restart)
|
||||||
service_restart
|
service_restart
|
||||||
;;
|
;;
|
||||||
|
firewall_setup)
|
||||||
|
firewall_setup
|
||||||
|
;;
|
||||||
|
firewall_clear)
|
||||||
|
firewall_clear
|
||||||
|
;;
|
||||||
set_config)
|
set_config)
|
||||||
read -r input
|
read -r input
|
||||||
key=$(echo "$input" | jsonfilter -e '@.key' 2>/dev/null)
|
key=$(echo "$input" | jsonfilter -e '@.key' 2>/dev/null)
|
||||||
value=$(echo "$input" | jsonfilter -e '@.value' 2>/dev/null)
|
value=$(echo "$input" | jsonfilter -e '@.value' 2>/dev/null)
|
||||||
set_config "$key" "$value"
|
set_config "$key" "$value"
|
||||||
;;
|
;;
|
||||||
|
add_to_list)
|
||||||
|
read -r input
|
||||||
|
key=$(echo "$input" | jsonfilter -e '@.key' 2>/dev/null)
|
||||||
|
value=$(echo "$input" | jsonfilter -e '@.value' 2>/dev/null)
|
||||||
|
add_to_list "$key" "$value"
|
||||||
|
;;
|
||||||
|
remove_from_list)
|
||||||
|
read -r input
|
||||||
|
key=$(echo "$input" | jsonfilter -e '@.key' 2>/dev/null)
|
||||||
|
value=$(echo "$input" | jsonfilter -e '@.value' 2>/dev/null)
|
||||||
|
remove_from_list "$key" "$value"
|
||||||
|
;;
|
||||||
clear_data)
|
clear_data)
|
||||||
clear_data
|
clear_data
|
||||||
;;
|
;;
|
||||||
|
|||||||
@ -14,3 +14,55 @@ config mitmproxy 'main'
|
|||||||
option anticache '0'
|
option anticache '0'
|
||||||
option anticomp '0'
|
option anticomp '0'
|
||||||
option flow_detail '1'
|
option flow_detail '1'
|
||||||
|
|
||||||
|
# Transparent mode settings
|
||||||
|
config transparent 'transparent'
|
||||||
|
option enabled '0'
|
||||||
|
# Interface to intercept traffic from (e.g., br-lan)
|
||||||
|
option interface 'br-lan'
|
||||||
|
# Redirect HTTP traffic (port 80)
|
||||||
|
option redirect_http '1'
|
||||||
|
# Redirect HTTPS traffic (port 443)
|
||||||
|
option redirect_https '1'
|
||||||
|
# Custom HTTP port (default 80)
|
||||||
|
option http_port '80'
|
||||||
|
# Custom HTTPS port (default 443)
|
||||||
|
option https_port '443'
|
||||||
|
|
||||||
|
# Whitelist/bypass - IPs and domains that bypass the proxy
|
||||||
|
config whitelist 'whitelist'
|
||||||
|
option enabled '1'
|
||||||
|
# Bypass local networks by default
|
||||||
|
list bypass_ip '10.0.0.0/8'
|
||||||
|
list bypass_ip '172.16.0.0/12'
|
||||||
|
list bypass_ip '192.168.0.0/16'
|
||||||
|
list bypass_ip '127.0.0.0/8'
|
||||||
|
# Bypass sensitive domains (banking, medical, etc.)
|
||||||
|
list bypass_domain 'banking'
|
||||||
|
list bypass_domain 'paypal.com'
|
||||||
|
list bypass_domain 'stripe.com'
|
||||||
|
# Add custom bypasses here
|
||||||
|
# list bypass_ip 'x.x.x.x'
|
||||||
|
# list bypass_domain 'example.com'
|
||||||
|
|
||||||
|
# CDN/MediaFlow filtering addon
|
||||||
|
config filtering 'filtering'
|
||||||
|
option enabled '0'
|
||||||
|
# Log all requests to JSON file
|
||||||
|
option log_requests '1'
|
||||||
|
# Filter CDN traffic (e.g., cloudflare, akamai, fastly)
|
||||||
|
option filter_cdn '0'
|
||||||
|
# Filter streaming media
|
||||||
|
option filter_media '0'
|
||||||
|
# Block ads and trackers
|
||||||
|
option block_ads '0'
|
||||||
|
# Custom filter script path
|
||||||
|
option addon_script '/etc/mitmproxy/addons/secubox_filter.py'
|
||||||
|
|
||||||
|
# Capture settings
|
||||||
|
config capture 'capture'
|
||||||
|
option save_flows '0'
|
||||||
|
option capture_request_headers '1'
|
||||||
|
option capture_response_headers '1'
|
||||||
|
option capture_request_body '0'
|
||||||
|
option capture_response_body '0'
|
||||||
|
|||||||
@ -1,15 +1,17 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# SecuBox mitmproxy manager - LXC container support
|
# SecuBox mitmproxy manager - LXC container support with transparent mode
|
||||||
# Copyright (C) 2024 CyberMind.fr
|
# Copyright (C) 2024-2025 CyberMind.fr
|
||||||
|
|
||||||
CONFIG="mitmproxy"
|
CONFIG="mitmproxy"
|
||||||
LXC_NAME="mitmproxy"
|
LXC_NAME="mitmproxy"
|
||||||
OPKG_UPDATED=0
|
OPKG_UPDATED=0
|
||||||
|
NFT_TABLE="mitmproxy"
|
||||||
|
|
||||||
# Paths
|
# Paths
|
||||||
LXC_PATH="/srv/lxc"
|
LXC_PATH="/srv/lxc"
|
||||||
LXC_ROOTFS="$LXC_PATH/$LXC_NAME/rootfs"
|
LXC_ROOTFS="$LXC_PATH/$LXC_NAME/rootfs"
|
||||||
LXC_CONFIG="$LXC_PATH/$LXC_NAME/config"
|
LXC_CONFIG="$LXC_PATH/$LXC_NAME/config"
|
||||||
|
ADDON_PATH="/etc/mitmproxy/addons"
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<'EOF'
|
cat <<'EOF'
|
||||||
@ -23,12 +25,14 @@ Commands:
|
|||||||
logs Show mitmproxy logs (use -f to follow)
|
logs Show mitmproxy logs (use -f to follow)
|
||||||
shell Open shell in container
|
shell Open shell in container
|
||||||
cert Show CA certificate info / export path
|
cert Show CA certificate info / export path
|
||||||
|
firewall-setup Setup nftables rules for transparent mode
|
||||||
|
firewall-clear Remove nftables transparent mode rules
|
||||||
service-run Internal: run container under procd
|
service-run Internal: run container under procd
|
||||||
service-stop Stop container
|
service-stop Stop container
|
||||||
|
|
||||||
Modes (configure in /etc/config/mitmproxy):
|
Modes (configure in /etc/config/mitmproxy):
|
||||||
regular - Standard HTTP/HTTPS proxy (default)
|
regular - Standard HTTP/HTTPS proxy (default)
|
||||||
transparent - Transparent proxy (requires iptables redirect)
|
transparent - Transparent proxy (auto-configures nftables)
|
||||||
upstream - Forward to upstream proxy
|
upstream - Forward to upstream proxy
|
||||||
reverse - Reverse proxy mode
|
reverse - Reverse proxy mode
|
||||||
|
|
||||||
@ -43,23 +47,44 @@ log_info() { echo "[INFO] $*"; }
|
|||||||
log_warn() { echo "[WARN] $*" >&2; }
|
log_warn() { echo "[WARN] $*" >&2; }
|
||||||
log_error() { echo "[ERROR] $*" >&2; }
|
log_error() { echo "[ERROR] $*" >&2; }
|
||||||
|
|
||||||
uci_get() { uci -q get ${CONFIG}.main.$1; }
|
uci_get() { uci -q get ${CONFIG}.$1; }
|
||||||
uci_set() { uci set ${CONFIG}.main.$1="$2" && uci commit ${CONFIG}; }
|
uci_set() { uci set ${CONFIG}.$1="$2" && uci commit ${CONFIG}; }
|
||||||
|
uci_get_list() { uci -q get ${CONFIG}.$1 2>/dev/null; }
|
||||||
|
|
||||||
# Load configuration with defaults
|
# Load configuration with defaults
|
||||||
load_config() {
|
load_config() {
|
||||||
proxy_port="$(uci_get proxy_port || echo 8080)"
|
# Main settings
|
||||||
web_port="$(uci_get web_port || echo 8081)"
|
proxy_port="$(uci_get main.proxy_port || echo 8080)"
|
||||||
web_host="$(uci_get web_host || echo 0.0.0.0)"
|
web_port="$(uci_get main.web_port || echo 8081)"
|
||||||
data_path="$(uci_get data_path || echo /srv/mitmproxy)"
|
web_host="$(uci_get main.web_host || echo 0.0.0.0)"
|
||||||
memory_limit="$(uci_get memory_limit || echo 256M)"
|
data_path="$(uci_get main.data_path || echo /srv/mitmproxy)"
|
||||||
mode="$(uci_get mode || echo regular)"
|
memory_limit="$(uci_get main.memory_limit || echo 256M)"
|
||||||
upstream_proxy="$(uci_get upstream_proxy || echo '')"
|
mode="$(uci_get main.mode || echo regular)"
|
||||||
reverse_target="$(uci_get reverse_target || echo '')"
|
upstream_proxy="$(uci_get main.upstream_proxy || echo '')"
|
||||||
ssl_insecure="$(uci_get ssl_insecure || echo 0)"
|
reverse_target="$(uci_get main.reverse_target || echo '')"
|
||||||
anticache="$(uci_get anticache || echo 0)"
|
ssl_insecure="$(uci_get main.ssl_insecure || echo 0)"
|
||||||
anticomp="$(uci_get anticomp || echo 0)"
|
anticache="$(uci_get main.anticache || echo 0)"
|
||||||
flow_detail="$(uci_get flow_detail || echo 1)"
|
anticomp="$(uci_get main.anticomp || echo 0)"
|
||||||
|
flow_detail="$(uci_get main.flow_detail || echo 1)"
|
||||||
|
|
||||||
|
# Transparent mode settings
|
||||||
|
transparent_enabled="$(uci_get transparent.enabled || echo 0)"
|
||||||
|
transparent_iface="$(uci_get transparent.interface || echo br-lan)"
|
||||||
|
redirect_http="$(uci_get transparent.redirect_http || echo 1)"
|
||||||
|
redirect_https="$(uci_get transparent.redirect_https || echo 1)"
|
||||||
|
http_port="$(uci_get transparent.http_port || echo 80)"
|
||||||
|
https_port="$(uci_get transparent.https_port || echo 443)"
|
||||||
|
|
||||||
|
# Whitelist settings
|
||||||
|
whitelist_enabled="$(uci_get whitelist.enabled || echo 1)"
|
||||||
|
|
||||||
|
# Filtering settings
|
||||||
|
filtering_enabled="$(uci_get filtering.enabled || echo 0)"
|
||||||
|
log_requests="$(uci_get filtering.log_requests || echo 1)"
|
||||||
|
filter_cdn="$(uci_get filtering.filter_cdn || echo 0)"
|
||||||
|
filter_media="$(uci_get filtering.filter_media || echo 0)"
|
||||||
|
block_ads="$(uci_get filtering.block_ads || echo 0)"
|
||||||
|
addon_script="$(uci_get filtering.addon_script || echo /etc/mitmproxy/addons/secubox_filter.py)"
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_dir() { [ -d "$1" ] || mkdir -p "$1"; }
|
ensure_dir() { [ -d "$1" ] || mkdir -p "$1"; }
|
||||||
@ -69,6 +94,10 @@ has_lxc() {
|
|||||||
command -v lxc-stop >/dev/null 2>&1
|
command -v lxc-stop >/dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
has_nft() {
|
||||||
|
command -v nft >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
# Ensure required packages are installed
|
# Ensure required packages are installed
|
||||||
ensure_packages() {
|
ensure_packages() {
|
||||||
require_root
|
require_root
|
||||||
@ -83,6 +112,128 @@ ensure_packages() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# NFTABLES TRANSPARENT MODE FUNCTIONS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
nft_setup() {
|
||||||
|
load_config
|
||||||
|
require_root
|
||||||
|
|
||||||
|
if ! has_nft; then
|
||||||
|
log_error "nftables not available"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$mode" != "transparent" ]; then
|
||||||
|
log_warn "Proxy mode is '$mode', not 'transparent'. Firewall rules not needed."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Setting up nftables for transparent proxy..."
|
||||||
|
|
||||||
|
# Create mitmproxy table
|
||||||
|
nft add table inet $NFT_TABLE 2>/dev/null || true
|
||||||
|
|
||||||
|
# Create chains
|
||||||
|
nft add chain inet $NFT_TABLE prerouting { type nat hook prerouting priority -100 \; } 2>/dev/null || true
|
||||||
|
nft add chain inet $NFT_TABLE output { type nat hook output priority -100 \; } 2>/dev/null || true
|
||||||
|
|
||||||
|
# Create bypass set for whitelisted IPs
|
||||||
|
nft add set inet $NFT_TABLE bypass_ipv4 { type ipv4_addr \; flags interval \; } 2>/dev/null || true
|
||||||
|
nft add set inet $NFT_TABLE bypass_ipv6 { type ipv6_addr \; flags interval \; } 2>/dev/null || true
|
||||||
|
|
||||||
|
# Load whitelist IPs into bypass set
|
||||||
|
if [ "$whitelist_enabled" = "1" ]; then
|
||||||
|
local bypass_ips=$(uci_get_list whitelist.bypass_ip 2>/dev/null)
|
||||||
|
for ip in $bypass_ips; do
|
||||||
|
case "$ip" in
|
||||||
|
*:*) nft add element inet $NFT_TABLE bypass_ipv6 { $ip } 2>/dev/null || true ;;
|
||||||
|
*) nft add element inet $NFT_TABLE bypass_ipv4 { $ip } 2>/dev/null || true ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
log_info "Loaded whitelist bypass IPs"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get interface index if specified
|
||||||
|
local iif_match=""
|
||||||
|
if [ -n "$transparent_iface" ]; then
|
||||||
|
iif_match="iifname \"$transparent_iface\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Flush existing rules in our chains
|
||||||
|
nft flush chain inet $NFT_TABLE prerouting 2>/dev/null || true
|
||||||
|
nft flush chain inet $NFT_TABLE output 2>/dev/null || true
|
||||||
|
|
||||||
|
# Add bypass rules first (before redirect)
|
||||||
|
nft add rule inet $NFT_TABLE prerouting ip daddr @bypass_ipv4 return 2>/dev/null || true
|
||||||
|
nft add rule inet $NFT_TABLE prerouting ip6 daddr @bypass_ipv6 return 2>/dev/null || true
|
||||||
|
|
||||||
|
# Don't intercept traffic from the proxy itself
|
||||||
|
nft add rule inet $NFT_TABLE prerouting meta skuid mitmproxy return 2>/dev/null || true
|
||||||
|
|
||||||
|
# Redirect HTTP traffic
|
||||||
|
if [ "$redirect_http" = "1" ]; then
|
||||||
|
if [ -n "$iif_match" ]; then
|
||||||
|
nft add rule inet $NFT_TABLE prerouting $iif_match tcp dport $http_port redirect to :$proxy_port
|
||||||
|
else
|
||||||
|
nft add rule inet $NFT_TABLE prerouting tcp dport $http_port redirect to :$proxy_port
|
||||||
|
fi
|
||||||
|
log_info "HTTP redirect: port $http_port -> $proxy_port"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Redirect HTTPS traffic
|
||||||
|
if [ "$redirect_https" = "1" ]; then
|
||||||
|
if [ -n "$iif_match" ]; then
|
||||||
|
nft add rule inet $NFT_TABLE prerouting $iif_match tcp dport $https_port redirect to :$proxy_port
|
||||||
|
else
|
||||||
|
nft add rule inet $NFT_TABLE prerouting tcp dport $https_port redirect to :$proxy_port
|
||||||
|
fi
|
||||||
|
log_info "HTTPS redirect: port $https_port -> $proxy_port"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "nftables transparent mode rules applied"
|
||||||
|
log_info "Table: inet $NFT_TABLE"
|
||||||
|
}
|
||||||
|
|
||||||
|
nft_teardown() {
|
||||||
|
require_root
|
||||||
|
|
||||||
|
if ! has_nft; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Removing nftables transparent mode rules..."
|
||||||
|
|
||||||
|
# Delete the entire table (removes all chains and rules)
|
||||||
|
nft delete table inet $NFT_TABLE 2>/dev/null || true
|
||||||
|
|
||||||
|
log_info "nftables rules removed"
|
||||||
|
}
|
||||||
|
|
||||||
|
nft_status() {
|
||||||
|
if ! has_nft; then
|
||||||
|
echo "nftables not available"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== mitmproxy nftables rules ==="
|
||||||
|
if nft list table inet $NFT_TABLE 2>/dev/null; then
|
||||||
|
echo ""
|
||||||
|
echo "Bypass IPv4 set:"
|
||||||
|
nft list set inet $NFT_TABLE bypass_ipv4 2>/dev/null || echo " (empty or not created)"
|
||||||
|
echo ""
|
||||||
|
echo "Bypass IPv6 set:"
|
||||||
|
nft list set inet $NFT_TABLE bypass_ipv6 2>/dev/null || echo " (empty or not created)"
|
||||||
|
else
|
||||||
|
echo "No mitmproxy rules configured"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# LXC CONTAINER FUNCTIONS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
lxc_check_prereqs() {
|
lxc_check_prereqs() {
|
||||||
log_info "Checking LXC prerequisites..."
|
log_info "Checking LXC prerequisites..."
|
||||||
ensure_packages lxc lxc-common lxc-attach lxc-start lxc-stop lxc-destroy || return 1
|
ensure_packages lxc lxc-common lxc-attach lxc-start lxc-stop lxc-destroy || return 1
|
||||||
@ -171,7 +322,7 @@ apk add --no-cache \
|
|||||||
pip3 install --break-system-packages mitmproxy
|
pip3 install --break-system-packages mitmproxy
|
||||||
|
|
||||||
# Create directories
|
# Create directories
|
||||||
mkdir -p /data /var/log/mitmproxy
|
mkdir -p /data /var/log/mitmproxy /etc/mitmproxy/addons
|
||||||
|
|
||||||
# Create startup script
|
# Create startup script
|
||||||
cat > /opt/start-mitmproxy.sh << 'START'
|
cat > /opt/start-mitmproxy.sh << 'START'
|
||||||
@ -183,6 +334,8 @@ MODE="${MITMPROXY_MODE:-regular}"
|
|||||||
PROXY_PORT="${MITMPROXY_PROXY_PORT:-8080}"
|
PROXY_PORT="${MITMPROXY_PROXY_PORT:-8080}"
|
||||||
WEB_PORT="${MITMPROXY_WEB_PORT:-8081}"
|
WEB_PORT="${MITMPROXY_WEB_PORT:-8081}"
|
||||||
WEB_HOST="${MITMPROXY_WEB_HOST:-0.0.0.0}"
|
WEB_HOST="${MITMPROXY_WEB_HOST:-0.0.0.0}"
|
||||||
|
ADDON_SCRIPT="${MITMPROXY_ADDON_SCRIPT:-}"
|
||||||
|
FILTERING_ENABLED="${MITMPROXY_FILTERING_ENABLED:-0}"
|
||||||
|
|
||||||
# Build command arguments
|
# Build command arguments
|
||||||
ARGS="--listen-host 0.0.0.0 --listen-port $PROXY_PORT"
|
ARGS="--listen-host 0.0.0.0 --listen-port $PROXY_PORT"
|
||||||
@ -207,6 +360,12 @@ esac
|
|||||||
[ "$ANTICOMP" = "1" ] && ARGS="$ARGS --anticomp"
|
[ "$ANTICOMP" = "1" ] && ARGS="$ARGS --anticomp"
|
||||||
[ -n "$FLOW_DETAIL" ] && ARGS="$ARGS --flow-detail $FLOW_DETAIL"
|
[ -n "$FLOW_DETAIL" ] && ARGS="$ARGS --flow-detail $FLOW_DETAIL"
|
||||||
|
|
||||||
|
# Load addon script if filtering is enabled
|
||||||
|
if [ "$FILTERING_ENABLED" = "1" ] && [ -n "$ADDON_SCRIPT" ] && [ -f "$ADDON_SCRIPT" ]; then
|
||||||
|
ARGS="$ARGS -s $ADDON_SCRIPT"
|
||||||
|
echo "Loading addon: $ADDON_SCRIPT"
|
||||||
|
fi
|
||||||
|
|
||||||
# Run mitmweb (web interface + proxy)
|
# Run mitmweb (web interface + proxy)
|
||||||
exec mitmweb $ARGS --web-host "$WEB_HOST" --web-port "$WEB_PORT" --no-web-open-browser
|
exec mitmweb $ARGS --web-host "$WEB_HOST" --web-port "$WEB_PORT" --no-web-open-browser
|
||||||
START
|
START
|
||||||
@ -225,11 +384,174 @@ SETUP
|
|||||||
}
|
}
|
||||||
|
|
||||||
rm -f "$rootfs/tmp/setup-mitmproxy.sh"
|
rm -f "$rootfs/tmp/setup-mitmproxy.sh"
|
||||||
|
|
||||||
|
# Install the SecuBox filter addon
|
||||||
|
install_addon_script
|
||||||
|
}
|
||||||
|
|
||||||
|
install_addon_script() {
|
||||||
|
load_config
|
||||||
|
ensure_dir "$ADDON_PATH"
|
||||||
|
ensure_dir "$LXC_ROOTFS/etc/mitmproxy/addons"
|
||||||
|
|
||||||
|
# Create the SecuBox filter addon
|
||||||
|
cat > "$ADDON_PATH/secubox_filter.py" << 'ADDON'
|
||||||
|
"""
|
||||||
|
SecuBox mitmproxy Filter Addon
|
||||||
|
CDN/MediaFlow filtering and request logging
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from datetime import datetime
|
||||||
|
from mitmproxy import http, ctx
|
||||||
|
|
||||||
|
# CDN domains to track
|
||||||
|
CDN_DOMAINS = [
|
||||||
|
r'\.cloudflare\.com$',
|
||||||
|
r'\.cloudflareinsights\.com$',
|
||||||
|
r'\.akamai\.net$',
|
||||||
|
r'\.akamaized\.net$',
|
||||||
|
r'\.fastly\.net$',
|
||||||
|
r'\.cloudfront\.net$',
|
||||||
|
r'\.azureedge\.net$',
|
||||||
|
r'\.jsdelivr\.net$',
|
||||||
|
r'\.unpkg\.com$',
|
||||||
|
r'\.cdnjs\.cloudflare\.com$',
|
||||||
|
]
|
||||||
|
|
||||||
|
# Media streaming domains
|
||||||
|
MEDIA_DOMAINS = [
|
||||||
|
r'\.googlevideo\.com$',
|
||||||
|
r'\.youtube\.com$',
|
||||||
|
r'\.ytimg\.com$',
|
||||||
|
r'\.netflix\.com$',
|
||||||
|
r'\.nflxvideo\.net$',
|
||||||
|
r'\.spotify\.com$',
|
||||||
|
r'\.scdn\.co$',
|
||||||
|
r'\.twitch\.tv$',
|
||||||
|
r'\.ttvnw\.net$',
|
||||||
|
]
|
||||||
|
|
||||||
|
# Ad/Tracker domains to block
|
||||||
|
AD_DOMAINS = [
|
||||||
|
r'\.doubleclick\.net$',
|
||||||
|
r'\.googlesyndication\.com$',
|
||||||
|
r'\.googleadservices\.com$',
|
||||||
|
r'\.facebook\.net$',
|
||||||
|
r'\.analytics\.google\.com$',
|
||||||
|
r'\.google-analytics\.com$',
|
||||||
|
r'\.hotjar\.com$',
|
||||||
|
r'\.segment\.io$',
|
||||||
|
r'\.mixpanel\.com$',
|
||||||
|
r'\.amplitude\.com$',
|
||||||
|
]
|
||||||
|
|
||||||
|
class SecuBoxFilter:
|
||||||
|
def __init__(self):
|
||||||
|
self.log_file = os.environ.get('MITMPROXY_LOG_FILE', '/data/requests.log')
|
||||||
|
self.filter_cdn = os.environ.get('MITMPROXY_FILTER_CDN', '0') == '1'
|
||||||
|
self.filter_media = os.environ.get('MITMPROXY_FILTER_MEDIA', '0') == '1'
|
||||||
|
self.block_ads = os.environ.get('MITMPROXY_BLOCK_ADS', '0') == '1'
|
||||||
|
self.log_requests = os.environ.get('MITMPROXY_LOG_REQUESTS', '1') == '1'
|
||||||
|
|
||||||
|
ctx.log.info(f"SecuBox Filter initialized")
|
||||||
|
ctx.log.info(f" Log requests: {self.log_requests}")
|
||||||
|
ctx.log.info(f" Filter CDN: {self.filter_cdn}")
|
||||||
|
ctx.log.info(f" Filter Media: {self.filter_media}")
|
||||||
|
ctx.log.info(f" Block Ads: {self.block_ads}")
|
||||||
|
|
||||||
|
def _match_domain(self, host, patterns):
|
||||||
|
"""Check if host matches any pattern"""
|
||||||
|
for pattern in patterns:
|
||||||
|
if re.search(pattern, host, re.IGNORECASE):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _log_request(self, flow: http.HTTPFlow, category: str = "normal"):
|
||||||
|
"""Log request to JSON file"""
|
||||||
|
if not self.log_requests:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
entry = {
|
||||||
|
"timestamp": datetime.now().isoformat(),
|
||||||
|
"category": category,
|
||||||
|
"request": {
|
||||||
|
"method": flow.request.method,
|
||||||
|
"host": flow.request.host,
|
||||||
|
"port": flow.request.port,
|
||||||
|
"path": flow.request.path,
|
||||||
|
"scheme": flow.request.scheme,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if flow.response:
|
||||||
|
entry["response"] = {
|
||||||
|
"status_code": flow.response.status_code,
|
||||||
|
"content_type": flow.response.headers.get("content-type", ""),
|
||||||
|
"content_length": len(flow.response.content) if flow.response.content else 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
with open(self.log_file, 'a') as f:
|
||||||
|
f.write(json.dumps(entry) + '\n')
|
||||||
|
except Exception as e:
|
||||||
|
ctx.log.error(f"Failed to log request: {e}")
|
||||||
|
|
||||||
|
def request(self, flow: http.HTTPFlow):
|
||||||
|
"""Process incoming request"""
|
||||||
|
host = flow.request.host
|
||||||
|
|
||||||
|
# Check for ad/tracker domains
|
||||||
|
if self.block_ads and self._match_domain(host, AD_DOMAINS):
|
||||||
|
ctx.log.info(f"Blocked ad/tracker: {host}")
|
||||||
|
flow.response = http.Response.make(
|
||||||
|
403,
|
||||||
|
b"Blocked by SecuBox",
|
||||||
|
{"Content-Type": "text/plain"}
|
||||||
|
)
|
||||||
|
self._log_request(flow, "blocked_ad")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Track CDN requests
|
||||||
|
if self._match_domain(host, CDN_DOMAINS):
|
||||||
|
self._log_request(flow, "cdn")
|
||||||
|
if self.filter_cdn:
|
||||||
|
ctx.log.info(f"CDN request: {host}{flow.request.path[:50]}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Track media requests
|
||||||
|
if self._match_domain(host, MEDIA_DOMAINS):
|
||||||
|
self._log_request(flow, "media")
|
||||||
|
if self.filter_media:
|
||||||
|
ctx.log.info(f"Media request: {host}{flow.request.path[:50]}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Log normal request
|
||||||
|
self._log_request(flow, "normal")
|
||||||
|
|
||||||
|
def response(self, flow: http.HTTPFlow):
|
||||||
|
"""Process response - update log entry if needed"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
addons = [SecuBoxFilter()]
|
||||||
|
ADDON
|
||||||
|
|
||||||
|
# Copy to container rootfs
|
||||||
|
cp "$ADDON_PATH/secubox_filter.py" "$LXC_ROOTFS/etc/mitmproxy/addons/" 2>/dev/null || true
|
||||||
|
|
||||||
|
log_info "Addon script installed: $ADDON_PATH/secubox_filter.py"
|
||||||
}
|
}
|
||||||
|
|
||||||
lxc_create_config() {
|
lxc_create_config() {
|
||||||
load_config
|
load_config
|
||||||
|
|
||||||
|
# Build addon path for container
|
||||||
|
local container_addon=""
|
||||||
|
if [ "$filtering_enabled" = "1" ] && [ -f "$LXC_ROOTFS$addon_script" ]; then
|
||||||
|
container_addon="$addon_script"
|
||||||
|
fi
|
||||||
|
|
||||||
cat > "$LXC_CONFIG" << EOF
|
cat > "$LXC_CONFIG" << EOF
|
||||||
# mitmproxy LXC Configuration
|
# mitmproxy LXC Configuration
|
||||||
lxc.uts.name = $LXC_NAME
|
lxc.uts.name = $LXC_NAME
|
||||||
@ -243,6 +565,7 @@ lxc.net.0.type = none
|
|||||||
# Mounts
|
# Mounts
|
||||||
lxc.mount.auto = proc:mixed sys:ro cgroup:mixed
|
lxc.mount.auto = proc:mixed sys:ro cgroup:mixed
|
||||||
lxc.mount.entry = $data_path data none bind,create=dir 0 0
|
lxc.mount.entry = $data_path data none bind,create=dir 0 0
|
||||||
|
lxc.mount.entry = $ADDON_PATH etc/mitmproxy/addons none bind,create=dir 0 0
|
||||||
|
|
||||||
# Environment variables for configuration
|
# Environment variables for configuration
|
||||||
lxc.environment = MITMPROXY_MODE=$mode
|
lxc.environment = MITMPROXY_MODE=$mode
|
||||||
@ -255,6 +578,13 @@ lxc.environment = SSL_INSECURE=$ssl_insecure
|
|||||||
lxc.environment = ANTICACHE=$anticache
|
lxc.environment = ANTICACHE=$anticache
|
||||||
lxc.environment = ANTICOMP=$anticomp
|
lxc.environment = ANTICOMP=$anticomp
|
||||||
lxc.environment = FLOW_DETAIL=$flow_detail
|
lxc.environment = FLOW_DETAIL=$flow_detail
|
||||||
|
lxc.environment = MITMPROXY_FILTERING_ENABLED=$filtering_enabled
|
||||||
|
lxc.environment = MITMPROXY_ADDON_SCRIPT=$addon_script
|
||||||
|
lxc.environment = MITMPROXY_LOG_REQUESTS=$log_requests
|
||||||
|
lxc.environment = MITMPROXY_FILTER_CDN=$filter_cdn
|
||||||
|
lxc.environment = MITMPROXY_FILTER_MEDIA=$filter_media
|
||||||
|
lxc.environment = MITMPROXY_BLOCK_ADS=$block_ads
|
||||||
|
lxc.environment = MITMPROXY_LOG_FILE=/data/requests.log
|
||||||
|
|
||||||
# Capabilities
|
# Capabilities
|
||||||
lxc.cap.drop = sys_admin sys_module mac_admin mac_override
|
lxc.cap.drop = sys_admin sys_module mac_admin mac_override
|
||||||
@ -293,19 +623,44 @@ lxc_run() {
|
|||||||
|
|
||||||
# Ensure mount points exist
|
# Ensure mount points exist
|
||||||
ensure_dir "$data_path"
|
ensure_dir "$data_path"
|
||||||
|
ensure_dir "$ADDON_PATH"
|
||||||
|
|
||||||
|
# Setup firewall rules if in transparent mode
|
||||||
|
if [ "$mode" = "transparent" ]; then
|
||||||
|
nft_setup
|
||||||
|
fi
|
||||||
|
|
||||||
log_info "Starting mitmproxy LXC container..."
|
log_info "Starting mitmproxy LXC container..."
|
||||||
|
log_info "Mode: $mode"
|
||||||
log_info "Web interface: http://0.0.0.0:$web_port"
|
log_info "Web interface: http://0.0.0.0:$web_port"
|
||||||
log_info "Proxy port: $proxy_port"
|
log_info "Proxy port: $proxy_port"
|
||||||
|
[ "$filtering_enabled" = "1" ] && log_info "Filtering: enabled"
|
||||||
exec lxc-start -n "$LXC_NAME" -F -f "$LXC_CONFIG"
|
exec lxc-start -n "$LXC_NAME" -F -f "$LXC_CONFIG"
|
||||||
}
|
}
|
||||||
|
|
||||||
lxc_status() {
|
lxc_status() {
|
||||||
|
load_config
|
||||||
|
echo "=== mitmproxy Status ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
if lxc-info -n "$LXC_NAME" >/dev/null 2>&1; then
|
if lxc-info -n "$LXC_NAME" >/dev/null 2>&1; then
|
||||||
lxc-info -n "$LXC_NAME"
|
lxc-info -n "$LXC_NAME"
|
||||||
else
|
else
|
||||||
echo "LXC container '$LXC_NAME' not found or not configured"
|
echo "LXC container '$LXC_NAME' not found or not configured"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Configuration ==="
|
||||||
|
echo "Mode: $mode"
|
||||||
|
echo "Proxy port: $proxy_port"
|
||||||
|
echo "Web port: $web_port"
|
||||||
|
echo "Data path: $data_path"
|
||||||
|
echo "Filtering: $([ "$filtering_enabled" = "1" ] && echo "enabled" || echo "disabled")"
|
||||||
|
|
||||||
|
if [ "$mode" = "transparent" ]; then
|
||||||
|
echo ""
|
||||||
|
nft_status
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
lxc_logs() {
|
lxc_logs() {
|
||||||
@ -342,6 +697,10 @@ lxc_destroy() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# COMMANDS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
cmd_install() {
|
cmd_install() {
|
||||||
require_root
|
require_root
|
||||||
load_config
|
load_config
|
||||||
@ -355,11 +714,12 @@ cmd_install() {
|
|||||||
|
|
||||||
# Create directories
|
# Create directories
|
||||||
ensure_dir "$data_path"
|
ensure_dir "$data_path"
|
||||||
|
ensure_dir "$ADDON_PATH"
|
||||||
|
|
||||||
lxc_check_prereqs || exit 1
|
lxc_check_prereqs || exit 1
|
||||||
lxc_create_rootfs || exit 1
|
lxc_create_rootfs || exit 1
|
||||||
|
|
||||||
uci_set enabled '1'
|
uci_set main.enabled '1'
|
||||||
/etc/init.d/mitmproxy enable
|
/etc/init.d/mitmproxy enable
|
||||||
|
|
||||||
log_info "mitmproxy installed."
|
log_info "mitmproxy installed."
|
||||||
@ -378,6 +738,12 @@ cmd_check() {
|
|||||||
else
|
else
|
||||||
log_warn "LXC: not available"
|
log_warn "LXC: not available"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if has_nft; then
|
||||||
|
log_info "nftables: available"
|
||||||
|
else
|
||||||
|
log_warn "nftables: not available (needed for transparent mode)"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_update() {
|
cmd_update() {
|
||||||
@ -429,6 +795,14 @@ cmd_cert() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd_firewall_setup() {
|
||||||
|
nft_setup
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_firewall_clear() {
|
||||||
|
nft_teardown
|
||||||
|
}
|
||||||
|
|
||||||
cmd_service_run() {
|
cmd_service_run() {
|
||||||
require_root
|
require_root
|
||||||
load_config
|
load_config
|
||||||
@ -444,6 +818,13 @@ cmd_service_run() {
|
|||||||
|
|
||||||
cmd_service_stop() {
|
cmd_service_stop() {
|
||||||
require_root
|
require_root
|
||||||
|
load_config
|
||||||
|
|
||||||
|
# Remove firewall rules
|
||||||
|
if [ "$mode" = "transparent" ]; then
|
||||||
|
nft_teardown
|
||||||
|
fi
|
||||||
|
|
||||||
lxc_stop
|
lxc_stop
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,6 +837,8 @@ case "${1:-}" in
|
|||||||
logs) shift; cmd_logs "$@" ;;
|
logs) shift; cmd_logs "$@" ;;
|
||||||
shell) shift; cmd_shell "$@" ;;
|
shell) shift; cmd_shell "$@" ;;
|
||||||
cert) shift; cmd_cert "$@" ;;
|
cert) shift; cmd_cert "$@" ;;
|
||||||
|
firewall-setup) shift; cmd_firewall_setup "$@" ;;
|
||||||
|
firewall-clear) shift; cmd_firewall_clear "$@" ;;
|
||||||
service-run) shift; cmd_service_run "$@" ;;
|
service-run) shift; cmd_service_run "$@" ;;
|
||||||
service-stop) shift; cmd_service_stop "$@" ;;
|
service-stop) shift; cmd_service_stop "$@" ;;
|
||||||
help|--help|-h|'') usage ;;
|
help|--help|-h|'') usage ;;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user