diff --git a/api/types/provisioning.go b/api/types/provisioning.go index 9efe139fc5a3f..3e98b5125d208 100644 --- a/api/types/provisioning.go +++ b/api/types/provisioning.go @@ -87,7 +87,7 @@ func ValidateJoinMethod(method JoinMethod) error { // ProvisionToken is a provisioning token type ProvisionToken interface { - Resource + ResourceWithOrigin // SetMetadata sets resource metatada SetMetadata(meta Metadata) // GetRoles returns a list of teleport roles @@ -377,6 +377,16 @@ func (p *ProvisionTokenV2) SetMetadata(meta Metadata) { p.Metadata = meta } +// Origin returns the origin value of the resource. +func (p *ProvisionTokenV2) Origin() string { + return p.Metadata.Origin() +} + +// SetOrigin sets the origin value of the resource. +func (p *ProvisionTokenV2) SetOrigin(origin string) { + p.Metadata.SetOrigin(origin) +} + // GetSuggestedLabels returns the labels the resource should set when using this token func (p *ProvisionTokenV2) GetSuggestedLabels() Labels { return p.Spec.SuggestedLabels diff --git a/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_githubconnectors.yaml b/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_githubconnectors.yaml index 0b999f6ac834f..40bbc0801a25d 100644 --- a/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_githubconnectors.yaml +++ b/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_githubconnectors.yaml @@ -35,27 +35,33 @@ spec: spec: description: GithubConnector resource definition v3 from Teleport properties: + api_endpoint_url: + description: APIEndpointURL is the URL of the API endpoint of the + Github instance this connector is for. + type: string client_id: - description: ClientID is the GitHub OAuth app client ID. + description: ClientID is the Github OAuth app client ID. type: string client_secret: - description: ClientSecret is the GitHub OAuth app client secret. + description: ClientSecret is the Github OAuth app client secret. type: string display: description: Display is the connector display name. type: string endpoint_url: + description: EndpointURL is the URL of the GitHub instance this connector + is for. type: string redirect_url: description: RedirectURL is the authorization callback URL. type: string teams_to_roles: - description: TeamsToRoles maps GitHub team memberships onto allowed + description: TeamsToRoles maps Github team memberships onto allowed roles. items: properties: organization: - description: Organization is a GitHub organization a user belongs + description: Organization is a Github organization a user belongs to. type: string roles: diff --git a/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_provisiontokens.yaml b/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_provisiontokens.yaml new file mode 100644 index 0000000000000..c35e7099b71a5 --- /dev/null +++ b/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_provisiontokens.yaml @@ -0,0 +1,427 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + creationTimestamp: null + name: teleportprovisiontokens.resources.teleport.dev +spec: + group: resources.teleport.dev + names: + kind: TeleportProvisionToken + listKind: TeleportProvisionTokenList + plural: teleportprovisiontokens + shortNames: + - provisiontoken + - provisiontokens + singular: teleportprovisiontoken + scope: Namespaced + versions: + - name: v2 + schema: + openAPIV3Schema: + description: ProvisionToken is the Schema for the provisiontokens API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ProvisionToken resource definition v2 from Teleport + properties: + allow: + description: Allow is a list of TokenRules, nodes using this token + must match one allow rule to use this token. + items: + properties: + aws_account: + description: AWSAccount is the AWS account ID. + type: string + aws_arn: + description: AWSARN is used for the IAM join method, the AWS + identity of joining nodes must match this ARN. Supports wildcards + "*" and "?". + type: string + aws_regions: + description: AWSRegions is used for the EC2 join method and + is a list of AWS regions a node is allowed to join from. + items: + type: string + nullable: true + type: array + aws_role: + description: AWSRole is used for the EC2 join method and is + the the ARN of the AWS role that the auth server will assume + in order to call the ec2 API. + type: string + type: object + nullable: true + type: array + aws_iid_ttl: + description: AWSIIDTTL is the TTL to use for AWS EC2 Instance Identity + Documents used to join the cluster with this token. + format: duration + type: string + azure: + description: Azure allows the configuration of options specific to + the "azure" join method. + nullable: true + properties: + allow: + description: Allow is a list of Rules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + type: object + bot_name: + description: BotName is the name of the bot this token grants access + to, if any + type: string + circleci: + description: CircleCI allows the configuration of options specific + to the "circleci" join method. + nullable: true + properties: + allow: + description: Allow is a list of TokenRules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + organization_id: + type: string + type: object + gcp: + description: GCP allows the configuration of options specific to the + "gcp" join method. + nullable: true + properties: + allow: + description: Allow is a list of Rules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + type: object + github: + description: GitHub allows the configuration of options specific to + the "github" join method. + nullable: true + properties: + allow: + description: Allow is a list of TokenRules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + enterprise_server_host: + description: EnterpriseServerHost allows joining from runners + associated with a GitHub Enterprise Server instance. When unconfigured, + tokens will be validated against github.com, but when configured + to the host of a GHES instance, then the tokens will be validated + against host. This value should be the hostname of the GHES + instance, and should not include the scheme or a path. The instance + must be accessible over HTTPS at this hostname and the certificate + must be trusted by the Auth Server. + type: string + type: object + gitlab: + description: GitLab allows the configuration of options specific to + the "gitlab" join method. + nullable: true + properties: + allow: + description: Allow is a list of TokenRules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + domain: + description: 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`. + type: string + type: object + join_method: + description: JoinMethod is the joining method required in order to + use this token. Supported joining methods include "token", "ec2", + and "iam". + type: string + kubernetes: + description: Kubernetes allows the configuration of options specific + to the "kubernetes" join method. + nullable: true + properties: + allow: + description: Allow is a list of Rules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + type: object + roles: + description: Roles is a list of roles associated with the token, that + will be converted to metadata in the SSH and X509 certificates issued + to the user of the token + items: + type: string + nullable: true + type: array + suggested_agent_matcher_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: SuggestedAgentMatcherLabels is a set of labels to be + used by agents to match on resources. When an agent uses this token, + the agent should monitor resources that match those labels. For + databases, this means adding the labels to `db_service.resources.labels`. + Currently, only node-join scripts create a configuration according + to the suggestion. + type: object + suggested_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: SuggestedLabels is a set of labels that resources should + set when using this token to enroll themselves in the cluster. Currently, + only node-join scripts create a configuration according to the suggestion. + type: object + type: object + status: + description: TeleportProvisionTokenStatus defines the observed state of + TeleportProvisionToken + properties: + conditions: + description: Conditions represent the latest available observations + of an object's state + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + teleportResourceID: + format: int64 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_roles.yaml b/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_roles.yaml index 0b0933b23212b..3c62a33cf1ca2 100644 --- a/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_roles.yaml +++ b/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_roles.yaml @@ -74,6 +74,13 @@ spec: type: string nullable: true type: array + db_roles: + description: DatabaseRoles is a list of databases roles for automatic + user creation. + items: + type: string + nullable: true + type: array db_service_labels: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -81,12 +88,19 @@ spec: allow/deny access to Database Services. type: object db_users: - description: DatabaseUsers is a list of databaes users this role + description: DatabaseUsers is a list of databases users this role is allowed to connect as. items: type: string nullable: true type: array + desktop_groups: + description: DesktopGroups is a list of groups for created desktop + users to be added to + items: + type: string + nullable: true + type: array gcp_service_accounts: description: GCPServiceAccounts is a list of GCP service accounts this role is allowed to assume. @@ -94,6 +108,12 @@ spec: type: string nullable: true type: array + group_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: GroupLabels is a map of labels used as part of the + RBAC system. + type: object host_groups: description: HostGroups is a list of groups for created users to be added to @@ -470,6 +490,13 @@ spec: type: string nullable: true type: array + db_roles: + description: DatabaseRoles is a list of databases roles for automatic + user creation. + items: + type: string + nullable: true + type: array db_service_labels: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -477,12 +504,19 @@ spec: allow/deny access to Database Services. type: object db_users: - description: DatabaseUsers is a list of databaes users this role + description: DatabaseUsers is a list of databases users this role is allowed to connect as. items: type: string nullable: true type: array + desktop_groups: + description: DesktopGroups is a list of groups for created desktop + users to be added to + items: + type: string + nullable: true + type: array gcp_service_accounts: description: GCPServiceAccounts is a list of GCP service accounts this role is allowed to assume. @@ -490,6 +524,12 @@ spec: type: string nullable: true type: array + group_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: GroupLabels is a map of labels used as part of the + RBAC system. + type: object host_groups: description: HostGroups is a list of groups for created users to be added to @@ -845,7 +885,7 @@ spec: format: int32 type: integer value: - description: Value specifies the valueg to be used in the + description: Value specifies the value to be used in the cert extension. type: string type: object @@ -861,6 +901,14 @@ spec: is set to the idle duration. format: duration type: string + create_db_user: + description: CreateDatabaseUser enabled automatic database user + creation. + type: boolean + create_desktop_user: + description: CreateDesktopUser allows users to be automatically + created on a Windows desktop + type: boolean create_host_user: description: CreateHostUser allows users to be automatically created on a host @@ -965,7 +1013,7 @@ spec: type: string type: object request_access: - description: RequestAccess defines the access request stategy + description: RequestAccess defines the access request strategy (optional|note|always) where optional is the default. type: string request_prompt: @@ -1128,6 +1176,13 @@ spec: type: string nullable: true type: array + db_roles: + description: DatabaseRoles is a list of databases roles for automatic + user creation. + items: + type: string + nullable: true + type: array db_service_labels: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -1135,12 +1190,19 @@ spec: allow/deny access to Database Services. type: object db_users: - description: DatabaseUsers is a list of databaes users this role + description: DatabaseUsers is a list of databases users this role is allowed to connect as. items: type: string nullable: true type: array + desktop_groups: + description: DesktopGroups is a list of groups for created desktop + users to be added to + items: + type: string + nullable: true + type: array gcp_service_accounts: description: GCPServiceAccounts is a list of GCP service accounts this role is allowed to assume. @@ -1148,6 +1210,12 @@ spec: type: string nullable: true type: array + group_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: GroupLabels is a map of labels used as part of the + RBAC system. + type: object host_groups: description: HostGroups is a list of groups for created users to be added to @@ -1524,6 +1592,13 @@ spec: type: string nullable: true type: array + db_roles: + description: DatabaseRoles is a list of databases roles for automatic + user creation. + items: + type: string + nullable: true + type: array db_service_labels: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -1531,12 +1606,19 @@ spec: allow/deny access to Database Services. type: object db_users: - description: DatabaseUsers is a list of databaes users this role + description: DatabaseUsers is a list of databases users this role is allowed to connect as. items: type: string nullable: true type: array + desktop_groups: + description: DesktopGroups is a list of groups for created desktop + users to be added to + items: + type: string + nullable: true + type: array gcp_service_accounts: description: GCPServiceAccounts is a list of GCP service accounts this role is allowed to assume. @@ -1544,6 +1626,12 @@ spec: type: string nullable: true type: array + group_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: GroupLabels is a map of labels used as part of the + RBAC system. + type: object host_groups: description: HostGroups is a list of groups for created users to be added to @@ -1899,7 +1987,7 @@ spec: format: int32 type: integer value: - description: Value specifies the valueg to be used in the + description: Value specifies the value to be used in the cert extension. type: string type: object @@ -1915,6 +2003,14 @@ spec: is set to the idle duration. format: duration type: string + create_db_user: + description: CreateDatabaseUser enabled automatic database user + creation. + type: boolean + create_desktop_user: + description: CreateDesktopUser allows users to be automatically + created on a Windows desktop + type: boolean create_host_user: description: CreateHostUser allows users to be automatically created on a host @@ -2019,7 +2115,7 @@ spec: type: string type: object request_access: - description: RequestAccess defines the access request stategy + description: RequestAccess defines the access request strategy (optional|note|always) where optional is the default. type: string request_prompt: diff --git a/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_users.yaml b/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_users.yaml index 3a4464bc20626..cf54b972693a2 100644 --- a/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_users.yaml +++ b/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_users.yaml @@ -36,7 +36,7 @@ spec: description: User resource definition v2 from Teleport properties: github_identities: - description: GithubIdentities list associated GitHub OAuth2 identities + description: GithubIdentities list associated Github OAuth2 identities that let user log in using externally verified identity items: properties: diff --git a/examples/chart/teleport-cluster/templates/auth/clusterrole.yaml b/examples/chart/teleport-cluster/templates/auth/clusterrole.yaml index 221a49db1d8d2..ad6ffdcd18068 100644 --- a/examples/chart/teleport-cluster/templates/auth/clusterrole.yaml +++ b/examples/chart/teleport-cluster/templates/auth/clusterrole.yaml @@ -41,6 +41,8 @@ rules: - teleportsamlconnectors/status - teleportloginrules - teleportloginrules/status + - teleportprovisiontokens + - teleportprovisiontokens/status verbs: - get - list diff --git a/examples/chart/teleport-cluster/tests/__snapshot__/auth_clusterrole_test.yaml.snap b/examples/chart/teleport-cluster/tests/__snapshot__/auth_clusterrole_test.yaml.snap index dc4fd1c4f2ee5..d6debffac98fc 100644 --- a/examples/chart/teleport-cluster/tests/__snapshot__/auth_clusterrole_test.yaml.snap +++ b/examples/chart/teleport-cluster/tests/__snapshot__/auth_clusterrole_test.yaml.snap @@ -40,6 +40,8 @@ adds operator permissions to ClusterRole: - teleportsamlconnectors/status - teleportloginrules - teleportloginrules/status + - teleportprovisiontokens + - teleportprovisiontokens/status verbs: - get - list diff --git a/integrations/operator/apis/resources/v2/provisiontoken_types.go b/integrations/operator/apis/resources/v2/provisiontoken_types.go new file mode 100644 index 0000000000000..5e81711309dbf --- /dev/null +++ b/integrations/operator/apis/resources/v2/provisiontoken_types.go @@ -0,0 +1,98 @@ +/* +Copyright 2022 Gravitational, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/integrations/operator/apis/resources" +) + +func init() { + SchemeBuilder.Register(&TeleportProvisionToken{}, &TeleportProvisionTokenList{}) +} + +// TeleportProvisionTokenSpec defines the desired state of TeleportProvisionToken +type TeleportProvisionTokenSpec types.ProvisionTokenSpecV2 + +// TeleportProvisionTokenStatus defines the observed state of TeleportProvisionToken +type TeleportProvisionTokenStatus struct { + // Conditions represent the latest available observations of an object's state + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` + // +optional + TeleportResourceID int64 `json:"teleportResourceID,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// TeleportProvisionToken is the Schema for the ProvisionToken API +type TeleportProvisionToken struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec TeleportProvisionTokenSpec `json:"spec,omitempty"` + Status TeleportProvisionTokenStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// TeleportProvisionTokenList contains a list of TeleportProvisionToken +type TeleportProvisionTokenList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []TeleportProvisionToken `json:"items"` +} + +func (c TeleportProvisionToken) ToTeleport() types.ProvisionToken { + return &types.ProvisionTokenV2{ + Kind: types.KindToken, + Version: types.V2, + Metadata: types.Metadata{ + Name: c.Name, + Labels: c.Labels, + Description: c.Annotations[resources.DescriptionKey], + }, + Spec: types.ProvisionTokenSpecV2(c.Spec), + } +} + +func (c *TeleportProvisionToken) StatusConditions() *[]metav1.Condition { + return &c.Status.Conditions +} + +// Marshal serializes a spec into binary data. +func (spec *TeleportProvisionTokenSpec) Marshal() ([]byte, error) { + return (*types.ProvisionTokenSpecV2)(spec).Marshal() +} + +// Unmarshal deserializes a spec from binary data. +func (spec *TeleportProvisionTokenSpec) Unmarshal(data []byte) error { + return (*types.ProvisionTokenSpecV2)(spec).Unmarshal(data) +} + +// DeepCopyInto deep-copies one user spec into another. +// Required to satisfy runtime.Object interface. +func (spec *TeleportProvisionTokenSpec) DeepCopyInto(out *TeleportProvisionTokenSpec) { + data, err := spec.Marshal() + if err != nil { + panic(err) + } + *out = TeleportProvisionTokenSpec{} + if err = out.Unmarshal(data); err != nil { + panic(err) + } +} diff --git a/integrations/operator/apis/resources/v2/zz_generated.deepcopy.go b/integrations/operator/apis/resources/v2/zz_generated.deepcopy.go index b779afcdefd13..168a8e20d05bd 100644 --- a/integrations/operator/apis/resources/v2/zz_generated.deepcopy.go +++ b/integrations/operator/apis/resources/v2/zz_generated.deepcopy.go @@ -26,6 +26,97 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TeleportProvisionToken) DeepCopyInto(out *TeleportProvisionToken) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TeleportProvisionToken. +func (in *TeleportProvisionToken) DeepCopy() *TeleportProvisionToken { + if in == nil { + return nil + } + out := new(TeleportProvisionToken) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TeleportProvisionToken) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TeleportProvisionTokenList) DeepCopyInto(out *TeleportProvisionTokenList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]TeleportProvisionToken, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TeleportProvisionTokenList. +func (in *TeleportProvisionTokenList) DeepCopy() *TeleportProvisionTokenList { + if in == nil { + return nil + } + out := new(TeleportProvisionTokenList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TeleportProvisionTokenList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TeleportProvisionTokenSpec. +func (in *TeleportProvisionTokenSpec) DeepCopy() *TeleportProvisionTokenSpec { + if in == nil { + return nil + } + out := new(TeleportProvisionTokenSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TeleportProvisionTokenStatus) DeepCopyInto(out *TeleportProvisionTokenStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TeleportProvisionTokenStatus. +func (in *TeleportProvisionTokenStatus) DeepCopy() *TeleportProvisionTokenStatus { + if in == nil { + return nil + } + out := new(TeleportProvisionTokenStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TeleportSAMLConnector) DeepCopyInto(out *TeleportSAMLConnector) { *out = *in diff --git a/integrations/operator/config/crd/bases/resources.teleport.dev_githubconnectors.yaml b/integrations/operator/config/crd/bases/resources.teleport.dev_githubconnectors.yaml index deaac8835bcac..40bbc0801a25d 100644 --- a/integrations/operator/config/crd/bases/resources.teleport.dev_githubconnectors.yaml +++ b/integrations/operator/config/crd/bases/resources.teleport.dev_githubconnectors.yaml @@ -35,6 +35,10 @@ spec: spec: description: GithubConnector resource definition v3 from Teleport properties: + api_endpoint_url: + description: APIEndpointURL is the URL of the API endpoint of the + Github instance this connector is for. + type: string client_id: description: ClientID is the Github OAuth app client ID. type: string @@ -45,6 +49,8 @@ spec: description: Display is the connector display name. type: string endpoint_url: + description: EndpointURL is the URL of the GitHub instance this connector + is for. type: string redirect_url: description: RedirectURL is the authorization callback URL. diff --git a/integrations/operator/config/crd/bases/resources.teleport.dev_provisiontokens.yaml b/integrations/operator/config/crd/bases/resources.teleport.dev_provisiontokens.yaml new file mode 100644 index 0000000000000..c35e7099b71a5 --- /dev/null +++ b/integrations/operator/config/crd/bases/resources.teleport.dev_provisiontokens.yaml @@ -0,0 +1,427 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + creationTimestamp: null + name: teleportprovisiontokens.resources.teleport.dev +spec: + group: resources.teleport.dev + names: + kind: TeleportProvisionToken + listKind: TeleportProvisionTokenList + plural: teleportprovisiontokens + shortNames: + - provisiontoken + - provisiontokens + singular: teleportprovisiontoken + scope: Namespaced + versions: + - name: v2 + schema: + openAPIV3Schema: + description: ProvisionToken is the Schema for the provisiontokens API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ProvisionToken resource definition v2 from Teleport + properties: + allow: + description: Allow is a list of TokenRules, nodes using this token + must match one allow rule to use this token. + items: + properties: + aws_account: + description: AWSAccount is the AWS account ID. + type: string + aws_arn: + description: AWSARN is used for the IAM join method, the AWS + identity of joining nodes must match this ARN. Supports wildcards + "*" and "?". + type: string + aws_regions: + description: AWSRegions is used for the EC2 join method and + is a list of AWS regions a node is allowed to join from. + items: + type: string + nullable: true + type: array + aws_role: + description: AWSRole is used for the EC2 join method and is + the the ARN of the AWS role that the auth server will assume + in order to call the ec2 API. + type: string + type: object + nullable: true + type: array + aws_iid_ttl: + description: AWSIIDTTL is the TTL to use for AWS EC2 Instance Identity + Documents used to join the cluster with this token. + format: duration + type: string + azure: + description: Azure allows the configuration of options specific to + the "azure" join method. + nullable: true + properties: + allow: + description: Allow is a list of Rules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + type: object + bot_name: + description: BotName is the name of the bot this token grants access + to, if any + type: string + circleci: + description: CircleCI allows the configuration of options specific + to the "circleci" join method. + nullable: true + properties: + allow: + description: Allow is a list of TokenRules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + organization_id: + type: string + type: object + gcp: + description: GCP allows the configuration of options specific to the + "gcp" join method. + nullable: true + properties: + allow: + description: Allow is a list of Rules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + type: object + github: + description: GitHub allows the configuration of options specific to + the "github" join method. + nullable: true + properties: + allow: + description: Allow is a list of TokenRules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + enterprise_server_host: + description: EnterpriseServerHost allows joining from runners + associated with a GitHub Enterprise Server instance. When unconfigured, + tokens will be validated against github.com, but when configured + to the host of a GHES instance, then the tokens will be validated + against host. This value should be the hostname of the GHES + instance, and should not include the scheme or a path. The instance + must be accessible over HTTPS at this hostname and the certificate + must be trusted by the Auth Server. + type: string + type: object + gitlab: + description: GitLab allows the configuration of options specific to + the "gitlab" join method. + nullable: true + properties: + allow: + description: Allow is a list of TokenRules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + domain: + description: 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`. + type: string + type: object + join_method: + description: JoinMethod is the joining method required in order to + use this token. Supported joining methods include "token", "ec2", + and "iam". + type: string + kubernetes: + description: Kubernetes allows the configuration of options specific + to the "kubernetes" join method. + nullable: true + properties: + allow: + description: Allow is a list of Rules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + type: object + roles: + description: Roles is a list of roles associated with the token, that + will be converted to metadata in the SSH and X509 certificates issued + to the user of the token + items: + type: string + nullable: true + type: array + suggested_agent_matcher_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: SuggestedAgentMatcherLabels is a set of labels to be + used by agents to match on resources. When an agent uses this token, + the agent should monitor resources that match those labels. For + databases, this means adding the labels to `db_service.resources.labels`. + Currently, only node-join scripts create a configuration according + to the suggestion. + type: object + suggested_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: SuggestedLabels is a set of labels that resources should + set when using this token to enroll themselves in the cluster. Currently, + only node-join scripts create a configuration according to the suggestion. + type: object + type: object + status: + description: TeleportProvisionTokenStatus defines the observed state of + TeleportProvisionToken + properties: + conditions: + description: Conditions represent the latest available observations + of an object's state + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + teleportResourceID: + format: int64 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/integrations/operator/config/crd/bases/resources.teleport.dev_roles.yaml b/integrations/operator/config/crd/bases/resources.teleport.dev_roles.yaml index 3618ad1815613..6019214c48b0d 100644 --- a/integrations/operator/config/crd/bases/resources.teleport.dev_roles.yaml +++ b/integrations/operator/config/crd/bases/resources.teleport.dev_roles.yaml @@ -74,6 +74,13 @@ spec: type: string nullable: true type: array + db_roles: + description: DatabaseRoles is a list of databases roles for automatic + user creation. + items: + type: string + nullable: true + type: array db_service_labels: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -81,12 +88,19 @@ spec: allow/deny access to Database Services. type: object db_users: - description: DatabaseUsers is a list of databaes users this role + description: DatabaseUsers is a list of databases users this role is allowed to connect as. items: type: string nullable: true type: array + desktop_groups: + description: DesktopGroups is a list of groups for created desktop + users to be added to + items: + type: string + nullable: true + type: array gcp_service_accounts: description: GCPServiceAccounts is a list of GCP service accounts this role is allowed to assume. @@ -94,6 +108,12 @@ spec: type: string nullable: true type: array + group_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: GroupLabels is a map of labels used as part of the + RBAC system. + type: object host_groups: description: HostGroups is a list of groups for created users to be added to @@ -470,6 +490,13 @@ spec: type: string nullable: true type: array + db_roles: + description: DatabaseRoles is a list of databases roles for automatic + user creation. + items: + type: string + nullable: true + type: array db_service_labels: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -477,12 +504,19 @@ spec: allow/deny access to Database Services. type: object db_users: - description: DatabaseUsers is a list of databaes users this role + description: DatabaseUsers is a list of databases users this role is allowed to connect as. items: type: string nullable: true type: array + desktop_groups: + description: DesktopGroups is a list of groups for created desktop + users to be added to + items: + type: string + nullable: true + type: array gcp_service_accounts: description: GCPServiceAccounts is a list of GCP service accounts this role is allowed to assume. @@ -490,6 +524,12 @@ spec: type: string nullable: true type: array + group_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: GroupLabels is a map of labels used as part of the + RBAC system. + type: object host_groups: description: HostGroups is a list of groups for created users to be added to @@ -845,7 +885,7 @@ spec: format: int32 type: integer value: - description: Value specifies the valueg to be used in the + description: Value specifies the value to be used in the cert extension. type: string type: object @@ -861,6 +901,14 @@ spec: is set to the idle duration. format: duration type: string + create_db_user: + description: CreateDatabaseUser enabled automatic database user + creation. + type: boolean + create_desktop_user: + description: CreateDesktopUser allows users to be automatically + created on a Windows desktop + type: boolean create_host_user: description: CreateHostUser allows users to be automatically created on a host @@ -965,7 +1013,7 @@ spec: type: string type: object request_access: - description: RequestAccess defines the access request stategy + description: RequestAccess defines the access request strategy (optional|note|always) where optional is the default. type: string request_prompt: @@ -1128,6 +1176,13 @@ spec: type: string nullable: true type: array + db_roles: + description: DatabaseRoles is a list of databases roles for automatic + user creation. + items: + type: string + nullable: true + type: array db_service_labels: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -1135,12 +1190,19 @@ spec: allow/deny access to Database Services. type: object db_users: - description: DatabaseUsers is a list of databaes users this role + description: DatabaseUsers is a list of databases users this role is allowed to connect as. items: type: string nullable: true type: array + desktop_groups: + description: DesktopGroups is a list of groups for created desktop + users to be added to + items: + type: string + nullable: true + type: array gcp_service_accounts: description: GCPServiceAccounts is a list of GCP service accounts this role is allowed to assume. @@ -1148,6 +1210,12 @@ spec: type: string nullable: true type: array + group_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: GroupLabels is a map of labels used as part of the + RBAC system. + type: object host_groups: description: HostGroups is a list of groups for created users to be added to @@ -1524,6 +1592,13 @@ spec: type: string nullable: true type: array + db_roles: + description: DatabaseRoles is a list of databases roles for automatic + user creation. + items: + type: string + nullable: true + type: array db_service_labels: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -1531,12 +1606,19 @@ spec: allow/deny access to Database Services. type: object db_users: - description: DatabaseUsers is a list of databaes users this role + description: DatabaseUsers is a list of databases users this role is allowed to connect as. items: type: string nullable: true type: array + desktop_groups: + description: DesktopGroups is a list of groups for created desktop + users to be added to + items: + type: string + nullable: true + type: array gcp_service_accounts: description: GCPServiceAccounts is a list of GCP service accounts this role is allowed to assume. @@ -1544,6 +1626,12 @@ spec: type: string nullable: true type: array + group_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: GroupLabels is a map of labels used as part of the + RBAC system. + type: object host_groups: description: HostGroups is a list of groups for created users to be added to @@ -1899,7 +1987,7 @@ spec: format: int32 type: integer value: - description: Value specifies the valueg to be used in the + description: Value specifies the value to be used in the cert extension. type: string type: object @@ -1915,6 +2003,14 @@ spec: is set to the idle duration. format: duration type: string + create_db_user: + description: CreateDatabaseUser enabled automatic database user + creation. + type: boolean + create_desktop_user: + description: CreateDesktopUser allows users to be automatically + created on a Windows desktop + type: boolean create_host_user: description: CreateHostUser allows users to be automatically created on a host @@ -2019,7 +2115,7 @@ spec: type: string type: object request_access: - description: RequestAccess defines the access request stategy + description: RequestAccess defines the access request strategy (optional|note|always) where optional is the default. type: string request_prompt: diff --git a/integrations/operator/controllers/resources/provision_token_controller.go b/integrations/operator/controllers/resources/provision_token_controller.go new file mode 100644 index 0000000000000..b366251081a64 --- /dev/null +++ b/integrations/operator/controllers/resources/provision_token_controller.go @@ -0,0 +1,85 @@ +/* +Copyright 2022 Gravitational, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resources + +import ( + "context" + + "github.com/gravitational/trace" + kclient "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/gravitational/teleport/api/types" + resourcesv2 "github.com/gravitational/teleport/integrations/operator/apis/resources/v2" + "github.com/gravitational/teleport/integrations/operator/sidecar" +) + +// provisionTokenClient implements TeleportResourceClient and offers CRUD methods needed to reconcile provision tokens +type provisionTokenClient struct { + TeleportClientAccessor sidecar.ClientAccessor +} + +// Get gets the Teleport provision token of a given name +func (r provisionTokenClient) Get(ctx context.Context, name string) (types.ProvisionToken, error) { + teleportClient, err := r.TeleportClientAccessor(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + token, err := teleportClient.GetToken(ctx, name) + return token, trace.Wrap(err) +} + +// Create creates a Teleport provision token +func (r provisionTokenClient) Create(ctx context.Context, token types.ProvisionToken) error { + teleportClient, err := r.TeleportClientAccessor(ctx) + if err != nil { + return trace.Wrap(err) + } + + return trace.Wrap(teleportClient.UpsertToken(ctx, token)) +} + +// Update updates a Teleport provision token +func (r provisionTokenClient) Update(ctx context.Context, token types.ProvisionToken) error { + teleportClient, err := r.TeleportClientAccessor(ctx) + if err != nil { + return trace.Wrap(err) + } + + return trace.Wrap(teleportClient.UpsertToken(ctx, token)) +} + +// Delete deletes a Teleport provision token +func (r provisionTokenClient) Delete(ctx context.Context, name string) error { + teleportClient, err := r.TeleportClientAccessor(ctx) + if err != nil { + return trace.Wrap(err) + } + + return trace.Wrap(teleportClient.DeleteToken(ctx, name)) +} + +// NewProvisionTokenReconciler instantiates a new Kubernetes controller reconciling provision token resources +func NewProvisionTokenReconciler(client kclient.Client, accessor sidecar.ClientAccessor) *TeleportResourceReconciler[types.ProvisionToken, *resourcesv2.TeleportProvisionToken] { + tokenClient := &provisionTokenClient{ + TeleportClientAccessor: accessor, + } + + resourceReconciler := NewTeleportResourceReconciler[types.ProvisionToken, *resourcesv2.TeleportProvisionToken]( + client, + tokenClient, + ) + + return resourceReconciler +} diff --git a/integrations/operator/controllers/resources/provision_token_controller_test.go b/integrations/operator/controllers/resources/provision_token_controller_test.go new file mode 100644 index 0000000000000..0905617d901b7 --- /dev/null +++ b/integrations/operator/controllers/resources/provision_token_controller_test.go @@ -0,0 +1,160 @@ +/* +Copyright 2022 Gravitational, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resources_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/gravitational/trace" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kclient "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/gravitational/teleport/api/types" + resourcesv2 "github.com/gravitational/teleport/integrations/operator/apis/resources/v2" + "github.com/gravitational/teleport/integrations/operator/controllers/resources/testlib" +) + +var tokenSpec = &types.ProvisionTokenSpecV2{ + Roles: []types.SystemRole{types.RoleNode}, + Allow: []*types.TokenRule{ + { + AWSAccount: "333333333333", + AWSARN: "arn:aws:sts::333333333333:assumed-role/teleport-node-role/i-*", + }, + }, + JoinMethod: types.JoinMethodIAM, +} + +// newProvisionTokenFromSpecNoExpire returns a new provision token with the given spec without expiration set. +func newProvisionTokenFromSpecNoExpire(token string, spec types.ProvisionTokenSpecV2) (types.ProvisionToken, error) { + t := &types.ProvisionTokenV2{ + Metadata: types.Metadata{ + Name: token, + }, + Spec: spec, + } + if err := t.CheckAndSetDefaults(); err != nil { + return nil, trace.Wrap(err) + } + return t, nil +} + +type tokenTestingPrimitives struct { + setup *testSetup +} + +func (g *tokenTestingPrimitives) Init(setup *testSetup) { + g.setup = setup +} + +func (g *tokenTestingPrimitives) SetupTeleportFixtures(ctx context.Context) error { + err := teleportCreateDummyRole(ctx, "testRoleA", g.setup.TeleportClient) + if err != nil { + return trace.Wrap(err) + } + return trace.Wrap(teleportCreateDummyRole(ctx, "testRoleB", g.setup.TeleportClient)) +} + +func (g *tokenTestingPrimitives) CreateTeleportResource(ctx context.Context, name string) error { + token, err := newProvisionTokenFromSpecNoExpire(name, *tokenSpec) + if err != nil { + return trace.Wrap(err) + } + token.SetOrigin(types.OriginKubernetes) + return trace.Wrap(g.setup.TeleportClient.UpsertToken(ctx, token)) +} + +func (g *tokenTestingPrimitives) GetTeleportResource(ctx context.Context, name string) (types.ProvisionToken, error) { + return g.setup.TeleportClient.GetToken(ctx, name) +} + +func (g *tokenTestingPrimitives) DeleteTeleportResource(ctx context.Context, name string) error { + return trace.Wrap(g.setup.TeleportClient.DeleteToken(ctx, name)) +} + +func (g *tokenTestingPrimitives) CreateKubernetesResource(ctx context.Context, name string) error { + token := &resourcesv2.TeleportProvisionToken{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: g.setup.Namespace.Name, + }, + Spec: resourcesv2.TeleportProvisionTokenSpec(*tokenSpec), + } + return trace.Wrap(g.setup.K8sClient.Create(ctx, token)) +} + +func (g *tokenTestingPrimitives) DeleteKubernetesResource(ctx context.Context, name string) error { + saml := &resourcesv2.TeleportProvisionToken{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: g.setup.Namespace.Name, + }, + } + return g.setup.K8sClient.Delete(ctx, saml) +} + +func (g *tokenTestingPrimitives) GetKubernetesResource(ctx context.Context, name string) (*resourcesv2.TeleportProvisionToken, error) { + saml := &resourcesv2.TeleportProvisionToken{} + obj := kclient.ObjectKey{ + Name: name, + Namespace: g.setup.Namespace.Name, + } + err := g.setup.K8sClient.Get(ctx, obj, saml) + return saml, trace.Wrap(err) +} + +func (g *tokenTestingPrimitives) ModifyKubernetesResource(ctx context.Context, name string) error { + saml, err := g.GetKubernetesResource(ctx, name) + if err != nil { + return trace.Wrap(err) + } + saml.Spec.Roles = []types.SystemRole{types.RoleNode, types.RoleProxy} + return trace.Wrap(g.setup.K8sClient.Update(ctx, saml)) +} + +func (g *tokenTestingPrimitives) CompareTeleportAndKubernetesResource(tResource types.ProvisionToken, kubeResource *resourcesv2.TeleportProvisionToken) (bool, string) { + teleportMap, _ := teleportResourceToMap(tResource) + kubernetesMap, _ := teleportResourceToMap(kubeResource.ToTeleport()) + + equal := cmp.Equal(teleportMap["spec"], kubernetesMap["spec"]) + if !equal { + return false, cmp.Diff(teleportMap["spec"], kubernetesMap["spec"]) + } + // The operator does not support resource expiration, the token should not expire + // else we'll end up in an inconsistent state + if !tResource.Expiry().IsZero() { + return false, "Token expires on the Teleport side" + } + return true, "" +} + +func TestProvisionTokenCreation(t *testing.T) { + test := &tokenTestingPrimitives{} + testlib.ResourceCreationTest[types.ProvisionToken, *resourcesv2.TeleportProvisionToken](t, test) +} + +func TestProvisionTokenDeletionDrift(t *testing.T) { + test := &tokenTestingPrimitives{} + testlib.ResourceDeletionDriftTest[types.ProvisionToken, *resourcesv2.TeleportProvisionToken](t, test) +} + +func TestProvisionTokenUpdate(t *testing.T) { + test := &tokenTestingPrimitives{} + testlib.ResourceUpdateTest[types.ProvisionToken, *resourcesv2.TeleportProvisionToken](t, test) +} diff --git a/integrations/operator/controllers/resources/testlib/env.go b/integrations/operator/controllers/resources/testlib/env.go index f74e8196d60bf..4133f54fb3c33 100644 --- a/integrations/operator/controllers/resources/testlib/env.go +++ b/integrations/operator/controllers/resources/testlib/env.go @@ -107,6 +107,7 @@ func defaultTeleportServiceConfig(t *testing.T) (*helpers.TeleInstance, string) types.NewRule("user", unrestricted), types.NewRule("auth_connector", unrestricted), types.NewRule("login_rule", unrestricted), + types.NewRule("token", unrestricted), }, }, }) @@ -190,6 +191,9 @@ func (s *TestSetup) StartKubernetesOperator(t *testing.T) { err = resources.NewLoginRuleReconciler(s.K8sClient, clientAccessor).SetupWithManager(k8sManager) require.NoError(t, err) + err = resources.NewProvisionTokenReconciler(s.K8sClient, clientAccessor).SetupWithManager(k8sManager) + require.NoError(t, err) + ctx, ctxCancel := context.WithCancel(context.Background()) s.Operator = k8sManager diff --git a/integrations/operator/crdgen/main.go b/integrations/operator/crdgen/main.go index 418fc84f3ffb5..b1cb94ec98cf4 100644 --- a/integrations/operator/crdgen/main.go +++ b/integrations/operator/crdgen/main.go @@ -110,6 +110,7 @@ func generateSchema(file *File, groupName string, resp *gogoplugin.CodeGenerator withCustomSpecFields([]string{"priority", "traits_expression", "traits_map"}), }, }, + {name: "ProvisionTokenV2"}, } for _, resource := range resources { diff --git a/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_provisiontokens.yaml b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_provisiontokens.yaml new file mode 100644 index 0000000000000..355119b6f294e --- /dev/null +++ b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_provisiontokens.yaml @@ -0,0 +1,391 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + creationTimestamp: null + name: teleportprovisiontokens.resources.teleport.dev +spec: + group: resources.teleport.dev + names: + kind: TeleportProvisionToken + listKind: TeleportProvisionTokenList + plural: teleportprovisiontokens + shortNames: + - provisiontoken + - provisiontokens + singular: teleportprovisiontoken + scope: Namespaced + versions: + - name: v2 + schema: + openAPIV3Schema: + description: ProvisionToken is the Schema for the provisiontokens API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ProvisionToken resource definition v2 from Teleport + properties: + allow: + description: Allow is a list of TokenRules, nodes using this token + must match one allow rule to use this token. + items: + properties: + aws_account: + description: AWSAccount is the AWS account ID. + type: string + aws_arn: + description: AWSARN is used for the IAM join method, the AWS + identity of joining nodes must match this ARN. Supports wildcards + "*" and "?". + type: string + aws_regions: + description: AWSRegions is used for the EC2 join method and + is a list of AWS regions a node is allowed to join from. + items: + type: string + nullable: true + type: array + aws_role: + description: AWSRole is used for the EC2 join method and is + the the ARN of the AWS role that the auth server will assume + in order to call the ec2 API. + type: string + type: object + nullable: true + type: array + aws_iid_ttl: + description: AWSIIDTTL is the TTL to use for AWS EC2 Instance Identity + Documents used to join the cluster with this token. + format: duration + type: string + azure: + description: Azure allows the configuration of options specific to + the "azure" join method. + nullable: true + properties: + allow: + description: Allow is a list of Rules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + type: object + bot_name: + description: BotName is the name of the bot this token grants access + to, if any + type: string + circleci: + description: CircleCI allows the configuration of options specific + to the "circleci" join method. + nullable: true + properties: + allow: + description: Allow is a list of TokenRules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + organization_id: + type: string + type: object + github: + description: GitHub allows the configuration of options specific to + the "github" join method. + nullable: true + properties: + allow: + description: Allow is a list of TokenRules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + enterprise_server_host: + description: EnterpriseServerHost allows joining from runners + associated with a GitHub Enterprise Server instance. When unconfigured, + tokens will be validated against github.com, but when configured + to the host of a GHES instance, then the tokens will be validated + against host. This value should be the hostname of the GHES + instance, and should not include the scheme or a path. The instance + must be accessible over HTTPS at this hostname and the certificate + must be trusted by the Auth Server. + type: string + type: object + gitlab: + description: GitLab allows the configuration of options specific to + the "gitlab" join method. + nullable: true + properties: + allow: + description: Allow is a list of TokenRules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + domain: + description: 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`. + type: string + type: object + join_method: + description: JoinMethod is the joining method required in order to + use this token. Supported joining methods include "token", "ec2", + and "iam". + type: string + kubernetes: + description: Kubernetes allows the configuration of options specific + to the "kubernetes" join method. + nullable: true + properties: + allow: + description: Allow is a list of Rules, nodes using this token + must match one allow rule to use this token. + items: + properties: + actions: + description: Actions specifies optional actions taken when + this rule matches + items: + type: string + nullable: true + type: array + resources: + description: Resources is a list of resources + items: + type: string + nullable: true + type: array + verbs: + description: Verbs is a list of verbs + items: + type: string + nullable: true + type: array + where: + description: Where specifies optional advanced matcher + type: string + type: object + nullable: true + type: array + type: object + roles: + description: Roles is a list of roles associated with the token, that + will be converted to metadata in the SSH and X509 certificates issued + to the user of the token + items: + type: string + nullable: true + type: array + suggested_agent_matcher_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: SuggestedAgentMatcherLabels is a set of labels to be + used by agents to match on resources. When an agent uses this token, + the agent should monitor resources that match those labels. For + databases, this means adding the labels to `db_service.resources.labels`. + Currently, only node-join scripts create a configuration according + to the suggestion. + type: object + suggested_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: SuggestedLabels is a set of labels that resources should + set when using this token to enroll themselves in the cluster. Currently, + only node-join scripts create a configuration according to the suggestion. + type: object + type: object + status: + description: TeleportProvisionTokenStatus defines the observed state of + TeleportProvisionToken + properties: + conditions: + description: Conditions represent the latest available observations + of an object's state + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + teleportResourceID: + format: int64 + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/integrations/operator/crdgen/tree.go b/integrations/operator/crdgen/tree.go index 2d12a82b7db8b..6be215bccda8b 100644 --- a/integrations/operator/crdgen/tree.go +++ b/integrations/operator/crdgen/tree.go @@ -90,33 +90,47 @@ func (forest *Forest) addFile(fileDesc *gogodesc.FileDescriptorProto) *File { // Build messages list. for i, msgDesc := range msgs { - msgDesc.GetName() - flds := msgDesc.GetField() - message := Message{ - index: i, - desc: msgDesc, - parent: &file, - Fields: make([]*Field, 0, len(flds)), - fieldMap: make(map[string]*Field, len(flds)), - } - for j, fld := range flds { - field := Field{ - index: j, - parent: &message, - desc: fld, - } - message.Fields = append(message.Fields, &field) - message.fieldMap[field.Name()] = &field - } - file.Messages[i] = &message - file.messageMap[msgDesc] = &message - file.messageByName[message.Name()] = &message - forest.messageMap[msgDesc] = &message + message := forest.addMessageFromDesc(msgDesc, i, &file, nil) + file.Messages[i] = message + file.messageMap[msgDesc] = message + file.messageByName[message.Name()] = message } return &file } +// addMessageFromDesc generates a message from a gogodesc.DescriptorProto and registers the message into the forest messageMap. +// This function will recursively register all nested messages if any, but only returns the root message. +func (forest *Forest) addMessageFromDesc(msgDesc *gogodesc.DescriptorProto, index int, file *File, parentMsg *Message) *Message { + msgDesc.GetName() + flds := msgDesc.GetField() + message := Message{ + index: index, + desc: msgDesc, + parent: file, + parentMsg: parentMsg, + Fields: make([]*Field, 0, len(flds)), + fieldMap: make(map[string]*Field, len(flds)), + } + for j, fld := range flds { + field := Field{ + index: j, + parent: &message, + desc: fld, + } + message.Fields = append(message.Fields, &field) + message.fieldMap[field.Name()] = &field + } + forest.messageMap[msgDesc] = &message + + for i, nestedType := range msgDesc.GetNestedType() { + forest.addMessageFromDesc(nestedType, i, file, &message) + } + + return &message + +} + func (file File) Forest() *Forest { return file.parent } diff --git a/integrations/operator/main.go b/integrations/operator/main.go index fce9f8c8a86b2..2d1f07d99cab6 100644 --- a/integrations/operator/main.go +++ b/integrations/operator/main.go @@ -184,6 +184,12 @@ func main() { setupLog.Info("Login Rules are only available in Teleport Enterprise edition. TeleportLoginRule resources won't be reconciled") } + if err = resources.NewProvisionTokenReconciler(mgr.GetClient(), bot.GetClient). + SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "TeleportProvisionToken") + os.Exit(1) + } + //+kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { setupLog.Error(err, "unable to set up health check")