secure-cluster-communication #14

Open
ryyst wants to merge 2 commits from secure-cluster-communication into master
3 changed files with 32 additions and 12 deletions
Showing only changes of commit 852275945c - Show all commits

View File

@@ -82,10 +82,19 @@ func (s *BootstrapService) attemptJoin(seedAddr string) bool {
return false return false
} }
client := &http.Client{Timeout: 10 * time.Second} client := NewAuthenticatedHTTPClient(s.config, 10*time.Second)
url := fmt.Sprintf("http://%s/members/join", seedAddr) protocol := GetProtocol(s.config)
url := fmt.Sprintf("%s://%s/members/join", protocol, seedAddr)
resp, err := client.Post(url, "application/json", bytes.NewBuffer(jsonData)) req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
s.logger.WithError(err).Error("Failed to create join request")
return false
}
req.Header.Set("Content-Type", "application/json")
AddClusterAuthHeaders(req, s.config)
resp, err := client.Do(req)
if err != nil { if err != nil {
s.logger.WithFields(logrus.Fields{ s.logger.WithFields(logrus.Fields{
"seed": seedAddr, "seed": seedAddr,

View File

@@ -125,6 +125,9 @@ EOF
test_cluster_formation() { test_cluster_formation() {
test_start "2-node cluster formation and Merkle Tree replication" test_start "2-node cluster formation and Merkle Tree replication"
# Shared cluster secret for authentication (Issue #13)
local CLUSTER_SECRET="test-cluster-secret-12345678901234567890"
# Node 1 config # Node 1 config
cat > cluster1.yaml <<EOF cat > cluster1.yaml <<EOF
node_id: "cluster-1" node_id: "cluster-1"
@@ -138,6 +141,7 @@ gossip_interval_max: 10
sync_interval: 10 sync_interval: 10
allow_anonymous_read: true allow_anonymous_read: true
allow_anonymous_write: true allow_anonymous_write: true
cluster_secret: "$CLUSTER_SECRET"
EOF EOF
# Node 2 config # Node 2 config
@@ -153,6 +157,7 @@ gossip_interval_max: 10
sync_interval: 10 sync_interval: 10
allow_anonymous_read: true allow_anonymous_read: true
allow_anonymous_write: true allow_anonymous_write: true
cluster_secret: "$CLUSTER_SECRET"
EOF EOF
# Start nodes # Start nodes
@@ -239,6 +244,9 @@ test_conflict_resolution() {
if go run test_conflict.go "$TEST_DIR/conflict1_data" "$TEST_DIR/conflict2_data"; then if go run test_conflict.go "$TEST_DIR/conflict1_data" "$TEST_DIR/conflict2_data"; then
cd "$TEST_DIR" cd "$TEST_DIR"
# Shared cluster secret for authentication (Issue #13)
local CLUSTER_SECRET="conflict-cluster-secret-1234567890123"
# Create configs # Create configs
cat > conflict1.yaml <<EOF cat > conflict1.yaml <<EOF
node_id: "conflict-1" node_id: "conflict-1"
@@ -250,6 +258,7 @@ log_level: "info"
sync_interval: 3 sync_interval: 3
allow_anonymous_read: true allow_anonymous_read: true
allow_anonymous_write: true allow_anonymous_write: true
cluster_secret: "$CLUSTER_SECRET"
EOF EOF
cat > conflict2.yaml <<EOF cat > conflict2.yaml <<EOF
@@ -262,6 +271,7 @@ log_level: "info"
sync_interval: 3 sync_interval: 3
allow_anonymous_read: true allow_anonymous_read: true
allow_anonymous_write: true allow_anonymous_write: true
cluster_secret: "$CLUSTER_SECRET"
EOF EOF
# Start nodes # Start nodes

View File

@@ -43,9 +43,11 @@ func (s *Server) setupRoutes() *mux.Router {
// Member endpoints (available when clustering is enabled) // Member endpoints (available when clustering is enabled)
if s.config.ClusteringEnabled { if s.config.ClusteringEnabled {
// Apply cluster authentication middleware if cluster secret is configured // GET /members/ is unprotected for monitoring/inspection
router.HandleFunc("/members/", s.getMembersHandler).Methods("GET")
// Apply cluster authentication middleware to all cluster communication endpoints
if s.clusterAuthService != nil { if s.clusterAuthService != nil {
router.Handle("/members/", s.clusterAuthService.Middleware(http.HandlerFunc(s.getMembersHandler))).Methods("GET")
router.Handle("/members/join", s.clusterAuthService.Middleware(http.HandlerFunc(s.joinMemberHandler))).Methods("POST") router.Handle("/members/join", s.clusterAuthService.Middleware(http.HandlerFunc(s.joinMemberHandler))).Methods("POST")
router.Handle("/members/leave", s.clusterAuthService.Middleware(http.HandlerFunc(s.leaveMemberHandler))).Methods("DELETE") router.Handle("/members/leave", s.clusterAuthService.Middleware(http.HandlerFunc(s.leaveMemberHandler))).Methods("DELETE")
router.Handle("/members/gossip", s.clusterAuthService.Middleware(http.HandlerFunc(s.gossipHandler))).Methods("POST") router.Handle("/members/gossip", s.clusterAuthService.Middleware(http.HandlerFunc(s.gossipHandler))).Methods("POST")
@@ -57,7 +59,6 @@ func (s *Server) setupRoutes() *mux.Router {
router.Handle("/kv_range", s.clusterAuthService.Middleware(http.HandlerFunc(s.getKVRangeHandler))).Methods("POST") router.Handle("/kv_range", s.clusterAuthService.Middleware(http.HandlerFunc(s.getKVRangeHandler))).Methods("POST")
} else { } else {
// Fallback to unprotected endpoints (for backwards compatibility) // Fallback to unprotected endpoints (for backwards compatibility)
router.HandleFunc("/members/", s.getMembersHandler).Methods("GET")
router.HandleFunc("/members/join", s.joinMemberHandler).Methods("POST") router.HandleFunc("/members/join", s.joinMemberHandler).Methods("POST")
router.HandleFunc("/members/leave", s.leaveMemberHandler).Methods("DELETE") router.HandleFunc("/members/leave", s.leaveMemberHandler).Methods("DELETE")
router.HandleFunc("/members/gossip", s.gossipHandler).Methods("POST") router.HandleFunc("/members/gossip", s.gossipHandler).Methods("POST")