Skip to content

Commit b526ba4

Browse files
feat[agent-manager](tenants): persist collector tenant from connection-key on registration (#2483)
1 parent 4629d1b commit b526ba4

3 files changed

Lines changed: 61 additions & 14 deletions

File tree

‎agent-manager/agent/collector_imp.go‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,20 @@ func InitCollectorService() {
148148
}
149149

150150
func (s *CollectorService) RegisterCollector(ctx context.Context, req *RegisterRequest) (*AuthResponse, error) {
151+
tenantID, ok := tenantFromContext(ctx)
152+
if !ok {
153+
// The interceptor is the only writer of this key on the connection-key
154+
// path; missing it means the route was reached through some other auth
155+
// that shouldn't be allowed to enrol collectors.
156+
return nil, status.Error(codes.PermissionDenied, "missing tenant on register")
157+
}
158+
151159
collector := &models.Collector{
152160
Ip: req.GetIp(),
153161
Hostname: req.GetHostname(),
154162
Version: req.GetVersion(),
155163
Module: models.CollectorModule(req.GetCollector().String()),
164+
TenantID: tenantID,
156165
}
157166

158167
oldCollector := &models.Collector{}
@@ -178,7 +187,7 @@ func (s *CollectorService) RegisterCollector(ctx context.Context, req *RegisterR
178187
}
179188

180189
s.CacheCollectorKeyMutex.Lock()
181-
entry := utils.ConnectorAuth{Key: key, TenantID: tenantOrDefault(collector.TenantID)}
190+
entry := utils.ConnectorAuth{Key: key, TenantID: collector.TenantID}
182191
s.CacheCollectorKey[collector.ID] = entry
183192
AuthCache.PublishCollector(collector.ID, entry)
184193
s.CacheCollectorKeyMutex.Unlock()

‎agent-manager/agent/interceptor.go‎

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,29 +20,45 @@ func UnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServ
2020
return nil, status.Error(codes.Unauthenticated, "metadata is not provided")
2121
}
2222

23-
err := authHeaders(md, info.FullMethod)
23+
tenantID, err := authHeaders(md, info.FullMethod)
2424
if err != nil {
2525
return nil, err
2626
}
2727

28+
if tenantID != "" {
29+
ctx = withTenant(ctx, tenantID)
30+
}
2831
return handler(ctx, req)
2932
}
3033

34+
type tenantServerStream struct {
35+
grpc.ServerStream
36+
ctx context.Context
37+
}
38+
39+
func (s *tenantServerStream) Context() context.Context { return s.ctx }
40+
3141
func StreamInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
3242
md, ok := metadata.FromIncomingContext(ss.Context())
3343
if !ok {
3444
return status.Error(codes.Unauthenticated, "metadata is not provided")
3545
}
3646

37-
err := authHeaders(md, info.FullMethod)
47+
tenantID, err := authHeaders(md, info.FullMethod)
3848
if err != nil {
3949
return err
4050
}
4151

52+
if tenantID != "" {
53+
ss = &tenantServerStream{ServerStream: ss, ctx: withTenant(ss.Context(), tenantID)}
54+
}
4255
return handler(srv, ss)
4356
}
4457

45-
func authHeaders(md metadata.MD, fullMethod string) error {
58+
// authHeaders returns the tenantID resolved from the auth material when the
59+
// connection-key path matched; otherwise "". A non-nil error means the call
60+
// is unauthenticated and must be rejected.
61+
func authHeaders(md metadata.MD, fullMethod string) (string, error) {
4662
var authType string
4763
var routes []string
4864
authKey := md.Get("key")
@@ -61,43 +77,45 @@ func authHeaders(md metadata.MD, fullMethod string) error {
6177
authType = "internal-key"
6278
routes = config.InternalKeyRoutes()
6379
} else {
64-
return status.Error(codes.Unauthenticated, "auth is not provided")
80+
return "", status.Error(codes.Unauthenticated, "auth is not provided")
6581
}
6682

6783
if !isInRoute(fullMethod, routes) {
68-
return status.Error(codes.PermissionDenied, fmt.Sprintf("route is not registered for authentication with %s auth type", authType))
84+
return "", status.Error(codes.PermissionDenied, fmt.Sprintf("route is not registered for authentication with %s auth type", authType))
6985
}
7086

7187
switch authType {
7288
case "key":
7389
key := authKey[0]
7490
id, err := strconv.ParseUint(authId[0], 10, 32)
7591
if err != nil {
76-
return status.Error(codes.PermissionDenied, "id is not valid")
92+
return "", status.Error(codes.PermissionDenied, "id is not valid")
7793
}
7894
typ := strings.ToLower(connectorType[0])
7995
switch typ {
8096
case "agent":
8197
if !AgentServ.ValidateAgentKey(key, uint(id)) {
82-
return status.Error(codes.PermissionDenied, "invalid key")
98+
return "", status.Error(codes.PermissionDenied, "invalid key")
8399
}
84100
case "collector":
85101
if !CollectorServ.ValidateCollectorKey(key, uint(id)) {
86-
return status.Error(codes.PermissionDenied, "invalid key")
102+
return "", status.Error(codes.PermissionDenied, "invalid key")
87103
}
88104
default:
89-
return status.Error(codes.PermissionDenied, "invalid type")
105+
return "", status.Error(codes.PermissionDenied, "invalid type")
90106
}
91107
case "connection-key":
92-
if !AgentServ.ValidateConnectionKey(authConnectionKey[0]) {
93-
return status.Error(codes.PermissionDenied, "invalid connection key")
108+
tenantID, ok := AgentServ.TenantForConnectionKey(authConnectionKey[0])
109+
if !ok {
110+
return "", status.Error(codes.PermissionDenied, "invalid connection key")
94111
}
112+
return tenantID, nil
95113
case "internal-key":
96114
if !isInternalKeyValid(authInternalKey[0]) {
97-
return status.Error(codes.PermissionDenied, "internal key does not match")
115+
return "", status.Error(codes.PermissionDenied, "internal key does not match")
98116
}
99117
}
100-
return nil
118+
return "", nil
101119
}
102120

103121
func isInternalKeyValid(token string) bool {

‎agent-manager/agent/tenant_ctx.go‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package agent
2+
3+
import "context"
4+
5+
// Handlers on the key/internal-key paths must fall back to the request
6+
// payload (or tenantOrDefault) if tenantFromContext returns false.
7+
8+
type tenantCtxKey struct{}
9+
10+
func withTenant(ctx context.Context, tenantID string) context.Context {
11+
return context.WithValue(ctx, tenantCtxKey{}, tenantID)
12+
}
13+
14+
func tenantFromContext(ctx context.Context) (string, bool) {
15+
t, ok := ctx.Value(tenantCtxKey{}).(string)
16+
if !ok || t == "" {
17+
return "", false
18+
}
19+
return t, true
20+
}

0 commit comments

Comments
 (0)