[management] Refuse pins onto a host another account's gateway already claims

A host can be claimed by a pin as well as by a proxy row, and the proxy-row
check cannot see that. Another account's labeled pin beneath a host makes
the host its cluster, so a self-addressed endpoint on it would never be
served; another account's self-addressed endpoint on a host makes the proxy
declaring it theirs, so a label beneath it would never be served either.
Both bootstrap paths now refuse those shapes before the insert.

Neither question touches the shared-cluster shape: labeled pins under one
cluster are asked about in neither direction, so any number of accounts
still pin beneath a shared cluster. Two self-addressed endpoints on one
hostname stay the domain unique index's conflict to refuse.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sa3DsBDP3VciAi4PPG17L6
This commit is contained in:
mlsmaycon
2026-09-12 17:02:05 +00:00
co-authored by Claude Fable 5.1
parent 09cc91886f
commit 0df2eb7b25
5 changed files with 135 additions and 1 deletions
@@ -315,6 +315,36 @@ func (s *SqlStore) GetAllAgentNetworkSettings(ctx context.Context, lockStrength
return settings, nil
}
// HasGatewayClusterPinnedByOtherAccount reports whether another account has a
// labeled agent network gateway pinned beneath host, making host its cluster.
// A self-addressed endpoint on the very same hostname is not counted: that
// collision is the domain unique index's to refuse, as a conflict. Case-folded,
// since a settings row written before hostnames were normalised may carry
// capitals; one row per account, so the scan is cheap.
func (s *SqlStore) HasGatewayClusterPinnedByOtherAccount(ctx context.Context, host, accountID string) (bool, error) {
return s.countGatewayRowsByOtherAccount(ctx, "LOWER(proxy_address) = LOWER(?) AND LOWER(domain) <> LOWER(proxy_address)", host, accountID)
}
// HasGatewayEndpointByOtherAccount reports whether host is another account's
// agent network endpoint hostname (domain). Case-folded for the same reason as
// HasGatewayClusterPinnedByOtherAccount.
func (s *SqlStore) HasGatewayEndpointByOtherAccount(ctx context.Context, host, accountID string) (bool, error) {
return s.countGatewayRowsByOtherAccount(ctx, "LOWER(domain) = LOWER(?)", host, accountID)
}
func (s *SqlStore) countGatewayRowsByOtherAccount(ctx context.Context, predicate, host, accountID string) (bool, error) {
var count int64
result := s.db.
Model(&agentNetworkTypes.Settings{}).
Where(predicate+" AND account_id != ?", host, accountID).
Count(&count)
if result.Error != nil {
log.WithContext(ctx).Errorf("failed to check agent network gateway claims at host: %v", result.Error)
return false, status.Errorf(status.Internal, "check agent network gateway claims at host")
}
return count > 0, nil
}
// GetAgentNetworkSettingsByProxyAddress returns every Settings row whose
// gateway is served by the proxy declaring the given cluster address. Used by
// cluster-scoped synthesis to find the accounts a shared proxy serves.
+2
View File
@@ -341,6 +341,8 @@ type Store interface {
IsClusterAddressConflicting(ctx context.Context, clusterAddress, accountID string) (bool, error)
HasActiveProxyAtClusterAddress(ctx context.Context, clusterAddress string) (bool, error)
HasForeignAccountProxyAtHost(ctx context.Context, host, accountID string) (bool, error)
HasGatewayClusterPinnedByOtherAccount(ctx context.Context, host, accountID string) (bool, error)
HasGatewayEndpointByOtherAccount(ctx context.Context, host, accountID string) (bool, error)
DeleteAccountCluster(ctx context.Context, clusterAddress, accountID string) error
GetCustomDomainsCounts(ctx context.Context) (total int64, validated int64, err error)
+30
View File
@@ -3080,6 +3080,36 @@ func (mr *MockStoreMockRecorder) HasForeignAccountProxyAtHost(ctx, host, account
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasForeignAccountProxyAtHost", reflect.TypeOf((*MockStore)(nil).HasForeignAccountProxyAtHost), ctx, host, accountID)
}
// HasGatewayClusterPinnedByOtherAccount mocks base method.
func (m *MockStore) HasGatewayClusterPinnedByOtherAccount(ctx context.Context, host, accountID string) (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "HasGatewayClusterPinnedByOtherAccount", ctx, host, accountID)
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// HasGatewayClusterPinnedByOtherAccount indicates an expected call of HasGatewayClusterPinnedByOtherAccount.
func (mr *MockStoreMockRecorder) HasGatewayClusterPinnedByOtherAccount(ctx, host, accountID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasGatewayClusterPinnedByOtherAccount", reflect.TypeOf((*MockStore)(nil).HasGatewayClusterPinnedByOtherAccount), ctx, host, accountID)
}
// HasGatewayEndpointByOtherAccount mocks base method.
func (m *MockStore) HasGatewayEndpointByOtherAccount(ctx context.Context, host, accountID string) (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "HasGatewayEndpointByOtherAccount", ctx, host, accountID)
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// HasGatewayEndpointByOtherAccount indicates an expected call of HasGatewayEndpointByOtherAccount.
func (mr *MockStoreMockRecorder) HasGatewayEndpointByOtherAccount(ctx, host, accountID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasGatewayEndpointByOtherAccount", reflect.TypeOf((*MockStore)(nil).HasGatewayEndpointByOtherAccount), ctx, host, accountID)
}
// IncrementAgentNetworkConsumption mocks base method.
func (m *MockStore) IncrementAgentNetworkConsumption(ctx context.Context, accountID string, kind types.ConsumptionDimension, dimID string, windowSeconds int64, windowStart time.Time, tokensIn, tokensOut int64, costUSD float64) error {
m.ctrl.T.Helper()