#!/usr/bin/env python3
"""
SecuBox Fabricator - Widget & Component Constructor
Multi-tab Streamlit app for building SecuBox components
"""
import streamlit as st
import json
import subprocess
import os
from datetime import datetime
st.set_page_config(
page_title="SecuBox Fabricator",
page_icon="🔧",
layout="wide",
initial_sidebar_state="collapsed"
)
# Custom CSS
st.markdown("""
""", unsafe_allow_html=True)
def run_cmd(cmd, timeout=30):
"""Run shell command and return output"""
try:
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
return result.stdout.strip(), result.returncode == 0
except subprocess.TimeoutExpired:
return "Command timed out", False
except Exception as e:
return str(e), False
def ssh_cmd(cmd, timeout=30):
"""Run command on router via SSH"""
return run_cmd(f'ssh -o ConnectTimeout=5 root@192.168.255.1 "{cmd}"', timeout)
# Main title
st.markdown('
🔧 SecuBox Fabricator
', unsafe_allow_html=True)
# Tab navigation
tabs = st.tabs(["📊 Collectors", "🚀 Apps", "📝 Blogs", "🌐 Statics", "🔌 Services", "🧩 Widgets", "🪟 Embedder"])
# ============== TAB 1: COLLECTORS ==============
with tabs[0]:
st.subheader("Stats Collector Builder")
st.markdown("Create custom stats collection scripts")
col1, col2 = st.columns([1, 1])
with col1:
collector_name = st.text_input("Collector Name", value="my-collector", key="coll_name")
collector_type = st.selectbox("Template", ["Custom", "CrowdSec", "mitmproxy", "Firewall", "Network"])
data_source = st.text_input("Data Source", value="/var/log/myservice.log", key="coll_src")
output_path = st.text_input("Output JSON", value=f"/tmp/secubox/{collector_name}.json", key="coll_out")
cron_schedule = st.selectbox("Update Frequency", ["*/5 * * * *", "*/1 * * * *", "*/15 * * * *", "0 * * * *"])
# Template-based script generation
if collector_type == "Custom":
script_template = f'''#!/bin/sh
# {collector_name} - Custom Stats Collector
OUTPUT="{output_path}"
SRC="{data_source}"
# Parse your data source here
count=$(wc -l < "$SRC" 2>/dev/null | tr -cd '0-9')
[ -z "$count" ] && count=0
cat > "$OUTPUT" << EOF
{{
"count": $count,
"last_update": "$(date -Iseconds)"
}}
EOF
'''
elif collector_type == "CrowdSec":
script_template = f'''#!/bin/sh
# {collector_name} - CrowdSec Stats
OUTPUT="{output_path}"
bans=$(cscli decisions list -o json 2>/dev/null | jsonfilter -e "@[*]" 2>/dev/null | wc -l)
[ -z "$bans" ] && bans=0
cat > "$OUTPUT" << EOF
{{ "bans": $bans, "last_update": "$(date -Iseconds)" }}
EOF
'''
elif collector_type == "mitmproxy":
script_template = f'''#!/bin/sh
# {collector_name} - mitmproxy WAF Stats
OUTPUT="{output_path}"
threats=$(wc -l < /srv/mitmproxy/threats.log 2>/dev/null | tr -cd '0-9')
[ -z "$threats" ] && threats=0
cat > "$OUTPUT" << EOF
{{ "threats": $threats, "last_update": "$(date -Iseconds)" }}
EOF
'''
elif collector_type == "Firewall":
script_template = f'''#!/bin/sh
# {collector_name} - Firewall Stats
OUTPUT="{output_path}"
dropped=$(nft list chain inet fw4 input_wan 2>/dev/null | grep -oE 'packets [0-9]+' | awk '{{sum+=$2}}END{{print sum+0}}')
cat > "$OUTPUT" << EOF
{{ "dropped": ${{dropped:-0}}, "last_update": "$(date -Iseconds)" }}
EOF
'''
else:
script_template = f'''#!/bin/sh
# {collector_name} - Network Stats
OUTPUT="{output_path}"
conns=$(wc -l < /proc/net/nf_conntrack 2>/dev/null | tr -cd '0-9')
cat > "$OUTPUT" << EOF
{{ "connections": ${{conns:-0}}, "last_update": "$(date -Iseconds)" }}
EOF
'''
with col2:
st.markdown("**Generated Script:**")
script_code = st.text_area("Script", value=script_template, height=300, key="coll_script")
if st.button("🚀 Deploy Collector", key="deploy_coll"):
script_path = f"/usr/sbin/{collector_name}.sh"
# Save script
with open(f"/tmp/{collector_name}.sh", "w") as f:
f.write(script_code)
os.system(f"scp /tmp/{collector_name}.sh root@192.168.255.1:{script_path}")
os.system(f"ssh root@192.168.255.1 'chmod +x {script_path}'")
# Add cron
cron_entry = f"{cron_schedule} {script_path} >/dev/null 2>&1"
os.system(f'ssh root@192.168.255.1 "grep -q \\"{collector_name}\\" /etc/crontabs/root || echo \\"{cron_entry}\\" >> /etc/crontabs/root"')
st.success(f"Deployed {collector_name} to {script_path}")
# ============== TAB 2: APPS ==============
with tabs[1]:
st.subheader("Streamlit App Deployer")
col1, col2 = st.columns([1, 1])
with col1:
st.markdown("**Create New App**")
app_name = st.text_input("App Name", value="myapp", key="app_name")
app_port = st.number_input("Port", min_value=8500, max_value=9999, value=8520, key="app_port")
app_template = st.selectbox("Template", ["Basic", "Dashboard", "Form", "Data Viewer"])
templates = {
"Basic": '''import streamlit as st
st.set_page_config(page_title="{name}", page_icon="🚀")
st.title("{name}")
st.write("Welcome to {name}!")
''',
"Dashboard": '''import streamlit as st
import json
st.set_page_config(page_title="{name}", page_icon="📊", layout="wide")
st.title("📊 {name} Dashboard")
col1, col2, col3 = st.columns(3)
with col1: st.metric("Metric 1", "100")
with col2: st.metric("Metric 2", "200")
with col3: st.metric("Metric 3", "300")
''',
"Form": '''import streamlit as st
st.set_page_config(page_title="{name}", page_icon="📝")
st.title("📝 {name}")
with st.form("main_form"):
name = st.text_input("Name")
email = st.text_input("Email")
if st.form_submit_button("Submit"):
st.success(f"Submitted: {{name}}, {{email}}")
''',
"Data Viewer": '''import streamlit as st
import json
st.set_page_config(page_title="{name}", page_icon="📈", layout="wide")
st.title("📈 {name}")
data_path = st.text_input("JSON Path", "/tmp/secubox/health-status.json")
if st.button("Load Data"):
try:
with open(data_path) as f:
st.json(json.load(f))
except Exception as e:
st.error(str(e))
'''
}
app_code = templates[app_template].format(name=app_name)
with col2:
st.markdown("**App Code:**")
final_code = st.text_area("Code", value=app_code, height=300, key="app_code")
if st.button("🚀 Deploy App", key="deploy_app"):
app_path = f"/srv/streamlit/apps/{app_name}"
with open(f"/tmp/{app_name}_app.py", "w") as f:
f.write(final_code)
os.system(f"ssh root@192.168.255.1 'mkdir -p {app_path}'")
os.system(f"scp /tmp/{app_name}_app.py root@192.168.255.1:{app_path}/app.py")
# Register in UCI
os.system(f'ssh root@192.168.255.1 "uci set streamlit.{app_name}=instance && uci set streamlit.{app_name}.name={app_name} && uci set streamlit.{app_name}.app={app_name}/app.py && uci set streamlit.{app_name}.port={app_port} && uci set streamlit.{app_name}.enabled=1 && uci commit streamlit"')
st.success(f"Deployed {app_name} on port {app_port}")
st.markdown("---")
st.markdown("**Running Instances:**")
output, _ = ssh_cmd("streamlitctl list 2>/dev/null | head -20")
st.code(output or "No instances found")
# ============== TAB 3: BLOGS ==============
with tabs[2]:
st.subheader("MetaBlogizer Sites")
col1, col2 = st.columns([1, 1])
with col1:
st.markdown("**Create Blog Site**")
blog_name = st.text_input("Site Name", value="myblog", key="blog_name")
blog_domain = st.text_input("Domain", value="myblog.example.com", key="blog_domain")
blog_port = st.number_input("Port", min_value=8900, max_value=9999, value=8920, key="blog_port")
blog_theme = st.selectbox("Theme", ["paper", "ananke", "book", "even", "stack"])
if st.button("🚀 Create Blog", key="create_blog"):
cmd = f'metablogizerctl create {blog_name} {blog_domain} {blog_port} {blog_theme} 2>&1'
output, success = ssh_cmd(cmd, timeout=60)
if success:
st.success(f"Created blog: {blog_name}")
else:
st.error(output)
with col2:
st.markdown("**Existing Sites:**")
sites_output, _ = ssh_cmd("metablogizerctl list 2>/dev/null")
st.code(sites_output or "No sites found")
st.markdown("**Quick Actions:**")
site_select = st.text_input("Site to manage", key="blog_manage")
action_col1, action_col2, action_col3 = st.columns(3)
with action_col1:
if st.button("Build", key="blog_build"):
output, _ = ssh_cmd(f"metablogizerctl build {site_select}")
st.info(output)
with action_col2:
if st.button("Serve", key="blog_serve"):
output, _ = ssh_cmd(f"metablogizerctl serve {site_select}")
st.info(output)
with action_col3:
if st.button("Emancipate", key="blog_emancipate"):
output, _ = ssh_cmd(f"metablogizerctl emancipate {site_select}")
st.info(output)
# ============== TAB 4: STATICS ==============
with tabs[3]:
st.subheader("Static Site Generator")
page_name = st.text_input("Page Name", value="mypage", key="static_name")
page_title = st.text_input("Title", value="My Page", key="static_title")
page_template = st.selectbox("Template", ["Landing", "Status", "Dashboard", "Portal"])
templates = {
"Landing": '''
{title}
{title}
Welcome to {title}
''',
"Status": '''
{title}
{title}
Loading...
''',
"Dashboard": '''
{title}
{title}
''',
"Portal": '''
{title}
{title}
'''
}
html_code = templates[page_template].format(title=page_title)
final_html = st.text_area("HTML Code", value=html_code, height=300, key="static_html")
if st.button("🚀 Deploy Page", key="deploy_static"):
with open(f"/tmp/{page_name}.html", "w") as f:
f.write(final_html)
os.system(f"scp /tmp/{page_name}.html root@192.168.255.1:/www/{page_name}.html")
st.success(f"Deployed to /www/{page_name}.html")
# ============== TAB 5: SERVICES ==============
with tabs[4]:
st.subheader("Service Exposure (Emancipate)")
col1, col2 = st.columns([1, 1])
with col1:
st.markdown("**Local Services Scan**")
if st.button("🔍 Scan Services", key="scan_svc"):
output, _ = ssh_cmd("netstat -tln | grep LISTEN | awk '{print $4}' | sort -u | head -20")
st.code(output or "No services found")
st.markdown("**Expose Service**")
svc_port = st.number_input("Port", min_value=1, max_value=65535, value=8080, key="svc_port")
svc_domain = st.text_input("Domain", value="myservice.example.com", key="svc_domain")
svc_backend = st.text_input("Backend Name", value="myservice", key="svc_backend")
expose_options = st.multiselect("Exposure Channels", ["HAProxy/SSL", "Tor", "Mesh"])
with col2:
if st.button("🔌 Emancipate Service", key="emancipate_svc"):
# Create HAProxy backend and vhost
if "HAProxy/SSL" in expose_options:
cmds = [
f'uci set haproxy.{svc_backend}=backend',
f'uci set haproxy.{svc_backend}.name="{svc_backend}"',
f'uci set haproxy.{svc_backend}.mode="http"',
f'uci set haproxy.{svc_backend}.balance="roundrobin"',
f'uci set haproxy.{svc_backend}.enabled="1"',
f'uci set haproxy.{svc_backend}.server="srv 192.168.255.1:{svc_port} check"',
f'uci set haproxy.{svc_domain.replace(".", "_")}=vhost',
f'uci set haproxy.{svc_domain.replace(".", "_")}.domain="{svc_domain}"',
f'uci set haproxy.{svc_domain.replace(".", "_")}.backend="{svc_backend}"',
f'uci set haproxy.{svc_domain.replace(".", "_")}.ssl="1"',
f'uci set haproxy.{svc_domain.replace(".", "_")}.https_redirect="1"',
f'uci set haproxy.{svc_domain.replace(".", "_")}.enabled="1"',
'uci commit haproxy',
'haproxyctl generate && haproxyctl reload'
]
for cmd in cmds:
ssh_cmd(cmd)
st.success(f"HAProxy vhost created for {svc_domain}")
if "Tor" in expose_options:
output, _ = ssh_cmd(f"torctl add {svc_backend} {svc_port}")
st.info(f"Tor: {output}")
if "Mesh" in expose_options:
output, _ = ssh_cmd(f"vortexctl mesh publish {svc_backend} {svc_domain}")
st.info(f"Mesh: {output}")
# ============== TAB 6: WIDGETS ==============
with tabs[5]:
st.subheader("Widget Designer")
widget_name = st.text_input("Widget Name", value="mywidget", key="widget_name")
widget_type = st.selectbox("Type", ["Metric", "Chart", "Status", "List"])
data_source = st.text_input("Data JSON Path", value="/secubox-status.json", key="widget_data")
data_field = st.text_input("Data Field", value="resources.cpu_load", key="widget_field")
widget_templates = {
"Metric": '''
''',
"Status": '''
{name}
''',
"Chart": '''
''',
"List": '''
'''
}
widget_code = widget_templates[widget_type].format(
name=widget_name,
source=data_source,
field=data_field
)
st.markdown("**Preview:**")
st.markdown(f'{widget_code.split("