feat: Add Gitea auto-push and fix Tor Shield server mode

Streamlit/MetaBlogizer:
- Add 'gitea push <name>' command to both streamlitctl and metablogizerctl
- Auto-creates Gitea repo via API if it doesn't exist
- Initializes git, commits all files, and pushes to Gitea
- Stores repo reference in UCI for future syncs

Tor Shield:
- Add 'wan_input_allow' option for server preset
- Server mode now properly allows WAN inbound (ports 80, 443, 8443)
- Uses nftables rules to integrate with OpenWrt firewall4
- Outbound traffic still routed through Tor (kill_switch)
- Cleanup nftables rules on stop/disable

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-02-10 06:42:50 +01:00
parent a694241604
commit 364f19d421
5 changed files with 274 additions and 5 deletions

View File

@ -38,6 +38,7 @@ Site Commands:
delete <name> Delete site
sync <name> Sync site from git repo
publish <name> Publish site (create HAProxy vhost)
gitea push <name> Create Gitea repo and push site content
emancipate <name> KISS ULTIME MODE - Full exposure workflow:
1. DNS A record (Gandi/OVH)
2. Vortex DNS mesh publication
@ -409,7 +410,8 @@ cmd_sync() {
local name="$1"
[ -z "$name" ] && { log_error "Site name required"; return 1; }
local gitea_repo=$(uci_get site_${name}.gitea_repo)
local section=$(get_section "$name")
local gitea_repo=$(uci_get ${section}.gitea_repo)
[ -z "$gitea_repo" ] && { log_error "No git repo configured for $name"; return 1; }
local site_dir="$SITES_ROOT/$name"
@ -434,6 +436,109 @@ cmd_sync() {
log_info "Sync complete"
}
# Create Gitea repo via API and push local site content
cmd_gitea_push() {
local name="$1"
[ -z "$name" ] && { log_error "Site name required"; return 1; }
if ! site_exists "$name"; then
log_error "Site '$name' not found"
return 1
fi
# Load Gitea config
local gitea_enabled=$(uci_get main.gitea_enabled)
local gitea_url=$(uci_get main.gitea_url)
local gitea_user=$(uci_get main.gitea_user)
local gitea_token=$(uci_get main.gitea_token)
[ -z "$gitea_url" ] && gitea_url="http://localhost:3000"
if [ -z "$gitea_token" ]; then
log_error "Gitea token not configured"
log_info "Configure with:"
log_info " uci set metablogizer.main.gitea_url='http://192.168.255.1:3000'"
log_info " uci set metablogizer.main.gitea_user='admin'"
log_info " uci set metablogizer.main.gitea_token='your-token'"
log_info " uci commit metablogizer"
return 1
fi
local site_dir="$SITES_ROOT/$name"
if [ ! -d "$site_dir" ]; then
log_error "Site '$name' not found at $site_dir"
return 1
fi
local gitea_host=$(echo "$gitea_url" | sed 's|^https\?://||' | sed 's|/.*||')
local gitea_proto=$(echo "$gitea_url" | grep -q '^https' && echo "https" || echo "http")
local repo_name="metablog-$name"
log_info "Creating Gitea repository: $repo_name"
# Check if repo exists, create if not
local repo_check=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: token $gitea_token" \
"${gitea_url}/api/v1/repos/${gitea_user}/${repo_name}" 2>/dev/null)
if [ "$repo_check" != "200" ]; then
log_info "Repository doesn't exist, creating..."
local create_result=$(curl -s -X POST \
-H "Authorization: token $gitea_token" \
-H "Content-Type: application/json" \
-d "{\"name\":\"${repo_name}\",\"description\":\"MetaBlogizer site: ${name}\",\"private\":false,\"auto_init\":false}" \
"${gitea_url}/api/v1/user/repos" 2>/dev/null)
if ! echo "$create_result" | grep -q "\"name\":"; then
log_error "Failed to create repository"
log_error "Response: $create_result"
return 1
fi
log_info "Repository created: ${gitea_user}/${repo_name}"
else
log_info "Repository exists: ${gitea_user}/${repo_name}"
fi
# Initialize git in site directory if needed
cd "$site_dir"
if [ ! -d ".git" ]; then
log_info "Initializing git repository..."
git init
git config user.name "$gitea_user"
git config user.email "${gitea_user}@localhost"
fi
# Set remote
local remote_url="${gitea_proto}://${gitea_user}:${gitea_token}@${gitea_host}/${gitea_user}/${repo_name}.git"
git remote remove origin 2>/dev/null
git remote add origin "$remote_url"
# Add, commit and push
log_info "Adding files and committing..."
git add -A
git commit -m "Auto-push from SecuBox MetaBlogizer at $(date -Iseconds)" 2>/dev/null || \
log_info "No changes to commit"
log_info "Pushing to Gitea..."
git push -u origin HEAD:main --force 2>&1 || {
# Try master branch as fallback
git push -u origin HEAD:master --force 2>&1 || {
log_error "Failed to push to Gitea"
return 1
}
}
# Save repo reference in UCI
local section=$(get_section "$name")
uci set "${CONFIG}.${section}.gitea_repo=${gitea_user}/${repo_name}"
uci set "${CONFIG}.${section}.gitea_synced=$(date -Iseconds)"
uci commit "$CONFIG"
log_info "Push complete: ${gitea_url}/${gitea_user}/${repo_name}"
}
cmd_runtime() {
local action="$1"
local value="$2"
@ -764,6 +869,13 @@ case "${1:-}" in
runtime) shift; cmd_runtime "$@" ;;
status) shift; cmd_status "$@" ;;
install-nginx) shift; cmd_install_nginx "$@" ;;
gitea)
shift
case "${1:-}" in
push) shift; cmd_gitea_push "$@" ;;
*) echo "Usage: metablogizerctl gitea push <name>"; exit 1 ;;
esac
;;
help|--help|-h) usage ;;
*) usage ;;
esac

View File

@ -96,6 +96,7 @@ Gitea Integration:
gitea setup Configure git credentials
gitea clone <name> <repo> Clone app from Gitea repo
gitea pull <name> Pull latest from Gitea
gitea push <name> Create Gitea repo and push app content
Exposure:
emancipate <name> [domain] KISS ULTIME MODE - Full exposure workflow:
@ -1028,6 +1029,99 @@ cmd_gitea_pull() {
log_info "Update complete"
}
# Create Gitea repo via API and push local content
cmd_gitea_push() {
require_root
load_config
local name="$1"
[ -z "$name" ] && { log_error "App name required"; return 1; }
if [ "$gitea_enabled" != "1" ]; then
log_error "Gitea integration not enabled"
log_info "Enable with: uci set streamlit.gitea.enabled=1 && uci commit streamlit"
return 1
fi
if [ -z "$gitea_token" ]; then
log_error "Gitea token not configured"
return 1
fi
local app_dir="$APPS_PATH/$name"
if [ ! -d "$app_dir" ]; then
log_error "App '$name' not found at $app_dir"
return 1
fi
local gitea_host=$(echo "$gitea_url" | sed 's|^https\?://||' | sed 's|/.*||')
local gitea_proto=$(echo "$gitea_url" | grep -q '^https' && echo "https" || echo "http")
local repo_name="streamlit-$name"
log_info "Creating Gitea repository: $repo_name"
# Check if repo exists, create if not
local repo_check=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: token $gitea_token" \
"${gitea_url}/api/v1/repos/${gitea_user}/${repo_name}" 2>/dev/null)
if [ "$repo_check" != "200" ]; then
log_info "Repository doesn't exist, creating..."
local create_result=$(curl -s -X POST \
-H "Authorization: token $gitea_token" \
-H "Content-Type: application/json" \
-d "{\"name\":\"${repo_name}\",\"description\":\"Streamlit app: ${name}\",\"private\":false,\"auto_init\":false}" \
"${gitea_url}/api/v1/user/repos" 2>/dev/null)
if ! echo "$create_result" | grep -q "\"name\":"; then
log_error "Failed to create repository"
log_error "Response: $create_result"
return 1
fi
log_info "Repository created: ${gitea_user}/${repo_name}"
else
log_info "Repository exists: ${gitea_user}/${repo_name}"
fi
# Initialize git in app directory if needed
cd "$app_dir"
if [ ! -d ".git" ]; then
log_info "Initializing git repository..."
git init
git config user.name "$gitea_user"
git config user.email "${gitea_user}@localhost"
fi
# Set remote
local remote_url="${gitea_proto}://${gitea_user}:${gitea_token}@${gitea_host}/${gitea_user}/${repo_name}.git"
git remote remove origin 2>/dev/null
git remote add origin "$remote_url"
# Add, commit and push
log_info "Adding files and committing..."
git add -A
git commit -m "Auto-push from SecuBox Streamlit at $(date -Iseconds)" 2>/dev/null || \
log_info "No changes to commit"
log_info "Pushing to Gitea..."
git push -u origin HEAD:main --force 2>&1 || {
# Try master branch as fallback
git push -u origin HEAD:master --force 2>&1 || {
log_error "Failed to push to Gitea"
return 1
}
}
# Save repo reference in UCI
uci set "${CONFIG}.${name}.repo=${gitea_user}/${repo_name}"
uci set "${CONFIG}.${name}.gitea_synced=$(date -Iseconds)"
uci commit "$CONFIG"
log_info "Push complete: ${gitea_url}/${gitea_user}/${repo_name}"
}
# ===========================================
# KISS ULTIME MODE - Emancipate
# ===========================================
@ -1462,7 +1556,8 @@ case "${1:-}" in
setup) shift; cmd_gitea_setup "$@" ;;
clone) shift; cmd_gitea_clone "$@" ;;
pull) shift; cmd_gitea_pull "$@" ;;
*) echo "Usage: streamlitctl gitea {setup|clone|pull}"; exit 1 ;;
push) shift; cmd_gitea_push "$@" ;;
*) echo "Usage: streamlitctl gitea {setup|clone|pull|push}"; exit 1 ;;
esac
;;

View File

@ -36,6 +36,7 @@ config preset 'server'
option dns_over_tor '1'
option kill_switch '1'
option lan_proxy '1'
option wan_input_allow '1'
config proxy 'socks'
option port '9050'

View File

@ -161,12 +161,13 @@ EOF
}
setup_iptables() {
local mode trans_port dns_port dns_over_tor kill_switch
local mode trans_port dns_port dns_over_tor kill_switch wan_input_allow
config_load "$CONFIG"
config_get mode main mode 'transparent'
config_get kill_switch main kill_switch '1'
config_get dns_over_tor main dns_over_tor '1'
config_get wan_input_allow main wan_input_allow '0'
config_get trans_port trans port '9040'
config_get dns_port trans dns_port '9053'
@ -186,6 +187,9 @@ setup_iptables() {
iptables -t filter -F TOR_SHIELD 2>/dev/null
iptables -t filter -X TOR_SHIELD 2>/dev/null
# Clean up nftables rules for server mode
cleanup_nftables_wan
[ "$mode" = "transparent" ] || return 0
# Create chains (ignore "already exists" errors)
@ -221,10 +225,60 @@ setup_iptables() {
iptables -t filter -A OUTPUT -j TOR_SHIELD
fi
# Server mode: Allow WAN inbound traffic (for published services)
if [ "$wan_input_allow" = "1" ]; then
setup_nftables_wan_allow
fi
# LAN client Tor routing via PREROUTING
setup_lan_proxy
}
# nftables helpers for server mode WAN access
cleanup_nftables_wan() {
# Remove any previous tor-shield nftables rules
if command -v nft >/dev/null 2>&1; then
nft delete chain inet fw4 tor_shield_wan_in 2>/dev/null || true
fi
}
setup_nftables_wan_allow() {
# Server mode: Ensure WAN inbound is allowed
# This creates nftables rules to allow inbound connections from WAN
# Works with OpenWrt firewall4 (nftables-based)
if ! command -v nft >/dev/null 2>&1; then
echo "Warning: nft command not found, WAN input rules may not apply"
return 1
fi
# Check if fw4 table exists (OpenWrt firewall4)
if ! nft list table inet fw4 >/dev/null 2>&1; then
echo "Note: fw4 table not found, using legacy firewall"
return 0
fi
# Create chain for Tor Shield WAN input if not exists
nft add chain inet fw4 tor_shield_wan_in 2>/dev/null || true
# Allow all established/related (responses to outgoing)
nft add rule inet fw4 tor_shield_wan_in ct state established,related accept 2>/dev/null || true
# Allow new inbound on common service ports (HAProxy, HTTPS, HTTP)
# These are the ports typically used for published services
nft add rule inet fw4 tor_shield_wan_in tcp dport 80 accept 2>/dev/null || true
nft add rule inet fw4 tor_shield_wan_in tcp dport 443 accept 2>/dev/null || true
nft add rule inet fw4 tor_shield_wan_in tcp dport 8443 accept 2>/dev/null || true
# Hook into input_wan chain (before drop)
# First remove any existing jump to avoid duplicates
nft delete rule inet fw4 input_wan jump tor_shield_wan_in 2>/dev/null || true
# Add jump at the beginning of input_wan
nft insert rule inet fw4 input_wan jump tor_shield_wan_in 2>/dev/null || true
echo "Server mode: WAN inbound allowed on ports 80, 443, 8443"
}
add_excluded_ip() {
iptables -t nat -A TOR_SHIELD -d "$1" -j RETURN
}
@ -279,6 +333,9 @@ remove_iptables() {
done
iptables -t filter -F TOR_SHIELD 2>/dev/null
iptables -t filter -X TOR_SHIELD 2>/dev/null
# Clean up nftables rules for server mode
cleanup_nftables_wan
}
start_service() {

View File

@ -123,14 +123,16 @@ cmd_status() {
fi
# Show config details
local dns_over_tor kill_switch
local dns_over_tor kill_switch wan_input_allow
config_get dns_over_tor main dns_over_tor '1'
config_get kill_switch main kill_switch '1'
config_get wan_input_allow main wan_input_allow '0'
echo ""
echo "Configuration:"
echo " DNS over Tor: $([ "$dns_over_tor" = "1" ] && echo "Yes" || echo "No")"
echo " Kill Switch: $([ "$kill_switch" = "1" ] && echo "Yes" || echo "No")"
echo " WAN Input Allow: $([ "$wan_input_allow" = "1" ] && echo "Yes (Server Mode)" || echo "No")"
# Bridge status
local bridges_enabled
@ -151,18 +153,20 @@ cmd_enable() {
# Load preset configuration
config_load "$CONFIG"
local preset_mode preset_dns preset_kill preset_bridges preset_lan_proxy
local preset_mode preset_dns preset_kill preset_bridges preset_lan_proxy preset_wan_input
config_get preset_mode "$preset" mode 'transparent'
config_get preset_dns "$preset" dns_over_tor '1'
config_get preset_kill "$preset" kill_switch '1'
config_get preset_bridges "$preset" use_bridges '0'
config_get preset_lan_proxy "$preset" lan_proxy '0'
config_get preset_wan_input "$preset" wan_input_allow '0'
# Apply preset settings
uci set tor-shield.main.enabled='1'
uci set tor-shield.main.mode="$preset_mode"
uci set tor-shield.main.dns_over_tor="$preset_dns"
uci set tor-shield.main.kill_switch="$preset_kill"
uci set tor-shield.main.wan_input_allow="$preset_wan_input"
uci set tor-shield.trans.lan_proxy="$preset_lan_proxy"
if [ "$preset_bridges" = "1" ]; then