Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ import (
dtconfig "github.com/gravitational/teleport/lib/devicetrust/config"
"github.com/gravitational/teleport/lib/events"
"github.com/gravitational/teleport/lib/gcp"
"github.com/gravitational/teleport/lib/gitlab"
"github.com/gravitational/teleport/lib/integrations/awsra/createsession"
"github.com/gravitational/teleport/lib/inventory"
iterstream "github.com/gravitational/teleport/lib/itertools/stream"
Expand All @@ -123,6 +122,7 @@ import (
"github.com/gravitational/teleport/lib/join/ec2join"
"github.com/gravitational/teleport/lib/join/env0"
"github.com/gravitational/teleport/lib/join/githubactions"
"github.com/gravitational/teleport/lib/join/gitlab"
"github.com/gravitational/teleport/lib/join/tpmjoin"
kubetoken "github.com/gravitational/teleport/lib/kube/token"
"github.com/gravitational/teleport/lib/limiter"
Expand Down Expand Up @@ -1285,7 +1285,7 @@ type Server struct {

// gitlabIDTokenValidator allows ID tokens from GitLab CI to be validated by
// the auth server. It can be overridden for the purpose of tests.
gitlabIDTokenValidator gitlabIDTokenValidator
gitlabIDTokenValidator gitlab.Validator

// azureDevopsIDTokenValidator allows ID tokens from Azure DevOps to be
// validated by the auth server. It can be overridden for the purpose of
Expand Down
8 changes: 0 additions & 8 deletions lib/auth/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,6 @@ func (a *Server) SetGCPIDTokenValidator(validator gcpIDTokenValidator) {
a.gcpIDTokenValidator = validator
}

func (a *Server) SetGitlabIDTokenValidator(validator gitlabIDTokenValidator) {
a.gitlabIDTokenValidator = validator
}

func (a *Server) SetK8sTokenReviewValidator(validator k8sTokenReviewValidator) {
a.k8sTokenReviewValidator = validator
}
Expand Down Expand Up @@ -368,10 +364,6 @@ func IsGCPZoneInLocation(rawLocation, rawZone string) bool {
return isGCPZoneInLocation(rawLocation, rawZone)
}

func JoinRuleGlobMatch(want string, got string) (bool, error) {
return joinRuleGlobMatch(want, got)
}

func FormatHeaderFromMap(m map[string]string) http.Header {
return formatHeaderFromMap(m)
}
Expand Down
14 changes: 8 additions & 6 deletions lib/auth/join/join.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ import (
"github.com/gravitational/teleport/lib/cloud/imds/gcp"
"github.com/gravitational/teleport/lib/cryptosuites"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/gitlab"
"github.com/gravitational/teleport/lib/join/githubactions"
"github.com/gravitational/teleport/lib/join/gitlab"
"github.com/gravitational/teleport/lib/jwt"
kubetoken "github.com/gravitational/teleport/lib/kube/token"
"github.com/gravitational/teleport/lib/spacelift"
Expand Down Expand Up @@ -333,11 +333,13 @@ func Register(ctx context.Context, params RegisterParams) (result *RegisterResul
}
}
case types.JoinMethodGitLab:
params.IDToken, err = gitlab.NewIDTokenSource(gitlab.IDTokenSourceConfig{
EnvVarName: params.GitlabParams.EnvVarName,
}).GetIDToken()
if err != nil {
return nil, trace.Wrap(err)
if params.IDToken == "" {
params.IDToken, err = gitlab.NewIDTokenSource(gitlab.IDTokenSourceConfig{
EnvVarName: params.GitlabParams.EnvVarName,
}).GetIDToken()
if err != nil {
return nil, trace.Wrap(err)
}
}
case types.JoinMethodCircleCI:
params.IDToken, err = circleci.GetIDToken(os.Getenv)
Expand Down
159 changes: 20 additions & 139 deletions lib/auth/join_gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,152 +24,33 @@ import (
"github.com/gravitational/trace"

"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/gitlab"
"github.com/gravitational/teleport/lib/join/joinutils"
"github.com/gravitational/teleport/lib/join/gitlab"
)

type gitlabIDTokenValidator interface {
Validate(
ctx context.Context, domain string, token string,
) (*gitlab.IDTokenClaims, error)
ValidateTokenWithJWKS(
ctx context.Context, jwks []byte, token string,
) (*gitlab.IDTokenClaims, error)
// GetGitlabIDTokenValidator returns the currently configured gitlab OIDC token
// validator.
func (a *Server) GetGitlabIDTokenValidator() gitlab.Validator {
return a.gitlabIDTokenValidator
}

// SetGitlabIDTokenValidator sets the validator implementation used to verify
// GitLab OIDC tokens. Used in tests to provide mock implementations.
func (a *Server) SetGitlabIDTokenValidator(validator gitlab.Validator) {
a.gitlabIDTokenValidator = validator
}

func (a *Server) checkGitLabJoinRequest(
ctx context.Context,
req *types.RegisterUsingTokenRequest,
pt types.ProvisionToken,
) (*gitlab.IDTokenClaims, error) {
if req.IDToken == "" {
return nil, trace.BadParameter("IDToken not provided for gitlab join request")
}
token, ok := pt.(*types.ProvisionTokenV2)
if !ok {
return nil, trace.BadParameter("gitlab join method only supports ProvisionTokenV2, '%T' was provided", pt)
}

var claims *gitlab.IDTokenClaims
var err error
if token.Spec.GitLab.StaticJWKS != "" {
claims, err = a.gitlabIDTokenValidator.ValidateTokenWithJWKS(
ctx, []byte(token.Spec.GitLab.StaticJWKS), req.IDToken,
)
if err != nil {
return nil, trace.Wrap(err, "validating with static jwks")
}
} else {
claims, err = a.gitlabIDTokenValidator.Validate(
ctx, token.Spec.GitLab.Domain, req.IDToken,
)
if err != nil {
return nil, trace.Wrap(err, "validating with oidc")
}
}

a.logger.InfoContext(ctx, "GitLab CI run trying to join cluster",
"claims", claims,
"token", pt.GetName(),
)

return claims, trace.Wrap(checkGitLabAllowRules(token, claims))
}

// joinRuleGlobMatch is used when comparing some rule fields from a
// ProvisionToken against a claim from a token. It allows simple pattern
// matching:
// - '*' matches zero or more characters.
// - '?' matches any single character.
// It returns true if a match is detected.
func joinRuleGlobMatch(want string, got string) (bool, error) {
if want == "" {
return true, nil
}
return joinutils.GlobMatch(want, got)
}

func checkGitLabAllowRules(token *types.ProvisionTokenV2, claims *gitlab.IDTokenClaims) error {
// Helper for comparing a BoolOption with GitLabs string bool.
// Returns true if OK - returns false if not OK
boolEqual := func(want *types.BoolOption, got string) bool {
if want == nil {
return true
}
return (want.Value && got == "true") || (!want.Value && got == "false")
}

// If a single rule passes, accept the IDToken
for i, rule := range token.Spec.GitLab.Allow {
// Please consider keeping these field validators in the same order they
// are defined within the ProvisionTokenSpecV2GitLab proto spec.
subMatches, err := joinRuleGlobMatch(rule.Sub, claims.Sub)
if err != nil {
return trace.Wrap(err, "evaluating rule (%d) sub match", i)
}
if !subMatches {
continue
}
refMatches, err := joinRuleGlobMatch(rule.Ref, claims.Ref)
if err != nil {
return trace.Wrap(err, "evaluating rule (%d) ref match", i)
}
if !refMatches {
continue
}
if rule.RefType != "" && claims.RefType != rule.RefType {
continue
}
namespacePathMatches, err := joinRuleGlobMatch(rule.NamespacePath, claims.NamespacePath)
if err != nil {
return trace.Wrap(err, "evaluating rule (%d) namespace_path match", i)
}
if !namespacePathMatches {
continue
}
projectPathMatches, err := joinRuleGlobMatch(rule.ProjectPath, claims.ProjectPath)
if err != nil {
return trace.Wrap(err, "evaluating rule (%d) project_path match", i)
}
if !projectPathMatches {
continue
}
if rule.PipelineSource != "" && claims.PipelineSource != rule.PipelineSource {
continue
}
if rule.Environment != "" && claims.Environment != rule.Environment {
continue
}
if rule.UserLogin != "" && claims.UserLogin != rule.UserLogin {
continue
}
if rule.UserID != "" && claims.UserID != rule.UserID {
continue
}
if rule.UserEmail != "" && claims.UserEmail != rule.UserEmail {
continue
}
if !boolEqual(rule.RefProtected, claims.RefProtected) {
continue
}
if !boolEqual(rule.EnvironmentProtected, claims.EnvironmentProtected) {
continue
}
if rule.CIConfigSHA != "" && claims.CIConfigSHA != rule.CIConfigSHA {
continue
}
if rule.CIConfigRefURI != "" && claims.CIConfigRefURI != rule.CIConfigRefURI {
continue
}
if rule.DeploymentTier != "" && claims.DeploymentTier != rule.DeploymentTier {
continue
}
if rule.ProjectVisibility != "" && claims.ProjectVisibility != rule.ProjectVisibility {
continue
}
// All provided rules met.
return nil
}

return trace.AccessDenied("id token claims did not match any allow rules")
claims, err := gitlab.CheckIDToken(ctx, &gitlab.CheckIDTokenParams{
ProvisionToken: pt,
IDToken: []byte(req.IDToken),
Validator: a.gitlabIDTokenValidator,
})

// Where possible, we try to return any extracted claims along with the
// error to provide better audit logs of failed join attempts.
return claims, trace.Wrap(err)
}
3 changes: 2 additions & 1 deletion lib/auth/join_spacelift.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/gravitational/trace"

"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/join/joinutils"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/spacelift"
)
Expand Down Expand Up @@ -73,7 +74,7 @@ func (a *Server) checkSpaceliftJoinRequest(
func checkSpaceliftAllowRules(token *types.ProvisionTokenV2, claims *spacelift.IDTokenClaims) error {
globCheck := func(want string, got string) (bool, error) {
if token.Spec.Spacelift.EnableGlobMatching {
return joinRuleGlobMatch(want, got)
return joinutils.GlobMatchAllowEmptyPattern(want, got)
}
if want == "" {
return true, nil
Expand Down
Loading
Loading