mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-10-06 08:19:07 +00:00
Add comprehensive production security for RQLite clustering
Production Security Features: - RQLite authentication with secure user management - Firewall configuration with IP-based restrictions - Automated credential generation and storage - Authenticated cluster join addresses - Credential masking in logs for security - Helper scripts for secure RQLite connections Network Architecture: - Port 4000: Public LibP2P P2P (encrypted) - Port 4001/4002: RQLite cluster (IP-restricted to cluster members) - UFW firewall rules restricting RQLite access to cluster IPs only Security Components: - /opt/debros/configs/rqlite-users.json: User authentication - /opt/debros/keys/rqlite-cluster-auth: Secure credential storage - Automatic credential masking in logs - Production-ready setup script This implements enterprise-grade security for public network deployment while maintaining seamless cluster communication between trusted nodes.
This commit is contained in:
parent
16a70a03aa
commit
3af1b58eb4
@ -55,6 +55,7 @@ func (r *RQLiteManager) Start(ctx context.Context) error {
|
|||||||
args := []string{
|
args := []string{
|
||||||
"-http-addr", fmt.Sprintf("0.0.0.0:%d", r.config.RQLitePort),
|
"-http-addr", fmt.Sprintf("0.0.0.0:%d", r.config.RQLitePort),
|
||||||
"-raft-addr", fmt.Sprintf("0.0.0.0:%d", r.config.RQLiteRaftPort),
|
"-raft-addr", fmt.Sprintf("0.0.0.0:%d", r.config.RQLiteRaftPort),
|
||||||
|
"-auth", "/opt/debros/configs/rqlite-users.json", // Enable authentication
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add advertised addresses if we have an external IP
|
// Add advertised addresses if we have an external IP
|
||||||
@ -65,23 +66,33 @@ func (r *RQLiteManager) Start(ctx context.Context) error {
|
|||||||
|
|
||||||
// Add join address if specified (for non-bootstrap or secondary bootstrap nodes)
|
// Add join address if specified (for non-bootstrap or secondary bootstrap nodes)
|
||||||
if r.config.RQLiteJoinAddress != "" {
|
if r.config.RQLiteJoinAddress != "" {
|
||||||
r.logger.Info("Joining RQLite cluster", zap.String("join_address", r.config.RQLiteJoinAddress))
|
r.logger.Info("Joining RQLite cluster", zap.String("join_address", r.maskCredentials(r.config.RQLiteJoinAddress)))
|
||||||
|
|
||||||
|
// Check for authenticated join address with credentials
|
||||||
|
joinAddress := r.config.RQLiteJoinAddress
|
||||||
|
if !strings.Contains(joinAddress, "@") {
|
||||||
|
// Try to load authentication credentials for cluster joining
|
||||||
|
if authAddr := r.loadAuthenticatedJoinAddress(); authAddr != "" {
|
||||||
|
joinAddress = authAddr
|
||||||
|
r.logger.Info("Using authenticated cluster join address")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Validate join address format before using it
|
// Validate join address format before using it
|
||||||
if strings.HasPrefix(r.config.RQLiteJoinAddress, "http://") {
|
if strings.HasPrefix(joinAddress, "http://") {
|
||||||
// Test connectivity and log the results, but always attempt to join
|
// Test connectivity and log the results, but always attempt to join
|
||||||
if err := r.testJoinAddress(r.config.RQLiteJoinAddress); err != nil {
|
if err := r.testJoinAddress(r.stripCredentials(joinAddress)); err != nil {
|
||||||
r.logger.Warn("Join address connectivity test failed, but will still attempt to join",
|
r.logger.Warn("Join address connectivity test failed, but will still attempt to join",
|
||||||
zap.String("join_address", r.config.RQLiteJoinAddress),
|
zap.String("join_address", r.maskCredentials(joinAddress)),
|
||||||
zap.Error(err))
|
zap.Error(err))
|
||||||
} else {
|
} else {
|
||||||
r.logger.Info("Join address is reachable, proceeding with cluster join")
|
r.logger.Info("Join address is reachable, proceeding with cluster join")
|
||||||
}
|
}
|
||||||
// Always add the join parameter - let RQLite handle retries
|
// Always add the join parameter - let RQLite handle retries
|
||||||
args = append(args, "-join", r.config.RQLiteJoinAddress)
|
args = append(args, "-join", joinAddress)
|
||||||
} else {
|
} else {
|
||||||
r.logger.Warn("Invalid join address format, skipping join", zap.String("address", r.config.RQLiteJoinAddress))
|
r.logger.Warn("Invalid join address format, skipping join", zap.String("address", r.maskCredentials(joinAddress)))
|
||||||
return fmt.Errorf("invalid RQLite join address format: %s (must start with http://)", r.config.RQLiteJoinAddress)
|
return fmt.Errorf("invalid RQLite join address format: %s (must start with http://)", r.maskCredentials(joinAddress))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
r.logger.Info("No join address specified - starting as new cluster")
|
r.logger.Info("No join address specified - starting as new cluster")
|
||||||
@ -321,3 +332,89 @@ func (r *RQLiteManager) testJoinAddress(joinAddress string) error {
|
|||||||
r.logger.Info("Join address is reachable", zap.String("address", joinAddress))
|
r.logger.Info("Join address is reachable", zap.String("address", joinAddress))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loadAuthenticatedJoinAddress loads authentication credentials and creates authenticated join URL
|
||||||
|
func (r *RQLiteManager) loadAuthenticatedJoinAddress() string {
|
||||||
|
// Try to load authentication credentials from environment file
|
||||||
|
if data, err := os.ReadFile("/opt/debros/configs/rqlite-env"); err == nil {
|
||||||
|
lines := strings.Split(string(data), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if strings.HasPrefix(line, "RQLITE_JOIN_ADDRESS_AUTH=") {
|
||||||
|
authAddr := strings.TrimPrefix(line, "RQLITE_JOIN_ADDRESS_AUTH=")
|
||||||
|
authAddr = strings.Trim(authAddr, `"`)
|
||||||
|
if authAddr != "" {
|
||||||
|
return authAddr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: try to construct from separate user/pass environment variables
|
||||||
|
if data, err := os.ReadFile("/opt/debros/keys/rqlite-cluster-auth"); err == nil {
|
||||||
|
lines := strings.Split(string(data), "\n")
|
||||||
|
var user, pass string
|
||||||
|
for _, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if strings.HasPrefix(line, "RQLITE_CLUSTER_USER=") {
|
||||||
|
user = strings.TrimPrefix(line, "RQLITE_CLUSTER_USER=")
|
||||||
|
user = strings.Trim(user, `"`)
|
||||||
|
} else if strings.HasPrefix(line, "RQLITE_CLUSTER_PASS=") {
|
||||||
|
pass = strings.TrimPrefix(line, "RQLITE_CLUSTER_PASS=")
|
||||||
|
pass = strings.Trim(pass, `"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if user != "" && pass != "" && r.config.RQLiteJoinAddress != "" {
|
||||||
|
// Extract base URL and add credentials
|
||||||
|
baseURL := r.config.RQLiteJoinAddress
|
||||||
|
if strings.HasPrefix(baseURL, "http://") {
|
||||||
|
// Insert credentials: http://user:pass@host:port
|
||||||
|
host := strings.TrimPrefix(baseURL, "http://")
|
||||||
|
return fmt.Sprintf("http://%s:%s@%s", user, pass, host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "" // No credentials found
|
||||||
|
}
|
||||||
|
|
||||||
|
// maskCredentials masks authentication credentials in URLs for logging
|
||||||
|
func (r *RQLiteManager) maskCredentials(url string) string {
|
||||||
|
if strings.Contains(url, "@") {
|
||||||
|
// URL contains credentials: http://user:pass@host:port
|
||||||
|
parts := strings.SplitN(url, "@", 2)
|
||||||
|
if len(parts) == 2 {
|
||||||
|
protocolAndCreds := parts[0]
|
||||||
|
hostAndPort := parts[1]
|
||||||
|
|
||||||
|
// Extract protocol
|
||||||
|
protocolParts := strings.SplitN(protocolAndCreds, "://", 2)
|
||||||
|
if len(protocolParts) == 2 {
|
||||||
|
protocol := protocolParts[0]
|
||||||
|
return fmt.Sprintf("%s://***:***@%s", protocol, hostAndPort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
// stripCredentials removes authentication credentials from URLs for connectivity testing
|
||||||
|
func (r *RQLiteManager) stripCredentials(url string) string {
|
||||||
|
if strings.Contains(url, "@") {
|
||||||
|
// URL contains credentials: http://user:pass@host:port
|
||||||
|
parts := strings.SplitN(url, "@", 2)
|
||||||
|
if len(parts) == 2 {
|
||||||
|
protocolAndCreds := parts[0]
|
||||||
|
hostAndPort := parts[1]
|
||||||
|
|
||||||
|
// Extract protocol
|
||||||
|
protocolParts := strings.SplitN(protocolAndCreds, "://", 2)
|
||||||
|
if len(protocolParts) == 2 {
|
||||||
|
protocol := protocolParts[0]
|
||||||
|
return fmt.Sprintf("%s://%s", protocol, hostAndPort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
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