feat: update RQLite configuration for direct TLS support

- Modified the RQLite node configuration to use direct TLS on port 7002 when HTTPS is enabled, bypassing SNI gateway conflicts.
- Updated the join address logic to reflect the new direct RQLite TLS connection method.
- Enhanced documentation comments to clarify the changes in TLS handling and port usage for Raft communication.
This commit is contained in:
anonpenguin23 2025-11-28 15:14:26 +02:00
parent 3ca4e1f43b
commit 3505a6a0eb
3 changed files with 51 additions and 17 deletions

View File

@ -106,13 +106,15 @@ func (cg *ConfigGenerator) GenerateNodeConfig(peerAddresses []string, vpsIP stri
} }
// Determine advertise addresses - use vpsIP if provided // Determine advertise addresses - use vpsIP if provided
// When HTTPS/SNI is enabled, use domain-based raft address for SNI routing // When HTTPS is enabled, RQLite uses native TLS on port 7002 (not SNI gateway)
// This avoids conflicts between SNI gateway TLS termination and RQLite's native TLS
var httpAdvAddr, raftAdvAddr string var httpAdvAddr, raftAdvAddr string
if vpsIP != "" { if vpsIP != "" {
httpAdvAddr = net.JoinHostPort(vpsIP, "5001") httpAdvAddr = net.JoinHostPort(vpsIP, "5001")
if enableHTTPS && domain != "" { if enableHTTPS {
// Use SNI domain for Raft advertisement so other nodes connect via SNI gateway // Use direct IP:7002 for Raft - RQLite handles TLS natively via -node-cert
raftAdvAddr = fmt.Sprintf("raft.%s:7001", domain) // This bypasses the SNI gateway which would cause TLS termination conflicts
raftAdvAddr = net.JoinHostPort(vpsIP, "7002")
} else { } else {
raftAdvAddr = net.JoinHostPort(vpsIP, "7001") raftAdvAddr = net.JoinHostPort(vpsIP, "7001")
} }
@ -123,15 +125,26 @@ func (cg *ConfigGenerator) GenerateNodeConfig(peerAddresses []string, vpsIP stri
} }
// Determine RQLite join address // Determine RQLite join address
// When HTTPS is enabled, use port 7002 (direct RQLite TLS) instead of 7001 (SNI gateway)
joinPort := "7001"
if enableHTTPS {
joinPort = "7002"
}
var rqliteJoinAddr string var rqliteJoinAddr string
if joinAddress != "" { if joinAddress != "" {
// Use explicitly provided join address // Use explicitly provided join address
rqliteJoinAddr = joinAddress // If it contains :7001 and HTTPS is enabled, update to :7002
if enableHTTPS && strings.Contains(joinAddress, ":7001") {
rqliteJoinAddr = strings.Replace(joinAddress, ":7001", ":7002", 1)
} else {
rqliteJoinAddr = joinAddress
}
} else if len(peerAddresses) > 0 { } else if len(peerAddresses) > 0 {
// Infer join address from peers // Infer join address from peers
peerIP := inferPeerIP(peerAddresses, "") peerIP := inferPeerIP(peerAddresses, "")
if peerIP != "" { if peerIP != "" {
rqliteJoinAddr = net.JoinHostPort(peerIP, "7001") rqliteJoinAddr = net.JoinHostPort(peerIP, joinPort)
// Validate that join address doesn't match this node's own raft address (would cause self-join) // Validate that join address doesn't match this node's own raft address (would cause self-join)
if rqliteJoinAddr == raftAdvAddr { if rqliteJoinAddr == raftAdvAddr {
rqliteJoinAddr = "" // Clear it - this is the first node rqliteJoinAddr = "" // Clear it - this is the first node
@ -176,14 +189,13 @@ func (cg *ConfigGenerator) GenerateNodeConfig(peerAddresses []string, vpsIP stri
HTTPSPort: httpsPort, HTTPSPort: httpsPort,
} }
// When HTTPS/SNI is enabled, configure RQLite node-to-node TLS encryption // When HTTPS is enabled, configure RQLite node-to-node TLS encryption
// This allows Raft traffic to be routed through the SNI gateway // RQLite handles TLS natively on port 7002, bypassing the SNI gateway
// Uses the same certificates as the SNI gateway (Let's Encrypt or self-signed) // This avoids TLS termination conflicts between SNI gateway and RQLite
if enableHTTPS && domain != "" { if enableHTTPS && domain != "" {
data.NodeCert = filepath.Join(tlsCacheDir, domain+".crt") data.NodeCert = filepath.Join(tlsCacheDir, domain+".crt")
data.NodeKey = filepath.Join(tlsCacheDir, domain+".key") data.NodeKey = filepath.Join(tlsCacheDir, domain+".key")
// Skip verification since nodes may have different domain certificates // Skip verification since nodes may have different domain certificates
// and we're routing through the SNI gateway which terminates TLS
data.NodeNoVerify = true data.NodeNoVerify = true
} }

View File

@ -15,7 +15,7 @@ database:
rqlite_port: {{.RQLiteHTTPPort}} rqlite_port: {{.RQLiteHTTPPort}}
rqlite_raft_port: {{.RQLiteRaftInternalPort}} rqlite_raft_port: {{.RQLiteRaftInternalPort}}
rqlite_join_address: "{{.RQLiteJoinAddress}}" rqlite_join_address: "{{.RQLiteJoinAddress}}"
{{if .NodeCert}}# Node-to-node TLS encryption for Raft communication (required for SNI gateway routing) {{if .NodeCert}}# Node-to-node TLS encryption for Raft communication (direct RQLite TLS on port 7002)
node_cert: "{{.NodeCert}}" node_cert: "{{.NodeCert}}"
node_key: "{{.NodeKey}}" node_key: "{{.NodeKey}}"
{{if .NodeCACert}}node_ca_cert: "{{.NodeCACert}}" {{if .NodeCACert}}node_ca_cert: "{{.NodeCACert}}"
@ -68,7 +68,7 @@ http_gateway:
cert_file: "{{.TLSCacheDir}}/{{.Domain}}.crt" cert_file: "{{.TLSCacheDir}}/{{.Domain}}.crt"
key_file: "{{.TLSCacheDir}}/{{.Domain}}.key" key_file: "{{.TLSCacheDir}}/{{.Domain}}.key"
routes: routes:
raft.{{.Domain}}: "localhost:{{.RQLiteRaftInternalPort}}" # Note: Raft traffic bypasses SNI gateway - RQLite uses native TLS on port 7002
ipfs.{{.Domain}}: "localhost:4101" ipfs.{{.Domain}}: "localhost:4101"
ipfs-cluster.{{.Domain}}: "localhost:9096" ipfs-cluster.{{.Domain}}: "localhost:9096"
olric.{{.Domain}}: "localhost:3322" olric.{{.Domain}}: "localhost:3322"

View File

@ -25,7 +25,8 @@ type InstallerConfig struct {
VpsIP string VpsIP string
Domain string Domain string
PeerDomain string // Domain of existing node to join PeerDomain string // Domain of existing node to join
JoinAddress string // Auto-populated: raft.{PeerDomain}:7001 PeerIP string // Resolved IP of peer domain (for Raft join)
JoinAddress string // Auto-populated: {PeerIP}:7002 (direct RQLite TLS)
Peers []string // Auto-populated: /dns4/{PeerDomain}/tcp/4001/p2p/{PeerID} Peers []string // Auto-populated: /dns4/{PeerDomain}/tcp/4001/p2p/{PeerID}
ClusterSecret string ClusterSecret string
SwarmKeyHex string // 64-hex IPFS swarm key (for joining private network) SwarmKeyHex string // 64-hex IPFS swarm key (for joining private network)
@ -280,8 +281,28 @@ func (m *Model) handleEnter() (tea.Model, tea.Cmd) {
m.config.PeerDomain = peerDomain m.config.PeerDomain = peerDomain
m.discoveredPeer = discovery.PeerID m.discoveredPeer = discovery.PeerID
// Auto-populate join address and bootstrap peers // Resolve peer domain to IP for direct RQLite TLS connection
m.config.JoinAddress = fmt.Sprintf("raft.%s:7001", peerDomain) // RQLite uses native TLS on port 7002 (not SNI gateway on 7001)
peerIPs, err := net.LookupIP(peerDomain)
if err != nil || len(peerIPs) == 0 {
m.err = fmt.Errorf("failed to resolve peer domain %s to IP: %w", peerDomain, err)
return m, nil
}
// Prefer IPv4
var peerIP string
for _, ip := range peerIPs {
if ip.To4() != nil {
peerIP = ip.String()
break
}
}
if peerIP == "" {
peerIP = peerIPs[0].String()
}
m.config.PeerIP = peerIP
// Auto-populate join address (direct RQLite TLS on port 7002) and bootstrap peers
m.config.JoinAddress = fmt.Sprintf("%s:7002", peerIP)
m.config.Peers = []string{ m.config.Peers = []string{
fmt.Sprintf("/dns4/%s/tcp/4001/p2p/%s", peerDomain, discovery.PeerID), fmt.Sprintf("/dns4/%s/tcp/4001/p2p/%s", peerDomain, discovery.PeerID),
} }
@ -836,12 +857,13 @@ func detectPublicIP() string {
} }
// validateSNIDNSRecords checks if the required SNI DNS records exist // validateSNIDNSRecords checks if the required SNI DNS records exist
// It tries to resolve the key SNI hostnames for RQLite, IPFS, IPFS Cluster, and Olric // It tries to resolve the key SNI hostnames for IPFS, IPFS Cluster, and Olric
// Note: Raft no longer uses SNI - it uses direct RQLite TLS on port 7002
// All should resolve to the same IP (the node's public IP or domain) // All should resolve to the same IP (the node's public IP or domain)
func validateSNIDNSRecords(domain string) error { func validateSNIDNSRecords(domain string) error {
// List of SNI services that need DNS records // List of SNI services that need DNS records
// Note: raft.domain is NOT included - RQLite uses direct TLS on port 7002
sniServices := []string{ sniServices := []string{
fmt.Sprintf("raft.%s", domain),
fmt.Sprintf("ipfs.%s", domain), fmt.Sprintf("ipfs.%s", domain),
fmt.Sprintf("ipfs-cluster.%s", domain), fmt.Sprintf("ipfs-cluster.%s", domain),
fmt.Sprintf("olric.%s", domain), fmt.Sprintf("olric.%s", domain),