Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v12] Backport #22705 #23191

Merged
merged 1 commit into from
Mar 17, 2023
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
43 changes: 43 additions & 0 deletions api/proto/teleport/legacy/types/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1138,6 +1138,8 @@ message ProvisionTokenSpecV2 {
ProvisionTokenSpecV2Kubernetes Kubernetes = 10 [(gogoproto.jsontag) = "kubernetes,omitempty"];
// Azure allows the configuration of options specific to the "azure" join method.
ProvisionTokenSpecV2Azure Azure = 11 [(gogoproto.jsontag) = "azure,omitempty"];
// GitLab allows the configuration of options specific to the "gitlab" join method.
ProvisionTokenSpecV2GitLab GitLab = 12 [(gogoproto.jsontag) = "gitlab,omitempty"];
}

// ProvisionTokenSpecV2Github contains the GitHub-specific part of the
Expand Down Expand Up @@ -1182,6 +1184,47 @@ message ProvisionTokenSpecV2GitHub {
string EnterpriseServerHost = 2 [(gogoproto.jsontag) = "enterprise_server_host,omitempty"];
}

// ProvisionTokenSpecV2GitLab contains the GitLab-specific part of the
// ProvisionTokenSpecV2
message ProvisionTokenSpecV2GitLab {
message Rule {
// Sub roughly uniquely identifies the workload. Example:
// `project_path:mygroup/my-project:ref_type:branch:ref:main`
// project_path:{group}/{project}:ref_type:{type}:ref:{branch_name}
string Sub = 1 [(gogoproto.jsontag) = "sub,omitempty"];
// Ref allows access to be limited to jobs triggered by a specific git ref.
// Ensure this is used in combination with ref_type.
string Ref = 2 [(gogoproto.jsontag) = "ref,omitempty"];
// RefType allows access to be limited to jobs triggered by a specific git
// ref type. Example:
// `branch` or `tag`
string RefType = 3 [(gogoproto.jsontag) = "ref_type,omitempty"];
// NamespacePath is used to limit access to jobs in a group or user's
// projects.
// Example:
// `mygroup`
string NamespacePath = 4 [(gogoproto.jsontag) = "namespace_path,omitempty"];
// ProjectPath is used to limit access to jobs belonging to an individual
// project. Example:
// `mygroup/myproject`
string ProjectPath = 5 [(gogoproto.jsontag) = "project_path,omitempty"];
// PipelineSource limits access by the job pipeline source type.
// https://docs.gitlab.com/ee/ci/jobs/job_control.html#common-if-clauses-for-rules
// Example: `web`
string PipelineSource = 6 [(gogoproto.jsontag) = "pipeline_source,omitempty"];
// Environment limits access by the environment the job deploys to
// (if one is associated)
string Environment = 7 [(gogoproto.jsontag) = "environment,omitempty"];
}
// Allow is a list of TokenRules, nodes using this token must match one
// allow rule to use this token.
repeated Rule Allow = 1 [(gogoproto.jsontag) = "allow,omitempty"];
// Domain is the domain of your GitLab instance. This will default to
// `gitlab.com` - but can be set to the domain of your self-hosted GitLab
// e.g `gitlab.example.com`.
string Domain = 2 [(gogoproto.jsontag) = "domain,omitempty"];
}

// ProvisionTokenSpecV2CircleCI contains the CircleCI-specific part of the
// ProvisionTokenSpecV2
message ProvisionTokenSpecV2CircleCI {
Expand Down
46 changes: 46 additions & 0 deletions api/types/provisioning.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ const (
// JoinMethodAzure indicates that the node will join with the Azure join
// method.
JoinMethodAzure JoinMethod = "azure"
// JoinMethodGitLab indicates that the node will join with the GitLab
// join method. Documentation regarding implementation of this
// can be found in lib/gitlab
JoinMethodGitLab JoinMethod = "gitlab"
)

var JoinMethods = []JoinMethod{
Expand All @@ -65,6 +69,7 @@ var JoinMethods = []JoinMethod{
JoinMethodCircleCI,
JoinMethodKubernetes,
JoinMethodAzure,
JoinMethodGitLab,
}

func ValidateJoinMethod(method JoinMethod) error {
Expand Down Expand Up @@ -267,6 +272,17 @@ func (p *ProvisionTokenV2) CheckAndSetDefaults() error {
if err := providerCfg.checkAndSetDefaults(); err != nil {
return trace.Wrap(err)
}
case JoinMethodGitLab:
providerCfg := p.Spec.GitLab
if providerCfg == nil {
return trace.BadParameter(
`"gitlab" configuration must be provided for the join method %q`,
JoinMethodGitLab,
)
}
if err := providerCfg.checkAndSetDefaults(); err != nil {
return trace.Wrap(err)
}
default:
return trace.BadParameter("unknown join method %q", p.Spec.JoinMethod)
}
Expand Down Expand Up @@ -567,3 +583,33 @@ func (a *ProvisionTokenSpecV2Azure) checkAndSetDefaults() error {
}
return nil
}

const defaultGitLabDomain = "gitlab.com"

func (a *ProvisionTokenSpecV2GitLab) checkAndSetDefaults() error {
if len(a.Allow) == 0 {
return trace.BadParameter(
"the %q join method requires defined gitlab allow rules",
JoinMethodGitLab,
)
}
for _, allowRule := range a.Allow {
if allowRule.Sub == "" && allowRule.NamespacePath == "" && allowRule.ProjectPath == "" {
return trace.BadParameter(
"the %q join method requires allow rules with at least 'sub', 'project_path' or 'namespace_path' to ensure security.",
JoinMethodGitLab,
)
}
}

if a.Domain == "" {
a.Domain = defaultGitLabDomain
} else {
if strings.Contains(a.Domain, "/") {
return trace.BadParameter(
"'spec.gitlab.domain' should not contain the scheme or path",
)
}
}
return nil
}
152 changes: 151 additions & 1 deletion api/types/provisioning_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (

"github.com/gravitational/trace"
"github.com/stretchr/testify/require"

"github.com/gravitational/teleport/api/defaults"
)

func TestProvisionTokenV2_CheckAndSetDefaults(t *testing.T) {
Expand Down Expand Up @@ -448,6 +450,154 @@ func TestProvisionTokenV2_CheckAndSetDefaults(t *testing.T) {
},
expectedErr: &trace.BadParameterError{},
},
{
desc: "gitlab empty allow rules",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: JoinMethodGitLab,
GitLab: &ProvisionTokenSpecV2GitLab{
Allow: []*ProvisionTokenSpecV2GitLab_Rule{},
},
},
},
expectedErr: &trace.BadParameterError{},
},
{
desc: "gitlab missing config",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: JoinMethodGitLab,
GitLab: nil,
},
},
expectedErr: &trace.BadParameterError{},
},
{
desc: "gitlab empty allow rule",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: JoinMethodGitLab,
GitLab: &ProvisionTokenSpecV2GitLab{
Allow: []*ProvisionTokenSpecV2GitLab_Rule{
{},
},
},
},
},
expectedErr: &trace.BadParameterError{},
},
{
desc: "gitlab defaults",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: JoinMethodGitLab,
GitLab: &ProvisionTokenSpecV2GitLab{
Allow: []*ProvisionTokenSpecV2GitLab_Rule{
{
Sub: "asub",
},
},
},
},
},
expected: &ProvisionTokenV2{
Kind: KindToken,
Version: V2,
Metadata: Metadata{
Name: "test",
Namespace: defaults.Namespace,
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: JoinMethodGitLab,
GitLab: &ProvisionTokenSpecV2GitLab{
Allow: []*ProvisionTokenSpecV2GitLab_Rule{
{
Sub: "asub",
},
},
Domain: defaultGitLabDomain,
},
},
},
},
{
desc: "overridden domain",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: JoinMethodGitLab,
GitLab: &ProvisionTokenSpecV2GitLab{
Allow: []*ProvisionTokenSpecV2GitLab_Rule{
{
Sub: "asub",
},
},
Domain: "gitlab.example.com",
},
},
},
expected: &ProvisionTokenV2{
Kind: KindToken,
Version: V2,
Metadata: Metadata{
Name: "test",
Namespace: defaults.Namespace,
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: JoinMethodGitLab,
GitLab: &ProvisionTokenSpecV2GitLab{
Allow: []*ProvisionTokenSpecV2GitLab_Rule{
{
Sub: "asub",
},
},
Domain: "gitlab.example.com",
},
},
},
},
{
desc: "invalid overridden domain",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: JoinMethodGitLab,
GitLab: &ProvisionTokenSpecV2GitLab{
Allow: []*ProvisionTokenSpecV2GitLab_Rule{
{
Sub: "asub",
},
},
Domain: "http://gitlab.example.com",
},
},
},
expectedErr: &trace.BadParameterError{},
},
}

for _, tc := range testcases {
Expand All @@ -459,7 +609,7 @@ func TestProvisionTokenV2_CheckAndSetDefaults(t *testing.T) {
}
require.NoError(t, err)
if tc.expected != nil {
require.Equal(t, tc.token, tc.expected)
require.Equal(t, tc.expected, tc.token)
}
})
}
Expand Down
Loading