mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-10-06 08:19:07 +00:00
Merge branch 'main' of https://git.debros.io/DeBros/network
This commit is contained in:
commit
f62b9c6423
109
cmd/cli/main.go
109
cmd/cli/main.go
@ -10,6 +10,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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/client"
|
||||||
"git.debros.io/DeBros/network/pkg/constants"
|
"git.debros.io/DeBros/network/pkg/constants"
|
||||||
)
|
)
|
||||||
@ -56,6 +58,8 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
handleConnect(args[0])
|
handleConnect(args[0])
|
||||||
|
case "peer-id":
|
||||||
|
handlePeerID()
|
||||||
case "help", "--help", "-h":
|
case "help", "--help", "-h":
|
||||||
showHelp()
|
showHelp()
|
||||||
default:
|
default:
|
||||||
@ -351,6 +355,72 @@ func handleConnect(peerAddr string) {
|
|||||||
fmt.Printf("✅ Connected to peer: %s\n", peerAddr)
|
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) {
|
func createClient() (client.NetworkClient, error) {
|
||||||
var bootstrapPeers []string
|
var bootstrapPeers []string
|
||||||
|
|
||||||
@ -445,6 +515,7 @@ func showHelp() {
|
|||||||
fmt.Printf(" health - Check network health\n")
|
fmt.Printf(" health - Check network health\n")
|
||||||
fmt.Printf(" peers - List connected peers\n")
|
fmt.Printf(" peers - List connected peers\n")
|
||||||
fmt.Printf(" status - Show network status\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(" query <sql> - Execute database query\n")
|
||||||
fmt.Printf(" storage get <key> - Get value from storage\n")
|
fmt.Printf(" storage get <key> - Get value from storage\n")
|
||||||
fmt.Printf(" storage put <key> <value> - Store value in 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(" --production - Connect to production bootstrap peers\n\n")
|
||||||
fmt.Printf("Examples:\n")
|
fmt.Printf("Examples:\n")
|
||||||
fmt.Printf(" network-cli health\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 --format json\n")
|
||||||
fmt.Printf(" network-cli peers --production\n")
|
fmt.Printf(" network-cli peers --production\n")
|
||||||
fmt.Printf(" network-cli storage put user:123 '{\"name\":\"Alice\"}'\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])
|
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 ""
|
||||||
|
}
|
||||||
|
170
cmd/node/main.go
170
cmd/node/main.go
@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@ -47,8 +48,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// All nodes use port 4001 for consistency
|
// LibP2P uses port 4000, RQLite uses 4001
|
||||||
port := 4001
|
port := 4000
|
||||||
|
|
||||||
// Create logger with appropriate component type
|
// Create logger with appropriate component type
|
||||||
var logger *logging.StandardLogger
|
var logger *logging.StandardLogger
|
||||||
@ -84,28 +85,77 @@ func main() {
|
|||||||
cfg.Database.RQLiteRaftPort = 4002
|
cfg.Database.RQLiteRaftPort = 4002
|
||||||
|
|
||||||
if isBootstrap {
|
if isBootstrap {
|
||||||
// Bootstrap node doesn't join anyone - it starts the cluster
|
// Check if this is the primary bootstrap node (first in list) or secondary
|
||||||
cfg.Database.RQLiteJoinAddress = ""
|
bootstrapPeers := constants.GetBootstrapPeers()
|
||||||
logger.Printf("Bootstrap node - starting new RQLite cluster")
|
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 {
|
} else {
|
||||||
// Regular nodes join the bootstrap node's RQLite cluster
|
|
||||||
cfg.Database.RQLiteJoinAddress = "http://localhost:4001"
|
|
||||||
|
|
||||||
// Configure bootstrap peers for P2P discovery
|
// Configure bootstrap peers for P2P discovery
|
||||||
|
var rqliteJoinAddr string
|
||||||
if *bootstrap != "" {
|
if *bootstrap != "" {
|
||||||
// Use command line bootstrap if provided
|
// Use command line bootstrap if provided
|
||||||
cfg.Discovery.BootstrapPeers = []string{*bootstrap}
|
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)
|
logger.Printf("Using command line bootstrap peer: %s", *bootstrap)
|
||||||
} else {
|
} else {
|
||||||
// Use environment-configured bootstrap peers
|
// Use environment-configured bootstrap peers
|
||||||
bootstrapPeers := constants.GetBootstrapPeers()
|
bootstrapPeers := constants.GetBootstrapPeers()
|
||||||
if len(bootstrapPeers) > 0 {
|
if len(bootstrapPeers) > 0 {
|
||||||
cfg.Discovery.BootstrapPeers = bootstrapPeers
|
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 {
|
} else {
|
||||||
logger.Printf("Warning: No bootstrap peers configured")
|
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)
|
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
|
return true // In development, assume we're running the bootstrap
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is a production bootstrap server
|
// Check if this is a production bootstrap server by IP
|
||||||
// You could add more sophisticated host matching here
|
if host != "" && isLocalIP(host) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is a production bootstrap server by hostname
|
||||||
if hostname != "" && strings.Contains(peerAddr, hostname) {
|
if hostname != "" && strings.Contains(peerAddr, hostname) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -184,6 +238,28 @@ func parseHostFromMultiaddr(multiaddr string) string {
|
|||||||
return ""
|
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 {
|
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
|
// Create and start node using the unified node implementation
|
||||||
n, err := node.NewNode(cfg)
|
n, err := node.NewNode(cfg)
|
||||||
@ -217,3 +293,75 @@ func startNode(ctx context.Context, cfg *config.Config, port int, isBootstrap bo
|
|||||||
// Stop node
|
// Stop node
|
||||||
return n.Stop()
|
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 ===")
|
||||||
|
}
|
||||||
|
@ -18,8 +18,8 @@ var (
|
|||||||
// BootstrapAddresses are the full multiaddrs for bootstrap nodes
|
// BootstrapAddresses are the full multiaddrs for bootstrap nodes
|
||||||
BootstrapAddresses []string
|
BootstrapAddresses []string
|
||||||
|
|
||||||
// BootstrapPort is the default port for bootstrap nodes
|
// BootstrapPort is the default port for bootstrap nodes (LibP2P)
|
||||||
BootstrapPort int = 4001
|
BootstrapPort int = 4000
|
||||||
)
|
)
|
||||||
|
|
||||||
// Load environment variables and initialize bootstrap configuration
|
// Load environment variables and initialize bootstrap configuration
|
||||||
@ -91,12 +91,12 @@ func setDefaultBootstrapConfig() {
|
|||||||
if env := os.Getenv("ENVIRONMENT"); env == "production" {
|
if env := os.Getenv("ENVIRONMENT"); env == "production" {
|
||||||
// Production: only use live production peers
|
// Production: only use live production peers
|
||||||
BootstrapPeerIDs = []string{
|
BootstrapPeerIDs = []string{
|
||||||
"12D3KooWQRK2duw5B5LXi8gA7HBBFiCsLvwyph2ZU9VBmvbE1Nei",
|
"12D3KooWNxt9bNvqftdqXg98JcUHreGxedWSZRUbyqXJ6CW7GaD4",
|
||||||
"12D3KooWGbdnA22bN24X2gyY1o9jozwTBq9wbfvwtJ7G4XQ9JgFm",
|
"12D3KooWGbdnA22bN24X2gyY1o9jozwTBq9wbfvwtJ7G4XQ9JgFm",
|
||||||
}
|
}
|
||||||
BootstrapAddresses = []string{
|
BootstrapAddresses = []string{
|
||||||
"/ip4/57.129.81.31/tcp/4001/p2p/12D3KooWQRK2duw5B5LXi8gA7HBBFiCsLvwyph2ZU9VBmvbE1Nei",
|
"/ip4/57.129.81.31/tcp/4000/p2p/12D3KooWNxt9bNvqftdqXg98JcUHreGxedWSZRUbyqXJ6CW7GaD4",
|
||||||
"/ip4/38.242.250.186/tcp/4001/p2p/12D3KooWGbdnA22bN24X2gyY1o9jozwTBq9wbfvwtJ7G4XQ9JgFm",
|
"/ip4/38.242.250.186/tcp/4000/p2p/12D3KooWGbdnA22bN24X2gyY1o9jozwTBq9wbfvwtJ7G4XQ9JgFm",
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Development: only use localhost bootstrap
|
// Development: only use localhost bootstrap
|
||||||
@ -104,10 +104,10 @@ func setDefaultBootstrapConfig() {
|
|||||||
"12D3KooWBQAr9Lj9Z3918wBT523tJaRiPN6zRywAtttvPrwcZfJb",
|
"12D3KooWBQAr9Lj9Z3918wBT523tJaRiPN6zRywAtttvPrwcZfJb",
|
||||||
}
|
}
|
||||||
BootstrapAddresses = []string{
|
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
|
// extractPeerIDFromMultiaddr extracts the peer ID from a multiaddr string
|
||||||
|
@ -3,10 +3,12 @@ package database
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rqlite/gorqlite"
|
"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)
|
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
|
// Build RQLite command
|
||||||
args := []string{
|
args := []string{
|
||||||
"-http-addr", fmt.Sprintf("localhost:%d", r.config.RQLitePort),
|
"-http-addr", fmt.Sprintf("0.0.0.0:%d", r.config.RQLitePort),
|
||||||
"-raft-addr", fmt.Sprintf("localhost:%d", r.config.RQLiteRaftPort),
|
"-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 != "" {
|
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
|
// 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("http_port", r.config.RQLitePort),
|
||||||
zap.Int("raft_port", r.config.RQLiteRaftPort),
|
zap.Int("raft_port", r.config.RQLiteRaftPort),
|
||||||
zap.String("join_address", r.config.RQLiteJoinAddress),
|
zap.String("join_address", r.config.RQLiteJoinAddress),
|
||||||
|
zap.String("external_ip", externalIP),
|
||||||
|
zap.Strings("full_args", args),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start RQLite process
|
// Start RQLite process
|
||||||
@ -168,3 +206,120 @@ func (r *RQLiteManager) Stop() error {
|
|||||||
|
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,6 @@ func NewNode(cfg *config.Config) (*Node, error) {
|
|||||||
func (n *Node) Start(ctx context.Context) error {
|
func (n *Node) Start(ctx context.Context) error {
|
||||||
n.logger.ComponentInfo(logging.ComponentNode, "Starting network node",
|
n.logger.ComponentInfo(logging.ComponentNode, "Starting network node",
|
||||||
zap.String("data_dir", n.config.Node.DataDir),
|
zap.String("data_dir", n.config.Node.DataDir),
|
||||||
zap.String("type", "bootstrap"),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create data directory
|
// Create data directory
|
||||||
|
@ -15,7 +15,7 @@ NOCOLOR='\033[0m'
|
|||||||
INSTALL_DIR="/opt/debros"
|
INSTALL_DIR="/opt/debros"
|
||||||
REPO_URL="https://git.debros.io/DeBros/network.git"
|
REPO_URL="https://git.debros.io/DeBros/network.git"
|
||||||
MIN_GO_VERSION="1.19"
|
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
|
RQLITE_PORT="4001" # All nodes use same RQLite port to join same cluster
|
||||||
RAFT_PORT="4002" # All nodes use same Raft port
|
RAFT_PORT="4002" # All nodes use same Raft port
|
||||||
UPDATE_MODE=false
|
UPDATE_MODE=false
|
||||||
@ -98,18 +98,12 @@ detect_os() {
|
|||||||
|
|
||||||
# Check if DeBros Network is already installed
|
# Check if DeBros Network is already installed
|
||||||
check_existing_installation() {
|
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"
|
log "Found existing DeBros Network installation at $INSTALL_DIR"
|
||||||
|
|
||||||
# Check if services are running
|
# Check if service is running
|
||||||
BOOTSTRAP_RUNNING=false
|
|
||||||
NODE_RUNNING=false
|
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
|
if systemctl is-active --quiet debros-node.service 2>/dev/null; then
|
||||||
NODE_RUNNING=true
|
NODE_RUNNING=true
|
||||||
log "Node service is currently running"
|
log "Node service is currently running"
|
||||||
@ -578,13 +572,11 @@ build_binaries() {
|
|||||||
if [ "$UPDATE_MODE" = true ]; then
|
if [ "$UPDATE_MODE" = true ]; then
|
||||||
log "Update mode: checking for running services before binary update..."
|
log "Update mode: checking for running services before binary update..."
|
||||||
|
|
||||||
for service in debros-bootstrap debros-node; do
|
if systemctl is-active --quiet debros-node.service 2>/dev/null; then
|
||||||
if systemctl is-active --quiet $service.service 2>/dev/null; then
|
log "Stopping debros-node service to update binaries..."
|
||||||
log "Stopping $service service to update binaries..."
|
sudo systemctl stop debros-node.service
|
||||||
sudo systemctl stop $service.service
|
services_were_running+=("debros-node")
|
||||||
services_were_running+=("$service")
|
fi
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Give services a moment to fully stop
|
# Give services a moment to fully stop
|
||||||
if [ ${#services_were_running[@]} -gt 0 ]; then
|
if [ ${#services_were_running[@]} -gt 0 ]; then
|
||||||
@ -669,7 +661,7 @@ configure_firewall() {
|
|||||||
log "Required ports to allow:"
|
log "Required ports to allow:"
|
||||||
log " - Port $NODE_PORT (Node)"
|
log " - Port $NODE_PORT (Node)"
|
||||||
log " - Port $RQLITE_PORT (RQLite)"
|
log " - Port $RQLITE_PORT (RQLite)"
|
||||||
log " - Port $RAFT_NODE_PORT (Raft)"
|
log " - Port $RAFT_PORT (Raft)"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@ -693,7 +685,7 @@ create_systemd_service() {
|
|||||||
|
|
||||||
# Determine the correct ExecStart command based on node type
|
# Determine the correct ExecStart command based on node type
|
||||||
local exec_start=""
|
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
|
cat > /tmp/debros-$NODE_TYPE.service << EOF
|
||||||
[Unit]
|
[Unit]
|
||||||
@ -828,7 +820,7 @@ main() {
|
|||||||
|
|
||||||
log "${GREEN}Node Port:${NOCOLOR} ${CYAN}$NODE_PORT${NOCOLOR}"
|
log "${GREEN}Node Port:${NOCOLOR} ${CYAN}$NODE_PORT${NOCOLOR}"
|
||||||
log "${GREEN}RQLite Port:${NOCOLOR} ${CYAN}$RQLITE_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 "${BLUE}==================================================${NOCOLOR}"
|
||||||
log "${GREEN}Management Commands:${NOCOLOR}"
|
log "${GREEN}Management Commands:${NOCOLOR}"
|
||||||
|
140
scripts/setup-production-security.sh
Executable file
140
scripts/setup-production-security.sh
Executable 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"
|
Loading…
x
Reference in New Issue
Block a user