proxy-pool/internal/controller/worker/identity.go

73 lines
2.1 KiB
Go

package worker
import (
"context"
"errors"
"fmt"
"strings"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/peer"
)
var ErrUnauthorizedIdentity = errors.New("worker identity is not authorized")
// AllowLoopbackIdentity is used only by the validated loopback plaintext mode.
// It deliberately does not inspect transport credentials because that mode has no TLS peer.
type AllowLoopbackIdentity struct{}
func (AllowLoopbackIdentity) Authorize(context.Context, string) error {
return nil
}
type SPIFFEIdentityAuthorizer struct {
trustDomain string
environment string
}
func NewSPIFFEIdentityAuthorizer(trustDomain, environment string) (*SPIFFEIdentityAuthorizer, error) {
if strings.TrimSpace(trustDomain) == "" || strings.TrimSpace(environment) == "" {
return nil, fmt.Errorf("spiffe identity authorizer: trust domain and environment are required")
}
return &SPIFFEIdentityAuthorizer{trustDomain: trustDomain, environment: environment}, nil
}
func (authorizer *SPIFFEIdentityAuthorizer) Authorize(ctx context.Context, workerID string) error {
if authorizer == nil || workerID == "" {
return ErrUnauthorizedIdentity
}
peerInfo, ok := peer.FromContext(ctx)
if !ok || peerInfo.AuthInfo == nil {
return ErrUnauthorizedIdentity
}
tlsInfo, ok := peerInfo.AuthInfo.(credentials.TLSInfo)
if !ok || len(tlsInfo.State.VerifiedChains) == 0 {
return ErrUnauthorizedIdentity
}
expectedPath := "/" + authorizer.environment + "/worker/" + workerID
workerIdentityCount := 0
for _, chain := range tlsInfo.State.VerifiedChains {
if len(chain) == 0 || chain[0] == nil {
continue
}
for _, uri := range chain[0].URIs {
if uri == nil || uri.Scheme != "spiffe" || uri.Host != authorizer.trustDomain {
continue
}
segments := strings.Split(strings.Trim(uri.Path, "/"), "/")
if len(segments) != 3 || segments[1] != "worker" {
continue
}
workerIdentityCount++
if workerIdentityCount > 1 || uri.RawQuery != "" || uri.Fragment != "" || uri.Path != expectedPath {
return ErrUnauthorizedIdentity
}
}
}
if workerIdentityCount != 1 {
return ErrUnauthorizedIdentity
}
return nil
}