feat(raft): TLS config surface with fail-closed startup
This commit is contained in:
@@ -44,6 +44,11 @@ type
|
|||||||
raftPeerClientAddrs*: Table[string, tuple[host: string, port: int]]
|
raftPeerClientAddrs*: Table[string, tuple[host: string, port: int]]
|
||||||
raftWriteTimeoutMs*: int
|
raftWriteTimeoutMs*: int
|
||||||
raftLogMaxEntries*: int
|
raftLogMaxEntries*: int
|
||||||
|
raftTlsEnabled*: bool
|
||||||
|
raftTlsCertFile*: string
|
||||||
|
raftTlsKeyFile*: string
|
||||||
|
raftTlsCaFile*: string
|
||||||
|
raftTlsVerifyPeer*: bool
|
||||||
|
|
||||||
CompactionStrategy* = enum
|
CompactionStrategy* = enum
|
||||||
csSizeTiered = "size_tiered"
|
csSizeTiered = "size_tiered"
|
||||||
@@ -86,6 +91,11 @@ proc defaultConfig*(): BaraConfig =
|
|||||||
raftPeerClientAddrs: initTable[string, tuple[host: string, port: int]](),
|
raftPeerClientAddrs: initTable[string, tuple[host: string, port: int]](),
|
||||||
raftWriteTimeoutMs: 5_000,
|
raftWriteTimeoutMs: 5_000,
|
||||||
raftLogMaxEntries: 256,
|
raftLogMaxEntries: 256,
|
||||||
|
raftTlsEnabled: false,
|
||||||
|
raftTlsCertFile: "",
|
||||||
|
raftTlsKeyFile: "",
|
||||||
|
raftTlsCaFile: "",
|
||||||
|
raftTlsVerifyPeer: false,
|
||||||
)
|
)
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
@@ -213,6 +223,11 @@ proc loadConfigFromEnv*(cfg: var BaraConfig) =
|
|||||||
cfg.raftNodeId = getEnv("BARADB_RAFT_NODE_ID", cfg.raftNodeId)
|
cfg.raftNodeId = getEnv("BARADB_RAFT_NODE_ID", cfg.raftNodeId)
|
||||||
cfg.raftWriteTimeoutMs = parseEnvInt(getEnv("BARADB_RAFT_WRITE_TIMEOUT_MS", ""), cfg.raftWriteTimeoutMs)
|
cfg.raftWriteTimeoutMs = parseEnvInt(getEnv("BARADB_RAFT_WRITE_TIMEOUT_MS", ""), cfg.raftWriteTimeoutMs)
|
||||||
cfg.raftLogMaxEntries = parseEnvInt(getEnv("BARADB_RAFT_LOG_MAX_ENTRIES", ""), cfg.raftLogMaxEntries)
|
cfg.raftLogMaxEntries = parseEnvInt(getEnv("BARADB_RAFT_LOG_MAX_ENTRIES", ""), cfg.raftLogMaxEntries)
|
||||||
|
cfg.raftTlsEnabled = parseEnvBool(getEnv("BARADB_RAFT_TLS_ENABLED", ""), cfg.raftTlsEnabled)
|
||||||
|
cfg.raftTlsCertFile = getEnv("BARADB_RAFT_TLS_CERT_FILE", cfg.raftTlsCertFile)
|
||||||
|
cfg.raftTlsKeyFile = getEnv("BARADB_RAFT_TLS_KEY_FILE", cfg.raftTlsKeyFile)
|
||||||
|
cfg.raftTlsCaFile = getEnv("BARADB_RAFT_TLS_CA_FILE", cfg.raftTlsCaFile)
|
||||||
|
cfg.raftTlsVerifyPeer = parseEnvBool(getEnv("BARADB_RAFT_TLS_VERIFY_PEER", ""), cfg.raftTlsVerifyPeer)
|
||||||
# Optional: client (SQL) addresses for leader write forwarding.
|
# Optional: client (SQL) addresses for leader write forwarding.
|
||||||
# Same id@host:port shape as BARADB_RAFT_PEERS, but ports are BARADB_PORT values.
|
# Same id@host:port shape as BARADB_RAFT_PEERS, but ports are BARADB_PORT values.
|
||||||
let clientPeersEnv = getEnv("BARADB_RAFT_CLIENT_PEERS", "")
|
let clientPeersEnv = getEnv("BARADB_RAFT_CLIENT_PEERS", "")
|
||||||
|
|||||||
@@ -338,6 +338,12 @@ proc main() =
|
|||||||
var raftNet: RaftNetwork = nil
|
var raftNet: RaftNetwork = nil
|
||||||
if config.raftEnabled:
|
if config.raftEnabled:
|
||||||
info("Starting Raft node " & config.raftNodeId & " on port " & $config.raftPort)
|
info("Starting Raft node " & config.raftNodeId & " on port " & $config.raftPort)
|
||||||
|
if config.raftTlsEnabled:
|
||||||
|
if config.raftTlsCertFile.len == 0 or config.raftTlsKeyFile.len == 0 or
|
||||||
|
not fileExists(config.raftTlsCertFile) or not fileExists(config.raftTlsKeyFile):
|
||||||
|
raise newException(ValueError,
|
||||||
|
"BARADB_RAFT_TLS_ENABLED=true but cert/key missing " &
|
||||||
|
"(BARADB_RAFT_TLS_CERT_FILE / BARADB_RAFT_TLS_KEY_FILE)")
|
||||||
let raftDataDir = config.dataDir / "raft"
|
let raftDataDir = config.dataDir / "raft"
|
||||||
createDir(raftDataDir) # idempotent; loadState reads from it, saveState writes
|
createDir(raftDataDir) # idempotent; loadState reads from it, saveState writes
|
||||||
# Raft convention: `peers` excludes the node itself (majority math and
|
# Raft convention: `peers` excludes the node itself (majority math and
|
||||||
|
|||||||
@@ -521,3 +521,34 @@ suite "Raft write classification":
|
|||||||
check not isRaftDdl(parse("CREATE DATABASE other").stmts[0])
|
check not isRaftDdl(parse("CREATE DATABASE other").stmts[0])
|
||||||
check not isRaftDdl(parse("INSERT INTO t (id) VALUES (1)").stmts[0])
|
check not isRaftDdl(parse("INSERT INTO t (id) VALUES (1)").stmts[0])
|
||||||
check not isRaftDdl(parse("SELECT 1").stmts[0])
|
check not isRaftDdl(parse("SELECT 1").stmts[0])
|
||||||
|
|
||||||
|
|
||||||
|
suite "Raft TLS config":
|
||||||
|
|
||||||
|
test "default config has raft TLS disabled with empty paths":
|
||||||
|
let cfg = defaultConfig()
|
||||||
|
check cfg.raftTlsEnabled == false
|
||||||
|
check cfg.raftTlsCertFile == ""
|
||||||
|
check cfg.raftTlsKeyFile == ""
|
||||||
|
check cfg.raftTlsCaFile == ""
|
||||||
|
check cfg.raftTlsVerifyPeer == false
|
||||||
|
|
||||||
|
test "env vars parse into raft TLS config":
|
||||||
|
putEnv("BARADB_RAFT_TLS_ENABLED", "true")
|
||||||
|
putEnv("BARADB_RAFT_TLS_CERT_FILE", "/tmp/raft.crt")
|
||||||
|
putEnv("BARADB_RAFT_TLS_KEY_FILE", "/tmp/raft.key")
|
||||||
|
putEnv("BARADB_RAFT_TLS_CA_FILE", "/tmp/raft-ca.crt")
|
||||||
|
putEnv("BARADB_RAFT_TLS_VERIFY_PEER", "1")
|
||||||
|
defer:
|
||||||
|
delEnv("BARADB_RAFT_TLS_ENABLED")
|
||||||
|
delEnv("BARADB_RAFT_TLS_CERT_FILE")
|
||||||
|
delEnv("BARADB_RAFT_TLS_KEY_FILE")
|
||||||
|
delEnv("BARADB_RAFT_TLS_CA_FILE")
|
||||||
|
delEnv("BARADB_RAFT_TLS_VERIFY_PEER")
|
||||||
|
var cfg = defaultConfig()
|
||||||
|
loadConfigFromEnv(cfg)
|
||||||
|
check cfg.raftTlsEnabled == true
|
||||||
|
check cfg.raftTlsCertFile == "/tmp/raft.crt"
|
||||||
|
check cfg.raftTlsKeyFile == "/tmp/raft.key"
|
||||||
|
check cfg.raftTlsCaFile == "/tmp/raft-ca.crt"
|
||||||
|
check cfg.raftTlsVerifyPeer == true
|
||||||
|
|||||||
Reference in New Issue
Block a user