From 56b99168450becf8f57687151c6557f333aa7aa8 Mon Sep 17 00:00:00 2001 From: Lucas Bremgartner Date: Wed, 9 Jul 2025 09:41:23 +0200 Subject: [PATCH 1/2] incusd: Cluster join, ensure server address The provided server address is validated against `core.https_address` and it is made sure, that the necessary configurations are taken, namely `core.https_address` and `cluster.https_address` if necessary. After everything is checked and put in the correct state, it is save to use the address from the join request for the subsequent operations. Signed-off-by: Lucas Bremgartner --- cmd/incusd/api_cluster.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cmd/incusd/api_cluster.go b/cmd/incusd/api_cluster.go index 4496312715a..5aeeffded62 100644 --- a/cmd/incusd/api_cluster.go +++ b/cmd/incusd/api_cluster.go @@ -457,8 +457,6 @@ func clusterPutJoin(d *Daemon, r *http.Request, req api.ClusterPut) response.Res if err != nil { return response.SmartError(err) } - - localHTTPSAddress = req.ServerAddress } else { // The user has previously set core.https_address and // is now providing a cluster address as well. If they @@ -468,8 +466,6 @@ func clusterPutJoin(d *Daemon, r *http.Request, req api.ClusterPut) response.Res if err != nil { return response.SmartError(err) } - - localHTTPSAddress = req.ServerAddress } // Update the cluster.https_address config key. @@ -644,7 +640,7 @@ func clusterPutJoin(d *Daemon, r *http.Request, req api.ClusterPut) response.Res }) // Now request for this node to be added to the list of cluster nodes. - info, err := clusterAcceptMember(client, req.ServerName, localHTTPSAddress, cluster.SchemaVersion, version.APIExtensionsCount(), pools, networks) + info, err := clusterAcceptMember(client, req.ServerName, req.ServerAddress, cluster.SchemaVersion, version.APIExtensionsCount(), pools, networks) if err != nil { return fmt.Errorf("Failed request to add member: %w", err) } From 6da4fe3dd02baf4078052aadda807df7a442b144 Mon Sep 17 00:00:00 2001 From: Lucas Bremgartner Date: Wed, 9 Jul 2025 10:02:32 +0200 Subject: [PATCH 2/2] incusd: Cluster join, check cluster.https_address If cluster.https_address has been previously configured, the server address provided with the cluster join request should be verified against this address. If the two address do not match, this is an error. Signed-off-by: Lucas Bremgartner --- cmd/incusd/api_cluster.go | 109 +++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 50 deletions(-) diff --git a/cmd/incusd/api_cluster.go b/cmd/incusd/api_cluster.go index 5aeeffded62..dc3772b68ce 100644 --- a/cmd/incusd/api_cluster.go +++ b/cmd/incusd/api_cluster.go @@ -425,72 +425,81 @@ func clusterPutJoin(d *Daemon, r *http.Request, req api.ClusterPut) response.Res return response.BadRequest(fmt.Errorf("Invalid server address %q: %w", req.ServerAddress, err)) } - localHTTPSAddress := s.LocalConfig.HTTPSAddress() + // Verify provided address against cluster.https_address if set. + localHTTPSAddress := s.LocalConfig.ClusterAddress() + if localHTTPSAddress != "" { + if !internalUtil.IsAddressCovered(req.ServerAddress, localHTTPSAddress) { + return response.BadRequest(fmt.Errorf(`Server address %q is not covered by %q from "cluster.https_address"`, req.ServerAddress, localHTTPSAddress)) + } + } else { + // If cluster.https_address is not set, check against core.https_address + localHTTPSAddress = s.LocalConfig.HTTPSAddress() - var config *node.Config + var config *node.Config - if localHTTPSAddress == "" { - // As the user always provides a server address, but no networking - // was setup on this node, let's do the job and open the - // port. We'll use the same address both for the REST API and - // for clustering. + if localHTTPSAddress == "" { + // As the user always provides a server address, but no networking + // was setup on this node, let's do the job and open the + // port. We'll use the same address both for the REST API and + // for clustering. - // First try to listen to the provided address. If we fail, we - // won't actually update the database config. - err := s.Endpoints.NetworkUpdateAddress(req.ServerAddress) - if err != nil { - return response.SmartError(err) - } - - err = s.DB.Node.Transaction(r.Context(), func(ctx context.Context, tx *db.NodeTx) error { - config, err = node.ConfigLoad(ctx, tx) + // First try to listen to the provided address. If we fail, we + // won't actually update the database config. + err := s.Endpoints.NetworkUpdateAddress(req.ServerAddress) if err != nil { - return fmt.Errorf("Failed to load cluster config: %w", err) + return response.SmartError(err) } - _, err = config.Patch(map[string]string{ - "core.https_address": req.ServerAddress, - "cluster.https_address": req.ServerAddress, + err = s.DB.Node.Transaction(r.Context(), func(ctx context.Context, tx *db.NodeTx) error { + config, err = node.ConfigLoad(ctx, tx) + if err != nil { + return fmt.Errorf("Failed to load cluster config: %w", err) + } + + _, err = config.Patch(map[string]string{ + "core.https_address": req.ServerAddress, + "cluster.https_address": req.ServerAddress, + }) + return err }) - return err - }) - if err != nil { - return response.SmartError(err) - } - } else { - // The user has previously set core.https_address and - // is now providing a cluster address as well. If they - // differ we need to listen to it. - if !internalUtil.IsAddressCovered(req.ServerAddress, localHTTPSAddress) { - err := s.Endpoints.ClusterUpdateAddress(req.ServerAddress) if err != nil { return response.SmartError(err) } - } + } else { + // The user has previously set core.https_address and + // is now providing a cluster address as well. If they + // differ we need to listen to it. + if !internalUtil.IsAddressCovered(req.ServerAddress, localHTTPSAddress) { + err := s.Endpoints.ClusterUpdateAddress(req.ServerAddress) + if err != nil { + return response.SmartError(err) + } + } - // Update the cluster.https_address config key. - err := s.DB.Node.Transaction(r.Context(), func(ctx context.Context, tx *db.NodeTx) error { - var err error + // Update the cluster.https_address config key. + err := s.DB.Node.Transaction(r.Context(), func(ctx context.Context, tx *db.NodeTx) error { + var err error - config, err = node.ConfigLoad(ctx, tx) - if err != nil { - return fmt.Errorf("Failed to load cluster config: %w", err) - } + config, err = node.ConfigLoad(ctx, tx) + if err != nil { + return fmt.Errorf("Failed to load cluster config: %w", err) + } - _, err = config.Patch(map[string]string{ - "cluster.https_address": req.ServerAddress, + _, err = config.Patch(map[string]string{ + "cluster.https_address": req.ServerAddress, + }) + return err }) - return err - }) - if err != nil { - return response.SmartError(err) + if err != nil { + return response.SmartError(err) + } } - } - // Update local config cache. - d.globalConfigMu.Lock() - d.localConfig = config - d.globalConfigMu.Unlock() + // Update local config cache. + d.globalConfigMu.Lock() + d.localConfig = config + d.globalConfigMu.Unlock() + } // Client parameters to connect to the target cluster node. serverCert := s.ServerCert()