This commit is contained in:
anonpenguin 2025-08-06 20:25:51 +03:00
commit f62b9c6423
7 changed files with 585 additions and 42 deletions

View File

@ -10,6 +10,8 @@ import (
"strings"
"time"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
"git.debros.io/DeBros/network/pkg/client"
"git.debros.io/DeBros/network/pkg/constants"
)
@ -56,6 +58,8 @@ func main() {
os.Exit(1)
}
handleConnect(args[0])
case "peer-id":
handlePeerID()
case "help", "--help", "-h":
showHelp()
default:
@ -351,6 +355,72 @@ func handleConnect(peerAddr string) {
fmt.Printf("✅ Connected to peer: %s\n", peerAddr)
}
func handlePeerID() {
// Try to get peer ID from running network first
client, err := createClient()
if err == nil {
defer client.Disconnect()
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
if status, err := client.Network().GetStatus(ctx); err == nil {
if format == "json" {
printJSON(map[string]string{"peer_id": status.NodeID})
} else {
fmt.Printf("🆔 Peer ID: %s\n", status.NodeID)
}
return
}
}
// Fallback: try to extract from local identity files
identityPaths := []string{
"/opt/debros/data/node/identity.key",
"/opt/debros/data/bootstrap/identity.key",
"/opt/debros/keys/node/identity.key",
"./data/node/identity.key",
"./data/bootstrap/identity.key",
}
for _, path := range identityPaths {
if peerID := extractPeerIDFromFile(path); peerID != "" {
if format == "json" {
printJSON(map[string]string{"peer_id": peerID, "source": "local_identity"})
} else {
fmt.Printf("🆔 Peer ID: %s\n", peerID)
fmt.Printf("📂 Source: %s\n", path)
}
return
}
}
// Check peer.info files as last resort
peerInfoPaths := []string{
"/opt/debros/data/node/peer.info",
"/opt/debros/data/bootstrap/peer.info",
"./data/node/peer.info",
"./data/bootstrap/peer.info",
}
for _, path := range peerInfoPaths {
if data, err := os.ReadFile(path); err == nil {
multiaddr := strings.TrimSpace(string(data))
if peerID := extractPeerIDFromMultiaddr(multiaddr); peerID != "" {
if format == "json" {
printJSON(map[string]string{"peer_id": peerID, "source": "peer_info"})
} else {
fmt.Printf("🆔 Peer ID: %s\n", peerID)
fmt.Printf("📂 Source: %s\n", path)
}
return
}
}
}
fmt.Fprintf(os.Stderr, "❌ Could not find peer ID. Make sure the node is running or identity files exist.\n")
os.Exit(1)
}
func createClient() (client.NetworkClient, error) {
var bootstrapPeers []string
@ -445,6 +515,7 @@ func showHelp() {
fmt.Printf(" health - Check network health\n")
fmt.Printf(" peers - List connected peers\n")
fmt.Printf(" status - Show network status\n")
fmt.Printf(" peer-id - Show this node's peer ID\n")
fmt.Printf(" query <sql> - Execute database query\n")
fmt.Printf(" storage get <key> - Get value from storage\n")
fmt.Printf(" storage put <key> <value> - Store value in storage\n")
@ -461,6 +532,8 @@ func showHelp() {
fmt.Printf(" --production - Connect to production bootstrap peers\n\n")
fmt.Printf("Examples:\n")
fmt.Printf(" network-cli health\n")
fmt.Printf(" network-cli peer-id\n")
fmt.Printf(" network-cli peer-id --format json\n")
fmt.Printf(" network-cli peers --format json\n")
fmt.Printf(" network-cli peers --production\n")
fmt.Printf(" network-cli storage put user:123 '{\"name\":\"Alice\"}'\n")
@ -594,3 +667,39 @@ func formatBytes(bytes int64) string {
}
return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}
// extractPeerIDFromFile extracts peer ID from an identity key file
func extractPeerIDFromFile(keyFile string) string {
// Read the identity key file
data, err := os.ReadFile(keyFile)
if err != nil {
return ""
}
// Unmarshal the private key
priv, err := crypto.UnmarshalPrivateKey(data)
if err != nil {
return ""
}
// Get the public key
pub := priv.GetPublic()
// Get the peer ID
peerID, err := peer.IDFromPublicKey(pub)
if err != nil {
return ""
}
return peerID.String()
}
// extractPeerIDFromMultiaddr extracts the peer ID from a multiaddr string
func extractPeerIDFromMultiaddr(multiaddr string) string {
// Look for /p2p/ followed by the peer ID
parts := strings.Split(multiaddr, "/p2p/")
if len(parts) >= 2 {
return parts[1]
}
return ""
}

View File

@ -6,6 +6,7 @@ import (
"fmt"
"log"
"os"
"os/exec"
"os/signal"
"path/filepath"
"strings"
@ -47,8 +48,8 @@ func main() {
}
}
// All nodes use port 4001 for consistency
port := 4001
// LibP2P uses port 4000, RQLite uses 4001
port := 4000
// Create logger with appropriate component type
var logger *logging.StandardLogger
@ -84,28 +85,77 @@ func main() {
cfg.Database.RQLiteRaftPort = 4002
if isBootstrap {
// Bootstrap node doesn't join anyone - it starts the cluster
cfg.Database.RQLiteJoinAddress = ""
logger.Printf("Bootstrap node - starting new RQLite cluster")
} else {
// Regular nodes join the bootstrap node's RQLite cluster
cfg.Database.RQLiteJoinAddress = "http://localhost:4001"
// Check if this is the primary bootstrap node (first in list) or secondary
bootstrapPeers := constants.GetBootstrapPeers()
isSecondaryBootstrap := false
if len(bootstrapPeers) > 1 {
// Check if this machine matches any bootstrap peer other than the first
for i := 1; i < len(bootstrapPeers); i++ {
host := parseHostFromMultiaddr(bootstrapPeers[i])
if host != "" && isLocalIP(host) {
isSecondaryBootstrap = true
break
}
}
}
if isSecondaryBootstrap {
// Secondary bootstrap nodes join the primary bootstrap
primaryBootstrapHost := parseHostFromMultiaddr(bootstrapPeers[0])
cfg.Database.RQLiteJoinAddress = fmt.Sprintf("http://%s:4001", primaryBootstrapHost)
logger.Printf("Secondary bootstrap node - joining primary bootstrap at: %s", cfg.Database.RQLiteJoinAddress)
} else {
// Primary bootstrap node doesn't join anyone - it starts the cluster
cfg.Database.RQLiteJoinAddress = ""
logger.Printf("Primary bootstrap node - starting new RQLite cluster")
}
} else {
// Configure bootstrap peers for P2P discovery
var rqliteJoinAddr string
if *bootstrap != "" {
// Use command line bootstrap if provided
cfg.Discovery.BootstrapPeers = []string{*bootstrap}
// Extract IP from bootstrap peer for RQLite join
bootstrapHost := parseHostFromMultiaddr(*bootstrap)
if bootstrapHost != "" {
rqliteJoinAddr = fmt.Sprintf("http://%s:4001", bootstrapHost)
logger.Printf("Using extracted bootstrap host %s for RQLite join", bootstrapHost)
} else {
logger.Printf("Warning: Could not extract host from bootstrap peer %s, using localhost fallback", *bootstrap)
rqliteJoinAddr = "http://localhost:4001" // Use localhost fallback instead
}
logger.Printf("Using command line bootstrap peer: %s", *bootstrap)
} else {
// Use environment-configured bootstrap peers
bootstrapPeers := constants.GetBootstrapPeers()
if len(bootstrapPeers) > 0 {
cfg.Discovery.BootstrapPeers = bootstrapPeers
logger.Printf("Using environment bootstrap peers: %v", bootstrapPeers)
// Use the first bootstrap peer for RQLite join
bootstrapHost := parseHostFromMultiaddr(bootstrapPeers[0])
if bootstrapHost != "" {
rqliteJoinAddr = fmt.Sprintf("http://%s:4001", bootstrapHost)
logger.Printf("Using extracted bootstrap host %s for RQLite join", bootstrapHost)
} else {
logger.Printf("Warning: Could not extract host from bootstrap peer %s", bootstrapPeers[0])
// Try primary production server as fallback
rqliteJoinAddr = "http://localhost:4001"
}
logger.Printf("Using environment bootstrap peers: %v", bootstrapPeers)
} else {
logger.Printf("Warning: No bootstrap peers configured")
// Default to localhost when no peers configured
rqliteJoinAddr = "http://localhost:4001"
logger.Printf("Using localhost fallback for RQLite join")
}
// Log network connectivity diagnostics
logger.Printf("=== NETWORK DIAGNOSTICS ===")
logger.Printf("Target RQLite join address: %s", rqliteJoinAddr)
runNetworkDiagnostics(rqliteJoinAddr, logger)
}
// Regular nodes join the bootstrap node's RQLite cluster
cfg.Database.RQLiteJoinAddress = rqliteJoinAddr
logger.Printf("Regular node - joining RQLite cluster at: %s", cfg.Database.RQLiteJoinAddress)
}
@ -159,8 +209,12 @@ func isBootstrapNode() bool {
return true // In development, assume we're running the bootstrap
}
// Check if this is a production bootstrap server
// You could add more sophisticated host matching here
// Check if this is a production bootstrap server by IP
if host != "" && isLocalIP(host) {
return true
}
// Check if this is a production bootstrap server by hostname
if hostname != "" && strings.Contains(peerAddr, hostname) {
return true
}
@ -184,6 +238,28 @@ func parseHostFromMultiaddr(multiaddr string) string {
return ""
}
// isLocalIP checks if the given IP address belongs to this machine
func isLocalIP(ip string) bool {
// Try to run ip command to get local IPs
if output, err := exec.Command("ip", "addr", "show").Output(); err == nil {
if strings.Contains(string(output), ip) {
return true
}
}
// Fallback: try hostname -I command
if output, err := exec.Command("hostname", "-I").Output(); err == nil {
ips := strings.Fields(strings.TrimSpace(string(output)))
for _, localIP := range ips {
if localIP == ip {
return true
}
}
}
return false
}
func startNode(ctx context.Context, cfg *config.Config, port int, isBootstrap bool, logger *logging.StandardLogger) error {
// Create and start node using the unified node implementation
n, err := node.NewNode(cfg)
@ -217,3 +293,75 @@ func startNode(ctx context.Context, cfg *config.Config, port int, isBootstrap bo
// Stop node
return n.Stop()
}
// runNetworkDiagnostics performs network connectivity tests
func runNetworkDiagnostics(rqliteJoinAddr string, logger *logging.StandardLogger) {
// Extract host and port from the join address
if !strings.HasPrefix(rqliteJoinAddr, "http://") {
logger.Printf("Invalid join address format: %s", rqliteJoinAddr)
return
}
// Parse URL to extract host:port
url := strings.TrimPrefix(rqliteJoinAddr, "http://")
parts := strings.Split(url, ":")
if len(parts) != 2 {
logger.Printf("Cannot parse host:port from %s", rqliteJoinAddr)
return
}
host := parts[0]
port := parts[1]
logger.Printf("Testing connectivity to %s:%s", host, port)
// Test 1: Basic connectivity with netcat or telnet
if output, err := exec.Command("timeout", "5", "nc", "-z", "-v", host, port).CombinedOutput(); err == nil {
logger.Printf("✅ Port %s:%s is reachable", host, port)
logger.Printf("netcat output: %s", strings.TrimSpace(string(output)))
} else {
logger.Printf("❌ Port %s:%s is NOT reachable", host, port)
logger.Printf("netcat error: %v", err)
logger.Printf("netcat output: %s", strings.TrimSpace(string(output)))
}
// Test 2: HTTP connectivity test
if output, err := exec.Command("timeout", "5", "curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", rqliteJoinAddr+"/status").Output(); err == nil {
httpCode := strings.TrimSpace(string(output))
if httpCode == "200" {
logger.Printf("✅ HTTP service is responding correctly (status: %s)", httpCode)
} else {
logger.Printf("⚠️ HTTP service responded with status: %s", httpCode)
}
} else {
logger.Printf("❌ HTTP request failed: %v", err)
}
// Test 3: Ping test
if output, err := exec.Command("ping", "-c", "3", "-W", "2", host).Output(); err == nil {
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.Contains(line, "packet loss") {
logger.Printf("🏓 Ping result: %s", strings.TrimSpace(line))
break
}
}
} else {
logger.Printf("❌ Ping test failed: %v", err)
}
// Test 4: DNS resolution
if output, err := exec.Command("nslookup", host).Output(); err == nil {
logger.Printf("🔍 DNS resolution successful")
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.Contains(line, "Address:") && !strings.Contains(line, "#53") {
logger.Printf("DNS result: %s", strings.TrimSpace(line))
}
}
} else {
logger.Printf("❌ DNS resolution failed: %v", err)
}
logger.Printf("=== END DIAGNOSTICS ===")
}

View File

@ -18,8 +18,8 @@ var (
// BootstrapAddresses are the full multiaddrs for bootstrap nodes
BootstrapAddresses []string
// BootstrapPort is the default port for bootstrap nodes
BootstrapPort int = 4001
// BootstrapPort is the default port for bootstrap nodes (LibP2P)
BootstrapPort int = 4000
)
// Load environment variables and initialize bootstrap configuration
@ -91,12 +91,12 @@ func setDefaultBootstrapConfig() {
if env := os.Getenv("ENVIRONMENT"); env == "production" {
// Production: only use live production peers
BootstrapPeerIDs = []string{
"12D3KooWQRK2duw5B5LXi8gA7HBBFiCsLvwyph2ZU9VBmvbE1Nei",
"12D3KooWNxt9bNvqftdqXg98JcUHreGxedWSZRUbyqXJ6CW7GaD4",
"12D3KooWGbdnA22bN24X2gyY1o9jozwTBq9wbfvwtJ7G4XQ9JgFm",
}
BootstrapAddresses = []string{
"/ip4/57.129.81.31/tcp/4001/p2p/12D3KooWQRK2duw5B5LXi8gA7HBBFiCsLvwyph2ZU9VBmvbE1Nei",
"/ip4/38.242.250.186/tcp/4001/p2p/12D3KooWGbdnA22bN24X2gyY1o9jozwTBq9wbfvwtJ7G4XQ9JgFm",
"/ip4/57.129.81.31/tcp/4000/p2p/12D3KooWNxt9bNvqftdqXg98JcUHreGxedWSZRUbyqXJ6CW7GaD4",
"/ip4/38.242.250.186/tcp/4000/p2p/12D3KooWGbdnA22bN24X2gyY1o9jozwTBq9wbfvwtJ7G4XQ9JgFm",
}
} else {
// Development: only use localhost bootstrap
@ -104,10 +104,10 @@ func setDefaultBootstrapConfig() {
"12D3KooWBQAr9Lj9Z3918wBT523tJaRiPN6zRywAtttvPrwcZfJb",
}
BootstrapAddresses = []string{
"/ip4/127.0.0.1/tcp/4001/p2p/12D3KooWBQAr9Lj9Z3918wBT523tJaRiPN6zRywAtttvPrwcZfJb",
"/ip4/127.0.0.1/tcp/4000/p2p/12D3KooWBQAr9Lj9Z3918wBT523tJaRiPN6zRywAtttvPrwcZfJb",
}
}
BootstrapPort = 4001
BootstrapPort = 4000
}
// extractPeerIDFromMultiaddr extracts the peer ID from a multiaddr string

View File

@ -3,10 +3,12 @@ package database
import (
"context"
"fmt"
"net"
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/rqlite/gorqlite"
@ -41,15 +43,49 @@ func (r *RQLiteManager) Start(ctx context.Context) error {
return fmt.Errorf("failed to create RQLite data directory: %w", err)
}
// Get the external IP address for advertising
externalIP, err := r.getExternalIP()
if err != nil {
r.logger.Warn("Failed to get external IP, using localhost", zap.Error(err))
externalIP = "localhost"
}
r.logger.Info("Using external IP for RQLite advertising", zap.String("ip", externalIP))
// Build RQLite command
args := []string{
"-http-addr", fmt.Sprintf("localhost:%d", r.config.RQLitePort),
"-raft-addr", fmt.Sprintf("localhost:%d", r.config.RQLiteRaftPort),
"-http-addr", fmt.Sprintf("0.0.0.0:%d", r.config.RQLitePort),
"-raft-addr", fmt.Sprintf("0.0.0.0:%d", r.config.RQLiteRaftPort),
// Auth disabled for testing
}
// Add join address if specified (for non-bootstrap nodes)
// Add advertised addresses if we have an external IP
if externalIP != "localhost" {
args = append(args, "-http-adv-addr", fmt.Sprintf("%s:%d", externalIP, r.config.RQLitePort))
args = append(args, "-raft-adv-addr", fmt.Sprintf("%s:%d", externalIP, r.config.RQLiteRaftPort))
}
// Add join address if specified (for non-bootstrap or secondary bootstrap nodes)
if r.config.RQLiteJoinAddress != "" {
args = append(args, "-join", r.config.RQLiteJoinAddress)
r.logger.Info("Joining RQLite cluster", zap.String("join_address", r.config.RQLiteJoinAddress))
// Validate join address format before using it
if strings.HasPrefix(r.config.RQLiteJoinAddress, "http://") {
// Test connectivity and log the results, but always attempt to join
if err := r.testJoinAddress(r.config.RQLiteJoinAddress); err != nil {
r.logger.Warn("Join address connectivity test failed, but will still attempt to join",
zap.String("join_address", r.config.RQLiteJoinAddress),
zap.Error(err))
} else {
r.logger.Info("Join address is reachable, proceeding with cluster join")
}
// Always add the join parameter - let RQLite handle retries
args = append(args, "-join", r.config.RQLiteJoinAddress)
} else {
r.logger.Warn("Invalid join address format, skipping join", zap.String("address", r.config.RQLiteJoinAddress))
return fmt.Errorf("invalid RQLite join address format: %s (must start with http://)", r.config.RQLiteJoinAddress)
}
} else {
r.logger.Info("No join address specified - starting as new cluster")
}
// Add data directory as positional argument
@ -60,6 +96,8 @@ func (r *RQLiteManager) Start(ctx context.Context) error {
zap.Int("http_port", r.config.RQLitePort),
zap.Int("raft_port", r.config.RQLiteRaftPort),
zap.String("join_address", r.config.RQLiteJoinAddress),
zap.String("external_ip", externalIP),
zap.Strings("full_args", args),
)
// Start RQLite process
@ -168,3 +206,120 @@ func (r *RQLiteManager) Stop() error {
return nil
}
// getExternalIP attempts to get the external IP address of this machine
func (r *RQLiteManager) getExternalIP() (string, error) {
// Method 1: Try using `ip route get` to find the IP used to reach the internet
if output, err := exec.Command("ip", "route", "get", "8.8.8.8").Output(); err == nil {
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.Contains(line, "src") {
parts := strings.Fields(line)
for i, part := range parts {
if part == "src" && i+1 < len(parts) {
ip := parts[i+1]
if net.ParseIP(ip) != nil {
r.logger.Debug("Found external IP via ip route", zap.String("ip", ip))
return ip, nil
}
}
}
}
}
}
// Method 2: Get all network interfaces and find non-localhost, non-private IPs
interfaces, err := net.Interfaces()
if err != nil {
return "", err
}
for _, iface := range interfaces {
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
continue
}
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
if ip == nil || ip.IsLoopback() {
continue
}
// Prefer public IPs over private IPs
if ip.To4() != nil && !ip.IsPrivate() {
r.logger.Debug("Found public IP", zap.String("ip", ip.String()))
return ip.String(), nil
}
}
}
// Method 3: Fall back to private IPs if no public IP found
for _, iface := range interfaces {
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
continue
}
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
if ip == nil || ip.IsLoopback() {
continue
}
// Use any IPv4 address
if ip.To4() != nil {
r.logger.Debug("Found private IP", zap.String("ip", ip.String()))
return ip.String(), nil
}
}
}
return "", fmt.Errorf("no suitable IP address found")
}
// testJoinAddress tests if a join address is reachable
func (r *RQLiteManager) testJoinAddress(joinAddress string) error {
// Test connection to the join address with a short timeout
client := &http.Client{Timeout: 5 * time.Second}
// Try to connect to the status endpoint
statusURL := joinAddress + "/status"
r.logger.Debug("Testing join address", zap.String("url", statusURL))
resp, err := client.Get(statusURL)
if err != nil {
return fmt.Errorf("failed to connect to join address %s: %w", joinAddress, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("join address %s returned status %d", joinAddress, resp.StatusCode)
}
r.logger.Info("Join address is reachable", zap.String("address", joinAddress))
return nil
}

View File

@ -58,7 +58,6 @@ func NewNode(cfg *config.Config) (*Node, error) {
func (n *Node) Start(ctx context.Context) error {
n.logger.ComponentInfo(logging.ComponentNode, "Starting network node",
zap.String("data_dir", n.config.Node.DataDir),
zap.String("type", "bootstrap"),
)
// Create data directory

View File

@ -15,7 +15,7 @@ NOCOLOR='\033[0m'
INSTALL_DIR="/opt/debros"
REPO_URL="https://git.debros.io/DeBros/network.git"
MIN_GO_VERSION="1.19"
NODE_PORT="4001"
NODE_PORT="4000" # LibP2P port for peer-to-peer communication
RQLITE_PORT="4001" # All nodes use same RQLite port to join same cluster
RAFT_PORT="4002" # All nodes use same Raft port
UPDATE_MODE=false
@ -98,18 +98,12 @@ detect_os() {
# Check if DeBros Network is already installed
check_existing_installation() {
if [ -d "$INSTALL_DIR" ] && [ -f "$INSTALL_DIR/bin/bootstrap" ] && [ -f "$INSTALL_DIR/bin/node" ]; then
if [ -d "$INSTALL_DIR" ] && [ -f "$INSTALL_DIR/bin/node" ]; then
log "Found existing DeBros Network installation at $INSTALL_DIR"
# Check if services are running
BOOTSTRAP_RUNNING=false
# Check if service is running
NODE_RUNNING=false
if systemctl is-active --quiet debros-bootstrap.service 2>/dev/null; then
BOOTSTRAP_RUNNING=true
log "Bootstrap service is currently running"
fi
if systemctl is-active --quiet debros-node.service 2>/dev/null; then
NODE_RUNNING=true
log "Node service is currently running"
@ -578,13 +572,11 @@ build_binaries() {
if [ "$UPDATE_MODE" = true ]; then
log "Update mode: checking for running services before binary update..."
for service in debros-bootstrap debros-node; do
if systemctl is-active --quiet $service.service 2>/dev/null; then
log "Stopping $service service to update binaries..."
sudo systemctl stop $service.service
services_were_running+=("$service")
fi
done
if systemctl is-active --quiet debros-node.service 2>/dev/null; then
log "Stopping debros-node service to update binaries..."
sudo systemctl stop debros-node.service
services_were_running+=("debros-node")
fi
# Give services a moment to fully stop
if [ ${#services_were_running[@]} -gt 0 ]; then
@ -669,7 +661,7 @@ configure_firewall() {
log "Required ports to allow:"
log " - Port $NODE_PORT (Node)"
log " - Port $RQLITE_PORT (RQLite)"
log " - Port $RAFT_NODE_PORT (Raft)"
log " - Port $RAFT_PORT (Raft)"
fi
fi
}
@ -693,7 +685,7 @@ create_systemd_service() {
# Determine the correct ExecStart command based on node type
local exec_start=""
exec_start="$INSTALL_DIR/bin/node -data $INSTALL_DIR/data/node -port $NODE_PORT"
exec_start="$INSTALL_DIR/bin/node -data $INSTALL_DIR/data/node"
cat > /tmp/debros-$NODE_TYPE.service << EOF
[Unit]
@ -828,7 +820,7 @@ main() {
log "${GREEN}Node Port:${NOCOLOR} ${CYAN}$NODE_PORT${NOCOLOR}"
log "${GREEN}RQLite Port:${NOCOLOR} ${CYAN}$RQLITE_PORT${NOCOLOR}"
log "${GREEN}Raft Port:${NOCOLOR} ${CYAN}$RAFT_NODE_PORT${NOCOLOR}"
log "${GREEN}Raft Port:${NOCOLOR} ${CYAN}$RAFT_PORT${NOCOLOR}"
log "${BLUE}==================================================${NOCOLOR}"
log "${GREEN}Management Commands:${NOCOLOR}"

View File

@ -0,0 +1,140 @@
#!/bin/bash
set -euo pipefail
# DeBros Network Production Security Setup
# This script configures secure RQLite clustering with authentication
DEBROS_DIR="/opt/debros"
CONFIG_DIR="$DEBROS_DIR/configs"
KEYS_DIR="$DEBROS_DIR/keys"
echo "🔐 Setting up DeBros Network Production Security..."
# Create security directories
sudo mkdir -p "$CONFIG_DIR" "$KEYS_DIR"
sudo chown debros:debros "$CONFIG_DIR" "$KEYS_DIR"
sudo chmod 750 "$KEYS_DIR"
# Generate cluster authentication credentials
CLUSTER_USER="debros_cluster"
CLUSTER_PASS=$(openssl rand -base64 32)
API_USER="debros_api"
API_PASS=$(openssl rand -base64 32)
echo "🔑 Generated cluster credentials:"
echo " Cluster User: $CLUSTER_USER"
echo " API User: $API_USER"
# Create RQLite users configuration
cat > "$CONFIG_DIR/rqlite-users.json" << EOF
[
{
"username": "$CLUSTER_USER",
"password": "$CLUSTER_PASS",
"perms": ["*"]
},
{
"username": "$API_USER",
"password": "$API_PASS",
"perms": ["status", "ready", "nodes", "db:*"]
}
]
EOF
sudo chown debros:debros "$CONFIG_DIR/rqlite-users.json"
sudo chmod 600 "$CONFIG_DIR/rqlite-users.json"
# Store credentials securely
cat > "$KEYS_DIR/rqlite-cluster-auth" << EOF
RQLITE_CLUSTER_USER="$CLUSTER_USER"
RQLITE_CLUSTER_PASS="$CLUSTER_PASS"
RQLITE_API_USER="$API_USER"
RQLITE_API_PASS="$API_PASS"
EOF
sudo chown debros:debros "$KEYS_DIR/rqlite-cluster-auth"
sudo chmod 600 "$KEYS_DIR/rqlite-cluster-auth"
# Configure firewall for production
echo "🛡️ Configuring production firewall rules..."
# Reset UFW to defaults
sudo ufw --force reset
# Default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# SSH (adjust port as needed)
sudo ufw allow 22/tcp comment "SSH"
# LibP2P P2P networking (public, encrypted)
sudo ufw allow 4000/tcp comment "LibP2P P2P"
sudo ufw allow 4000/udp comment "LibP2P QUIC"
# RQLite ports (restrict to cluster IPs only)
BOOTSTRAP_IPS=("57.129.81.31" "38.242.250.186")
for ip in "${BOOTSTRAP_IPS[@]}"; do
sudo ufw allow from "$ip" to any port 4001 comment "RQLite HTTP from $ip"
sudo ufw allow from "$ip" to any port 4002 comment "RQLite Raft from $ip"
done
# Enable firewall
sudo ufw --force enable
echo "🔧 Configuring RQLite cluster authentication..."
# Update RQLite join addresses with authentication
AUTHENTICATED_JOIN_ADDRESS="http://$CLUSTER_USER:$CLUSTER_PASS@57.129.81.31:4001"
# Create environment file for authenticated connections
cat > "$CONFIG_DIR/rqlite-env" << EOF
# RQLite cluster authentication
RQLITE_JOIN_AUTH_USER="$CLUSTER_USER"
RQLITE_JOIN_AUTH_PASS="$CLUSTER_PASS"
RQLITE_JOIN_ADDRESS_AUTH="$AUTHENTICATED_JOIN_ADDRESS"
EOF
sudo chown debros:debros "$CONFIG_DIR/rqlite-env"
sudo chmod 600 "$CONFIG_DIR/rqlite-env"
# Create connection helper script
cat > "$DEBROS_DIR/bin/rqlite-connect" << 'EOF'
#!/bin/bash
# Helper script for authenticated RQLite connections
source /opt/debros/keys/rqlite-cluster-auth
if [ "$1" = "cluster" ]; then
rqlite -H localhost -p 4001 -u "$RQLITE_CLUSTER_USER" -p "$RQLITE_CLUSTER_PASS"
elif [ "$1" = "api" ]; then
rqlite -H localhost -p 4001 -u "$RQLITE_API_USER" -p "$RQLITE_API_PASS"
else
echo "Usage: $0 {cluster|api}"
exit 1
fi
EOF
sudo chown debros:debros "$DEBROS_DIR/bin/rqlite-connect"
sudo chmod 755 "$DEBROS_DIR/bin/rqlite-connect"
echo "✅ Production security setup complete!"
echo ""
echo "📋 Security Summary:"
echo " - RQLite authentication enabled"
echo " - Firewall configured with IP restrictions"
echo " - Cluster credentials generated and stored"
echo " - Port 4000: Public LibP2P (encrypted P2P)"
echo " - Port 4001/4002: RQLite cluster (IP-restricted)"
echo ""
echo "🔐 Credentials stored in:"
echo " - Users: $CONFIG_DIR/rqlite-users.json"
echo " - Auth: $KEYS_DIR/rqlite-cluster-auth"
echo ""
echo "🔌 Connect to RQLite:"
echo " - Cluster admin: $DEBROS_DIR/bin/rqlite-connect cluster"
echo " - API access: $DEBROS_DIR/bin/rqlite-connect api"
echo ""
echo "⚠️ IMPORTANT: Save these credentials securely!"
echo " Cluster User: $CLUSTER_USER"
echo " Cluster Pass: $CLUSTER_PASS"