这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

sentry库的identify实现

identify

1 - identify.go

identify结构体定义

结构体定义

// Bundle 包含了足以以识别一个跨信任域和命名空间的工作负载的所有的元素:

type Bundle struct {
	ID          string
	Namespace   string
	TrustDomain string
}

其实就三个元素: ID / Namespace 以及 TrustDomain

NewBundle() 方法

NewBundle() 方法返回一个新的 identity Bundle。

func NewBundle(id, namespace, trustDomain string) *Bundle {
	// Empty namespace and trust domain result in an empty bundle
  // 如果 namespace 或者 trust domain 为空,则返回空的 bundle(nil)
	if namespace == "" || trustDomain == "" {
		return nil
	}

  // 否则指示简单的赋值三个属性
	return &Bundle{
		ID:          id,
		Namespace:   namespace,
		TrustDomain: trustDomain,
	}
}

namespace和trustDomain是可选参数。当为空时,将返回一个 nil 值。

2 - validator.go

validator 接口定义

接口定义

Validator 通过使用 ID 和 token 来验证证书请求的身份

type Validator interface {
	Validate(id, token, namespace string) error
}

3 - spiff.go

创建 spiff ID

CreateSPIFFEID 方法

CreateSPIFFEID() 方法从给定的 trustDomain, namespace, appID 创建符合 SPIFFE 标准的唯一ID:

func CreateSPIFFEID(trustDomain, namespace, appID string) (string, error) {
  // trustDomain, namespace, appID 三者都不能为空
	if trustDomain == "" {
		return "", errors.New("can't create spiffe id: trust domain is empty")
	}
	if namespace == "" {
		return "", errors.New("can't create spiffe id: namespace is empty")
	}
	if appID == "" {
		return "", errors.New("can't create spiffe id: app id is empty")
	}

  // 根据 SPIFFE 规范进行验证
	// Validate according to the SPIFFE spec
	if strings.ContainsRune(trustDomain, ':') {
    // trustDomain不能带":"
		return "", errors.New("trust domain cannot contain the ':' character")
	}
  // trustDomain 的长度不能大于255个 byte
	if len([]byte(trustDomain)) > 255 {
		return "", errors.New("trust domain cannot exceed 255 bytes")
	}

  // 拼接出 SPIFFE ID
	id := fmt.Sprintf("spiffe://%s/ns/%s/%s", trustDomain, namespace, appID)
	if len([]byte(id)) > 2048 {
    // 验证 SPIFFE ID 长度不大于 2048
		return "", errors.New("spiffe id cannot exceed 2048 bytes")
	}
	return id, nil
}

4 - kubernetes下的validator.go

kubernetes下的validator实现

准备工作

结构体定义

validator 结构体定义:

type validator struct {
	client    k8s.Interface
	auth      kauth.AuthenticationV1Interface
	audiences []string
}

创建validator的方法

NewValidator() 方法创建新的 validator 结构体:

func NewValidator(client k8s.Interface, audiences []string) identity.Validator {
	return &validator{
		client:    client,
		auth:      client.AuthenticationV1(),
		audiences: audiences,
	}
}

实现

Validate() 实现通过使用 ID 和 token 来验证证书请求的身份:

func (v *validator) Validate(id, token, namespace string) error {
  // id 和 token 不能为空
	if id == "" {
		return fmt.Errorf("%s: id field in request must not be empty", errPrefix)
	}
	if token == "" {
		return fmt.Errorf("%s: token field in request must not be empty", errPrefix)
	}

	// TODO: Remove in Dapr 1.12 to enforce setting an explicit audience
	var canTryWithNilAudience, showDefaultTokenAudienceWarning bool

	audiences := v.audiences
  
	if len(audiences) == 0 {
    // 处理用户没有显式设置 audience 的特殊情况
    // 此时采用默认是 sentryConsts.ServiceAccountTokenAudience "dapr.io/sentry"
		audiences = []string{sentryConsts.ServiceAccountTokenAudience}

		// TODO: Remove in Dapr 1.12 to enforce setting an explicit audience
		// Because the user did not specify an explicit audience and is instead relying on the default, if the authentication fails we can retry with nil audience
    // 并记录下来这是特殊情况,如果认证失败则应该尝试 audience 为 nil 的情况
		canTryWithNilAudience = true
	}
	tokenReview := &kauthapi.TokenReview{
		Spec: kauthapi.TokenReviewSpec{
			Token:     token,
			Audiences: audiences,
		},
	}

tr: // TODO: Remove in Dapr 1.12 to enforce setting an explicit audience

	prts, err := v.executeTokenReview(tokenReview)
	if err != nil {
		// TODO: Remove in Dapr 1.12 to enforce setting an explicit audience
		if canTryWithNilAudience {
			// Retry with a nil audience, which means the default audience for the K8s API server
			tokenReview.Spec.Audiences = nil
			showDefaultTokenAudienceWarning = true
			canTryWithNilAudience = false
			goto tr
		}

		return err
	}

	// TODO: Remove in Dapr 1.12 to enforce setting an explicit audience
	if showDefaultTokenAudienceWarning {
		log.Warn("WARNING: Sentry accepted a token with the audience for the Kubernetes API server. This is deprecated and only supported to ensure a smooth upgrade from Dapr pre-1.10.")
	}

	if len(prts) != 4 || prts[0] != "system" {
		return fmt.Errorf("%s: provided token is not a properly structured service account token", errPrefix)
	}

	podSa := prts[3]
	podNs := prts[2]

  // 检验 namespace
	if namespace != "" {
		if podNs != namespace {
			return fmt.Errorf("%s: namespace mismatch. received namespace: %s", errPrefix, namespace)
		}
	}

  // 检验 id
	if id != podNs+":"+podSa {
		return fmt.Errorf("%s: token/id mismatch. received id: %s", errPrefix, id)
	}
	return nil
}

executeTokenReview() 方法执行 tokenReview,如果 token 无效或者失败则返回错误:

func (v *validator) executeTokenReview(tokenReview *kauthapi.TokenReview) ([]string, error) {
	review, err := v.auth.TokenReviews().Create(context.TODO(), tokenReview, v1.CreateOptions{})
	if err != nil {
		return nil, fmt.Errorf("%s: token review failed: %w", errPrefix, err)
	}
	if review.Status.Error != "" {
		return nil, fmt.Errorf("%s: invalid token: %s", errPrefix, review.Status.Error)
	}
	if !review.Status.Authenticated {
		return nil, fmt.Errorf("%s: authentication failed", errPrefix)
	}
	return strings.Split(review.Status.User.Username, ":"), nil
}

5 - selfhosted下的validator.go

selfhosted下的validator实现

selfhosted 下实际没有做验证:

func NewValidator() identity.Validator {
	return &validator{}
}

type validator struct{}

func (v *validator) Validate(id, token, namespace string) error {
	// no validation for self hosted.
	return nil
}

只是保留了一套代码框架,以满足 Validator 接口的要求。

这意味着在 selfhosted 下是不会进行身份验证的。