From 90a26295a4fbf6b73d27945e7234a1f619c12b17 Mon Sep 17 00:00:00 2001 From: anonpenguin23 Date: Sat, 22 Nov 2025 13:01:46 +0200 Subject: [PATCH] feat: add port checking and anyone-client installation to production setup - Introduced a new `PortChecker` type to verify port availability, enhancing service management during startup. - Updated the `BinaryInstaller` to install the `anyone-client` npm package globally, ensuring its availability for SOCKS5 proxy functionality. - Enhanced the `ProductionSetup` to include checks for port usage before starting the `anyone-client` service, improving conflict resolution. - Added logging for the installation and service creation of `anyone-client`, providing clearer feedback during the setup process. --- CHANGELOG.md | 17 +++++++++ Makefile | 2 +- README.md | 2 + pkg/environments/production/checks.go | 33 +++++++++++++++++ pkg/environments/production/installers.go | 33 ++++++++++++++++- pkg/environments/production/orchestrator.go | 41 ++++++++++++++++++--- pkg/environments/production/services.go | 31 ++++++++++++++++ 7 files changed, 150 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0b7a10..ed27f2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,23 @@ The format is based on [Keep a Changelog][keepachangelog] and adheres to [Semant ### Deprecated ### Fixed +## [0.69.18] - 2025-11-22 + +### Added +- Integrated `anyone-client` (SOCKS5 proxy) installation and systemd service (`debros-anyone-client.service`). +- Added port availability checking logic to prevent conflicts when starting services (e.g., `anyone-client` on port 9050). + +### Changed +- Updated system dependencies installation to include `nodejs` and `npm` required for `anyone-client`. +- Modified Olric configuration generation to bind to the specific VPS IP if provided, otherwise defaults to 0.0.0.0. +- Improved IPFS Cluster initialization by passing `CLUSTER_SECRET` directly as an environment variable. + +### Deprecated + +### Removed + +### Fixed +\n ## [0.69.17] - 2025-11-21 ### Added diff --git a/Makefile b/Makefile index c6d6145..15cb2a2 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ test-e2e: .PHONY: build clean test run-node run-node2 run-node3 run-example deps tidy fmt vet lint clear-ports install-hooks kill -VERSION := 0.69.17 +VERSION := 0.69.18 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/README.md b/README.md index e9a096a..cc9a566 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,8 @@ Install the CLI tool first: ```bash curl -fsSL https://install.debros.network | sudo bash + +./install-debros-network.sh --prerelease --nightly ``` Or download manually from [GitHub Releases](https://github.com/DeBrosOfficial/network/releases). diff --git a/pkg/environments/production/checks.go b/pkg/environments/production/checks.go index d15b916..e8e3b45 100644 --- a/pkg/environments/production/checks.go +++ b/pkg/environments/production/checks.go @@ -2,6 +2,7 @@ package production import ( "fmt" + "net" "os" "os/exec" "path/filepath" @@ -297,3 +298,35 @@ func (rc *ResourceChecker) CheckCPU() error { } return nil } + +// PortChecker checks if ports are available or in use +type PortChecker struct{} + +// NewPortChecker creates a new port checker +func NewPortChecker() *PortChecker { + return &PortChecker{} +} + +// IsPortInUse checks if a specific port is already in use +func (pc *PortChecker) IsPortInUse(port int) bool { + addr := fmt.Sprintf("localhost:%d", port) + conn, err := net.Dial("tcp", addr) + if err != nil { + // Port is not in use + return false + } + defer conn.Close() + // Port is in use + return true +} + +// IsPortInUseOnHost checks if a port is in use on a specific host +func (pc *PortChecker) IsPortInUseOnHost(host string, port int) bool { + addr := net.JoinHostPort(host, fmt.Sprintf("%d", port)) + conn, err := net.Dial("tcp", addr) + if err != nil { + return false + } + defer conn.Close() + return true +} diff --git a/pkg/environments/production/installers.go b/pkg/environments/production/installers.go index 63c7236..5a136de 100644 --- a/pkg/environments/production/installers.go +++ b/pkg/environments/production/installers.go @@ -391,8 +391,8 @@ func (bi *BinaryInstaller) InstallSystemDependencies() error { fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Warning: apt update failed\n") } - // Install dependencies - cmd = exec.Command("apt-get", "install", "-y", "curl", "git", "make", "build-essential", "wget") + // Install dependencies including Node.js for anyone-client + cmd = exec.Command("apt-get", "install", "-y", "curl", "git", "make", "build-essential", "wget", "nodejs", "npm") if err := cmd.Run(); err != nil { return fmt.Errorf("failed to install dependencies: %w", err) } @@ -567,6 +567,10 @@ func (bi *BinaryInstaller) InitializeIPFSClusterConfig(nodeType, clusterPath, cl fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Initializing IPFS Cluster config...\n") cmd := exec.Command(clusterBinary, "init", "--force") cmd.Env = append(os.Environ(), "IPFS_CLUSTER_PATH="+clusterPath) + // Pass CLUSTER_SECRET to init so it writes the correct secret to service.json directly + if clusterSecret != "" { + cmd.Env = append(cmd.Env, "CLUSTER_SECRET="+clusterSecret) + } if output, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("failed to initialize IPFS Cluster config: %v\n%s", err, string(output)) } @@ -689,3 +693,28 @@ func (bi *BinaryInstaller) InitializeRQLiteDataDir(nodeType, dataDir string) err exec.Command("chown", "-R", "debros:debros", dataDir).Run() return nil } + +// InstallAnyoneClient installs the anyone-client npm package globally +func (bi *BinaryInstaller) InstallAnyoneClient() error { + // Check if anyone-client is already available + if _, err := exec.LookPath("anyone-client"); err == nil { + fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " ✓ anyone-client already installed\n") + return nil + } + + fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " Installing anyone-client...\n") + + // Install anyone-client globally via npm + cmd := exec.Command("npm", "install", "-g", "anyone-client") + if output, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("failed to install anyone-client: %w\n%s", err, string(output)) + } + + // Verify installation + if _, err := exec.LookPath("anyone-client"); err != nil { + return fmt.Errorf("anyone-client installation failed - not found in PATH") + } + + fmt.Fprintf(bi.logWriter.(interface{ Write([]byte) (int, error) }), " ✓ anyone-client installed\n") + return nil +} diff --git a/pkg/environments/production/orchestrator.go b/pkg/environments/production/orchestrator.go index 487d646..4348d43 100644 --- a/pkg/environments/production/orchestrator.go +++ b/pkg/environments/production/orchestrator.go @@ -25,6 +25,7 @@ type ProductionSetup struct { osDetector *OSDetector archDetector *ArchitectureDetector resourceChecker *ResourceChecker + portChecker *PortChecker fsProvisioner *FilesystemProvisioner userProvisioner *UserProvisioner stateDetector *StateDetector @@ -90,6 +91,7 @@ func NewProductionSetup(debrosHome string, logWriter io.Writer, forceReconfigure osDetector: &OSDetector{}, archDetector: &ArchitectureDetector{}, resourceChecker: NewResourceChecker(), + portChecker: NewPortChecker(), fsProvisioner: NewFilesystemProvisioner(debrosHome), userProvisioner: NewUserProvisioner("debros", debrosHome, "/bin/bash"), stateDetector: NewStateDetector(debrosDir), @@ -258,6 +260,11 @@ func (ps *ProductionSetup) Phase2bInstallBinaries() error { ps.logf(" ⚠️ Olric install warning: %v", err) } + // Install anyone-client for SOCKS5 proxy + if err := ps.binaryInstaller.InstallAnyoneClient(); err != nil { + ps.logf(" ⚠️ anyone-client install warning: %v", err) + } + // Install DeBros binaries if err := ps.binaryInstaller.InstallDeBrosBinaries(ps.branch, ps.debrosHome, ps.skipRepoUpdate); err != nil { return fmt.Errorf("failed to install DeBros binaries: %w", err) @@ -439,9 +446,13 @@ func (ps *ProductionSetup) Phase4GenerateConfigs(isBootstrap bool, bootstrapPeer } ps.logf(" ✓ Gateway config generated") - // Olric config - bind to 0.0.0.0 to listen on all interfaces + // Olric config - bind to vpsIP if provided, otherwise all interfaces // Gateway will connect using the specific address from olricServers list above - olricConfig, err := ps.configGenerator.GenerateOlricConfig("0.0.0.0", 3320, 3322) + olricBindAddr := vpsIP + if olricBindAddr == "" { + olricBindAddr = "0.0.0.0" + } + olricConfig, err := ps.configGenerator.GenerateOlricConfig(olricBindAddr, 3320, 3322) if err != nil { return fmt.Errorf("failed to generate olric config: %w", err) } @@ -522,6 +533,13 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(nodeType string, vpsIP st } ps.logf(" ✓ Gateway service created") + // Anyone Client service (SOCKS5 proxy) + anyoneUnit := ps.serviceGenerator.GenerateAnyoneClientService() + if err := ps.serviceController.WriteServiceUnit("debros-anyone-client.service", anyoneUnit); err != nil { + return fmt.Errorf("failed to write Anyone Client service: %w", err) + } + ps.logf(" ✓ Anyone Client service created") + // Reload systemd daemon if err := ps.serviceController.DaemonReload(); err != nil { return fmt.Errorf("failed to reload systemd: %w", err) @@ -529,7 +547,7 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(nodeType string, vpsIP st ps.logf(" ✓ Systemd daemon reloaded") // Enable services (RQLite is managed by node, not as separate service) - services := []string{unitName, clusterUnitName, "debros-olric.service", nodeUnitName, "debros-gateway.service"} + services := []string{unitName, clusterUnitName, "debros-olric.service", nodeUnitName, "debros-gateway.service", "debros-anyone-client.service"} for _, svc := range services { if err := ps.serviceController.EnableService(svc); err != nil { ps.logf(" ⚠️ Failed to enable %s: %v", svc, err) @@ -541,8 +559,17 @@ func (ps *ProductionSetup) Phase5CreateSystemdServices(nodeType string, vpsIP st // Start services in dependency order ps.logf(" Starting services...") - // Start infrastructure first (IPFS, Olric) - RQLite is managed by node + // Start infrastructure first (IPFS, Olric, Anyone Client) - RQLite is managed by node infraServices := []string{unitName, "debros-olric.service"} + + // Check if port 9050 is already in use (e.g., another anyone-client or similar service) + if ps.portChecker.IsPortInUse(9050) { + ps.logf(" ℹ️ Port 9050 is already in use (anyone-client or similar service running)") + ps.logf(" ℹ️ Skipping debros-anyone-client startup - using existing service") + } else { + infraServices = append(infraServices, "debros-anyone-client.service") + } + for _, svc := range infraServices { if err := ps.serviceController.StartService(svc); err != nil { ps.logf(" ⚠️ Failed to start %s: %v", svc, err) @@ -592,9 +619,11 @@ func (ps *ProductionSetup) LogSetupComplete(peerID string) { ps.logf(" %s/logs/olric.log", ps.debrosDir) ps.logf(" %s/logs/node-bootstrap.log", ps.debrosDir) ps.logf(" %s/logs/gateway.log", ps.debrosDir) + ps.logf(" %s/logs/anyone-client.log", ps.debrosDir) ps.logf("\nStart All Services:") - ps.logf(" systemctl start debros-ipfs-bootstrap debros-ipfs-cluster-bootstrap debros-olric debros-node-bootstrap debros-gateway") + ps.logf(" systemctl start debros-ipfs-bootstrap debros-ipfs-cluster-bootstrap debros-olric debros-anyone-client debros-node-bootstrap debros-gateway") ps.logf("\nVerify Installation:") ps.logf(" curl http://localhost:6001/health") - ps.logf(" curl http://localhost:5001/status\n") + ps.logf(" curl http://localhost:5001/status") + ps.logf(" # Anyone Client SOCKS5 proxy on localhost:9050\n") } diff --git a/pkg/environments/production/services.go b/pkg/environments/production/services.go index c0839b5..171a156 100644 --- a/pkg/environments/production/services.go +++ b/pkg/environments/production/services.go @@ -266,6 +266,37 @@ WantedBy=multi-user.target `, nodeService, olricService, nodeService, olricService, ssg.debrosHome, ssg.debrosHome, ssg.debrosHome, ssg.debrosDir, logFile, logFile, ssg.debrosDir) } +// GenerateAnyoneClientService generates the Anyone Client SOCKS5 proxy systemd unit +func (ssg *SystemdServiceGenerator) GenerateAnyoneClientService() string { + logFile := filepath.Join(ssg.debrosDir, "logs", "anyone-client.log") + + return fmt.Sprintf(`[Unit] +Description=Anyone Client SOCKS5 Proxy +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=debros +Group=debros +Environment=HOME=%[1]s +ExecStart=/usr/bin/env anyone-client +Restart=always +RestartSec=5 +StandardOutput=file:%[2]s +StandardError=file:%[2]s +SyslogIdentifier=anyone-client + +NoNewPrivileges=yes +PrivateTmp=yes +ProtectSystem=strict +ReadWritePaths=%[3]s + +[Install] +WantedBy=multi-user.target +`, ssg.debrosHome, logFile, ssg.debrosDir) +} + // SystemdController manages systemd service operations type SystemdController struct { systemdDir string