From 21e82abb650aaff116144436bc9f218dc571a405 Mon Sep 17 00:00:00 2001 From: anonpenguin23 Date: Tue, 10 Feb 2026 09:39:26 +0200 Subject: [PATCH] Fixed WG port issues and production firewall fixes --- Makefile | 2 +- pkg/cli/production/install/flags.go | 6 ++- pkg/cli/production/install/orchestrator.go | 14 +++++-- pkg/cli/production/upgrade/flags.go | 6 ++- pkg/cli/production/upgrade/orchestrator.go | 10 ++++- pkg/environments/production/firewall.go | 8 ++++ pkg/environments/production/firewall_test.go | 1 + .../production/installers/anyone_relay.go | 32 ++++++++++++++++ pkg/environments/production/orchestrator.go | 38 +++++++++++++++++-- pkg/environments/production/preferences.go | 5 ++- pkg/environments/production/wireguard.go | 5 +++ pkg/environments/production/wireguard_test.go | 6 +++ 12 files changed, 118 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index f4cbfea..3d193e4 100644 --- a/Makefile +++ b/Makefile @@ -86,7 +86,7 @@ test-e2e-quick: .PHONY: build clean test run-node run-node2 run-node3 run-example deps tidy fmt vet lint clear-ports install-hooks kill -VERSION := 0.101.3 +VERSION := 0.101.4 COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown) DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ) LDFLAGS := -X 'main.version=$(VERSION)' -X 'main.commit=$(COMMIT)' -X 'main.date=$(DATE)' diff --git a/pkg/cli/production/install/flags.go b/pkg/cli/production/install/flags.go index d61851a..66d9738 100644 --- a/pkg/cli/production/install/flags.go +++ b/pkg/cli/production/install/flags.go @@ -33,7 +33,8 @@ type Flags struct { // Security flags SkipFirewall bool // Skip UFW firewall setup (for users who manage their own firewall) - // Anyone relay operator flags + // Anyone flags + AnyoneClient bool // Run Anyone as client-only (SOCKS5 proxy on port 9050, no relay) AnyoneRelay bool // Run as relay operator instead of client AnyoneExit bool // Run as exit relay (legal implications) AnyoneMigrate bool // Migrate existing Anyone installation @@ -80,7 +81,8 @@ func ParseFlags(args []string) (*Flags, error) { // Security flags fs.BoolVar(&flags.SkipFirewall, "skip-firewall", false, "Skip UFW firewall setup (for users who manage their own firewall)") - // Anyone relay operator flags + // Anyone flags + fs.BoolVar(&flags.AnyoneClient, "anyone-client", false, "Install Anyone as client-only (SOCKS5 proxy on port 9050, no relay)") fs.BoolVar(&flags.AnyoneRelay, "anyone-relay", false, "Run as Anyone relay operator (earn rewards)") fs.BoolVar(&flags.AnyoneExit, "anyone-exit", false, "Run as exit relay (requires --anyone-relay, legal implications)") fs.BoolVar(&flags.AnyoneMigrate, "anyone-migrate", false, "Migrate existing Anyone installation into Orama Network") diff --git a/pkg/cli/production/install/orchestrator.go b/pkg/cli/production/install/orchestrator.go index e04d7a8..b30a9f1 100644 --- a/pkg/cli/production/install/orchestrator.go +++ b/pkg/cli/production/install/orchestrator.go @@ -47,7 +47,10 @@ func NewOrchestrator(flags *Flags) (*Orchestrator, error) { setup := production.NewProductionSetup(oramaHome, os.Stdout, flags.Force, flags.Branch, flags.NoPull, flags.SkipChecks, flags.PreBuilt) setup.SetNameserver(flags.Nameserver) - // Configure Anyone relay if enabled + // Configure Anyone mode + if flags.AnyoneRelay && flags.AnyoneClient { + return nil, fmt.Errorf("--anyone-relay and --anyone-client are mutually exclusive") + } if flags.AnyoneRelay { setup.SetAnyoneRelayConfig(&production.AnyoneRelayConfig{ Enabled: true, @@ -61,6 +64,8 @@ func NewOrchestrator(flags *Flags) (*Orchestrator, error) { BandwidthPct: flags.AnyoneBandwidth, AccountingMax: flags.AnyoneAccounting, }) + } else if flags.AnyoneClient { + setup.SetAnyoneClient(true) } validator := NewValidator(flags, oramaDir) @@ -118,10 +123,11 @@ func (o *Orchestrator) Execute() error { } } - // Save preferences for future upgrades (branch + nameserver) + // Save preferences for future upgrades prefs := &production.NodePreferences{ - Branch: o.flags.Branch, - Nameserver: o.flags.Nameserver, + Branch: o.flags.Branch, + Nameserver: o.flags.Nameserver, + AnyoneClient: o.flags.AnyoneClient, } if err := production.SavePreferences(o.oramaDir, prefs); err != nil { fmt.Fprintf(os.Stderr, "⚠️ Warning: Failed to save preferences: %v\n", err) diff --git a/pkg/cli/production/upgrade/flags.go b/pkg/cli/production/upgrade/flags.go index c45b81a..cff6d1d 100644 --- a/pkg/cli/production/upgrade/flags.go +++ b/pkg/cli/production/upgrade/flags.go @@ -16,7 +16,8 @@ type Flags struct { Branch string Nameserver *bool // Pointer so we can detect if explicitly set vs default - // Anyone relay operator flags + // Anyone flags + AnyoneClient bool AnyoneRelay bool AnyoneExit bool AnyoneMigrate bool @@ -46,7 +47,8 @@ func ParseFlags(args []string) (*Flags, error) { // Nameserver flag - use pointer to detect if explicitly set nameserver := fs.Bool("nameserver", false, "Make this node a nameserver (uses saved preference if not specified)") - // Anyone relay operator flags + // Anyone flags + fs.BoolVar(&flags.AnyoneClient, "anyone-client", false, "Install Anyone as client-only (SOCKS5 proxy on port 9050, no relay)") fs.BoolVar(&flags.AnyoneRelay, "anyone-relay", false, "Run as Anyone relay operator (earn rewards)") fs.BoolVar(&flags.AnyoneExit, "anyone-exit", false, "Run as exit relay (requires --anyone-relay, legal implications)") fs.BoolVar(&flags.AnyoneMigrate, "anyone-migrate", false, "Migrate existing Anyone installation into Orama Network") diff --git a/pkg/cli/production/upgrade/orchestrator.go b/pkg/cli/production/upgrade/orchestrator.go index 06f5a8b..c145fa0 100644 --- a/pkg/cli/production/upgrade/orchestrator.go +++ b/pkg/cli/production/upgrade/orchestrator.go @@ -47,7 +47,7 @@ func NewOrchestrator(flags *Flags) *Orchestrator { setup := production.NewProductionSetup(oramaHome, os.Stdout, flags.Force, branch, flags.NoPull, flags.SkipChecks, flags.PreBuilt) setup.SetNameserver(isNameserver) - // Configure Anyone relay if enabled + // Configure Anyone mode (flag > saved preference) if flags.AnyoneRelay { setup.SetAnyoneRelayConfig(&production.AnyoneRelayConfig{ Enabled: true, @@ -61,6 +61,8 @@ func NewOrchestrator(flags *Flags) *Orchestrator { BandwidthPct: flags.AnyoneBandwidth, AccountingMax: flags.AnyoneAccounting, }) + } else if flags.AnyoneClient || prefs.AnyoneClient { + setup.SetAnyoneClient(true) } return &Orchestrator{ @@ -208,6 +210,12 @@ func (o *Orchestrator) handleBranchPreferences() error { fmt.Printf(" Nameserver mode: enabled (CoreDNS + Caddy)\n") } + // If anyone-client was explicitly provided, update it + if o.flags.AnyoneClient { + prefs.AnyoneClient = true + prefsChanged = true + } + // Save preferences if anything changed if prefsChanged { if err := production.SavePreferences(o.oramaDir, prefs); err != nil { diff --git a/pkg/environments/production/firewall.go b/pkg/environments/production/firewall.go index 330fdfa..40ec143 100644 --- a/pkg/environments/production/firewall.go +++ b/pkg/environments/production/firewall.go @@ -90,6 +90,14 @@ func (fp *FirewallProvisioner) GenerateRules() []string { // Enable firewall rules = append(rules, "ufw --force enable") + // Accept all WireGuard traffic before conntrack can classify it as "invalid". + // UFW's built-in "ct state invalid → DROP" runs before user rules like + // "allow from 10.0.0.0/8". Packets arriving through the WireGuard tunnel + // can be misclassified as "invalid" by conntrack due to reordering/jitter + // (especially between high-latency peers), causing silent packet drops. + // Inserting at position 1 in INPUT ensures this runs before UFW chains. + rules = append(rules, "iptables -I INPUT 1 -i wg0 -s 10.0.0.0/8 -j ACCEPT") + return rules } diff --git a/pkg/environments/production/firewall_test.go b/pkg/environments/production/firewall_test.go index 2507a65..43690e9 100644 --- a/pkg/environments/production/firewall_test.go +++ b/pkg/environments/production/firewall_test.go @@ -20,6 +20,7 @@ func TestFirewallProvisioner_GenerateRules_StandardNode(t *testing.T) { assertContainsRule(t, rules, "ufw allow 443/tcp") assertContainsRule(t, rules, "ufw allow from 10.0.0.0/8") assertContainsRule(t, rules, "ufw --force enable") + assertContainsRule(t, rules, "iptables -I INPUT 1 -i wg0 -s 10.0.0.0/8 -j ACCEPT") // Should NOT contain DNS or Anyone relay for _, rule := range rules { diff --git a/pkg/environments/production/installers/anyone_relay.go b/pkg/environments/production/installers/anyone_relay.go index 02c3551..3c9b726 100644 --- a/pkg/environments/production/installers/anyone_relay.go +++ b/pkg/environments/production/installers/anyone_relay.go @@ -290,6 +290,38 @@ func (ari *AnyoneRelayInstaller) Configure() error { return nil } +// ConfigureClient generates a client-only anonrc (SocksPort 9050, no relay) +func (ari *AnyoneRelayInstaller) ConfigureClient() error { + fmt.Fprintf(ari.logWriter, " Configuring Anyone client-only mode...\n") + + configPath := "/etc/anon/anonrc" + + // Backup existing config if it exists + if _, err := os.Stat(configPath); err == nil { + backupPath := configPath + ".bak" + if err := exec.Command("cp", configPath, backupPath).Run(); err != nil { + fmt.Fprintf(ari.logWriter, " ⚠️ Warning: failed to backup existing config: %v\n", err) + } + } + + config := `# Anyone Client Configuration (Managed by Orama Network) +# Client-only mode — no relay traffic, no ORPort + +SocksPort 9050 + +Log notice file /var/log/anon/notices.log +DataDirectory /var/lib/anon +ControlPort 9051 +` + + if err := os.WriteFile(configPath, []byte(config), 0644); err != nil { + return fmt.Errorf("failed to write client anonrc: %w", err) + } + + fmt.Fprintf(ari.logWriter, " ✓ Anyone client configured (SocksPort 9050)\n") + return nil +} + // generateAnonrc creates the anonrc configuration content func (ari *AnyoneRelayInstaller) generateAnonrc() string { var sb strings.Builder diff --git a/pkg/environments/production/orchestrator.go b/pkg/environments/production/orchestrator.go index 7549dc3..fa3492f 100644 --- a/pkg/environments/production/orchestrator.go +++ b/pkg/environments/production/orchestrator.go @@ -37,6 +37,7 @@ type ProductionSetup struct { skipOptionalDeps bool skipResourceChecks bool isNameserver bool // Whether this node is a nameserver (runs CoreDNS + Caddy) + isAnyoneClient bool // Whether this node runs Anyone as client-only (SOCKS5 proxy) anyoneRelayConfig *AnyoneRelayConfig // Configuration for Anyone relay mode privChecker *PrivilegeChecker osDetector *OSDetector @@ -152,6 +153,16 @@ func (ps *ProductionSetup) IsAnyoneRelay() bool { return ps.anyoneRelayConfig != nil && ps.anyoneRelayConfig.Enabled } +// SetAnyoneClient sets whether this node runs Anyone as client-only +func (ps *ProductionSetup) SetAnyoneClient(enabled bool) { + ps.isAnyoneClient = enabled +} + +// IsAnyoneClient returns whether this node runs Anyone as client-only +func (ps *ProductionSetup) IsAnyoneClient() bool { + return ps.isAnyoneClient +} + // Phase1CheckPrerequisites performs initial environment validation func (ps *ProductionSetup) Phase1CheckPrerequisites() error { ps.logf("Phase 1: Checking prerequisites...") @@ -444,6 +455,19 @@ func (ps *ProductionSetup) Phase2bInstallBinaries() error { if err := relayInstaller.Configure(); err != nil { ps.logf(" ⚠️ Anyone relay config warning: %v", err) } + } else if ps.IsAnyoneClient() { + ps.logf(" Installing Anyone client-only mode (SOCKS5 proxy)...") + clientInstaller := installers.NewAnyoneRelayInstaller(ps.arch, ps.logWriter, installers.AnyoneRelayConfig{}) + + // Install the anon binary (same apt package as relay) + if err := clientInstaller.Install(); err != nil { + ps.logf(" ⚠️ Anyone client install warning: %v", err) + } + + // Configure as client-only (SocksPort 9050, no ORPort) + if err := clientInstaller.ConfigureClient(); err != nil { + ps.logf(" ⚠️ Anyone client config warning: %v", err) + } } ps.logf(" ✓ All binaries installed") @@ -711,6 +735,12 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(enableHTTPS bool) error { return fmt.Errorf("failed to write Anyone Relay service: %w", err) } ps.logf(" ✓ Anyone Relay service created (operator mode, ORPort: %d)", ps.anyoneRelayConfig.ORPort) + } else if ps.IsAnyoneClient() { + anyoneUnit := ps.serviceGenerator.GenerateAnyoneRelayService() + if err := ps.serviceController.WriteServiceUnit("debros-anyone-relay.service", anyoneUnit); err != nil { + return fmt.Errorf("failed to write Anyone client service: %w", err) + } + ps.logf(" ✓ Anyone client service created (SocksPort 9050)") } // CoreDNS service (only for nameserver nodes) @@ -753,8 +783,8 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(enableHTTPS bool) error { // Note: debros-rqlite.service is NOT created - RQLite is managed by each node internally services := []string{"debros-ipfs.service", "debros-ipfs-cluster.service", "debros-olric.service", "debros-node.service"} - // Add Anyone Relay service if configured - if ps.IsAnyoneRelay() { + // Add Anyone service if configured (relay or client) + if ps.IsAnyoneRelay() || ps.IsAnyoneClient() { services = append(services, "debros-anyone-relay.service") } @@ -783,7 +813,7 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(enableHTTPS bool) error { // Start infrastructure first (IPFS, Olric, Anyone) - RQLite is managed internally by each node infraServices := []string{"debros-ipfs.service", "debros-olric.service"} - // Add Anyone Relay service if configured + // Add Anyone service if configured (relay or client) if ps.IsAnyoneRelay() { orPort := 9001 if ps.anyoneRelayConfig != nil && ps.anyoneRelayConfig.ORPort > 0 { @@ -795,6 +825,8 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(enableHTTPS bool) error { } else { infraServices = append(infraServices, "debros-anyone-relay.service") } + } else if ps.IsAnyoneClient() { + infraServices = append(infraServices, "debros-anyone-relay.service") } for _, svc := range infraServices { diff --git a/pkg/environments/production/preferences.go b/pkg/environments/production/preferences.go index 8e40c9d..e3926be 100644 --- a/pkg/environments/production/preferences.go +++ b/pkg/environments/production/preferences.go @@ -10,8 +10,9 @@ import ( // NodePreferences contains persistent node configuration that survives upgrades type NodePreferences struct { - Branch string `yaml:"branch"` - Nameserver bool `yaml:"nameserver"` + Branch string `yaml:"branch"` + Nameserver bool `yaml:"nameserver"` + AnyoneClient bool `yaml:"anyone_client"` } const ( diff --git a/pkg/environments/production/wireguard.go b/pkg/environments/production/wireguard.go index 8bf4ce9..c1901a5 100644 --- a/pkg/environments/production/wireguard.go +++ b/pkg/environments/production/wireguard.go @@ -114,6 +114,11 @@ func (wp *WireGuardProvisioner) GenerateConfig() string { sb.WriteString(fmt.Sprintf("Address = %s/24\n", wp.config.PrivateIP)) sb.WriteString(fmt.Sprintf("ListenPort = %d\n", wp.config.ListenPort)) + // Accept all WireGuard subnet traffic before UFW's conntrack "invalid" drop. + // Without this, packets reordered by the tunnel get silently dropped. + sb.WriteString("PostUp = iptables -I INPUT 1 -i wg0 -s 10.0.0.0/8 -j ACCEPT\n") + sb.WriteString("PostDown = iptables -D INPUT -i wg0 -s 10.0.0.0/8 -j ACCEPT\n") + for _, peer := range wp.config.Peers { sb.WriteString("\n[Peer]\n") sb.WriteString(fmt.Sprintf("PublicKey = %s\n", peer.PublicKey)) diff --git a/pkg/environments/production/wireguard_test.go b/pkg/environments/production/wireguard_test.go index 193abdd..42d0adc 100644 --- a/pkg/environments/production/wireguard_test.go +++ b/pkg/environments/production/wireguard_test.go @@ -92,6 +92,12 @@ func TestWireGuardProvisioner_GenerateConfig_NoPeers(t *testing.T) { if !strings.Contains(config, "PrivateKey = dGVzdHByaXZhdGVrZXl0ZXN0cHJpdmF0ZWtleXM=") { t.Error("config should contain PrivateKey") } + if !strings.Contains(config, "PostUp = iptables -I INPUT 1 -i wg0 -s 10.0.0.0/8 -j ACCEPT") { + t.Error("config should contain PostUp iptables rule for WireGuard subnet") + } + if !strings.Contains(config, "PostDown = iptables -D INPUT -i wg0 -s 10.0.0.0/8 -j ACCEPT") { + t.Error("config should contain PostDown iptables cleanup rule") + } if strings.Contains(config, "[Peer]") { t.Error("config should NOT contain [Peer] section with no peers") }