diff --git a/docs/pages/admin-guides/infrastructure-as-code/teleport-operator/secret-lookup.mdx b/docs/pages/admin-guides/infrastructure-as-code/teleport-operator/secret-lookup.mdx index a23e4935c5051..ca8f2b20e5b60 100644 --- a/docs/pages/admin-guides/infrastructure-as-code/teleport-operator/secret-lookup.mdx +++ b/docs/pages/admin-guides/infrastructure-as-code/teleport-operator/secret-lookup.mdx @@ -11,7 +11,10 @@ of the Teleport Kubernetes operator CRs. Some Teleport resources might contain sensitive values. Select CR fields can reference an existing Kubernetes secret and the operator will retrieve the value from the secret when reconciling. -Currently only the GithubConnector and OIDCConnector `client_secret` field support secret lookup. +Currently supported fields for secret lookup: +- GithubConnector `client_secret` +- OIDCConnector `client_secret` +- TrustedClusterV2 `token` ## Prerequisites diff --git a/docs/pages/admin-guides/infrastructure-as-code/teleport-operator/teleport-operator.mdx b/docs/pages/admin-guides/infrastructure-as-code/teleport-operator/teleport-operator.mdx index e8dec4b877a13..890421acf2742 100644 --- a/docs/pages/admin-guides/infrastructure-as-code/teleport-operator/teleport-operator.mdx +++ b/docs/pages/admin-guides/infrastructure-as-code/teleport-operator/teleport-operator.mdx @@ -26,16 +26,21 @@ could cause instability and non-deterministic behaviour. Currently supported Teleport resources are: -- users (`TeleportUser`) -- roles +- Users (`TeleportUser`) +- Roles - `TeleportRole` creates role v5 - `TeleportRoleV6` creates role v6 - `TeleportRoleV7` creates role v7 -- OIDC connectors (`TeleportOIDCConnector`) -- SAML connectors (`TeleportSAMLConnector`) -- GitHub connectors (`TeleportGithubConnector`) -- provision tokens (`TeleportProvisionToken`) -- Login Rules (`TeleportLoginRules`) +- OIDC Connectors (`TeleportOIDCConnector`) +- SAML Connectors (`TeleportSAMLConnector`) +- GitHub Connectors (`TeleportGithubConnector`) +- Provision Tokens (`TeleportProvisionToken`) +- Login Rules (`TeleportLoginRule`) +- Access Lists (`TeleportAccessList`) +- Okta Import Rules (`TeleportOktaImportRule`) +- OpenSSHEICE Servers (`TeleportOpenSSHEICEServerV2`) +- OpenSSH Servers (`TeleportOpenSSHServerV2`) +- Trusted Clusters (`TeleportTrustedClusterV2`) ### Setting up the operator diff --git a/docs/pages/reference/operator-resources/resources.teleport.dev_trustedclustersv2.mdx b/docs/pages/reference/operator-resources/resources.teleport.dev_trustedclustersv2.mdx new file mode 100644 index 0000000000000..8728b51b2ab5c --- /dev/null +++ b/docs/pages/reference/operator-resources/resources.teleport.dev_trustedclustersv2.mdx @@ -0,0 +1,41 @@ +--- +title: TeleportTrustedClusterV2 +description: Provides a comprehensive list of fields in the TeleportTrustedClusterV2 resource available through the Teleport Kubernetes operator +tocDepth: 3 +--- + +{/*Auto-generated file. Do not edit.*/} +{/*To regenerate, navigate to integrations/operator and run "make crd-docs".*/} + +This guide is a comprehensive reference to the fields in the `TeleportTrustedClusterV2` +resource, which you can apply after installing the Teleport Kubernetes operator. + + +## resources.teleport.dev/v1 + +**apiVersion:** resources.teleport.dev/v1 + +|Field|Type|Description| +|---|---|---| +|apiVersion|string|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| +|kind|string|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| +|metadata|object|| +|spec|[object](#spec)|TrustedCluster resource definition v2 from Teleport| + +### spec + +|Field|Type|Description| +|---|---|---| +|enabled|boolean|Enabled is a bool that indicates if the TrustedCluster is enabled or disabled. Setting Enabled to false has a side effect of deleting the user and host certificate authority (CA).| +|role_map|[][object](#specrole_map-items)|RoleMap specifies role mappings to remote roles.| +|token|string|Token is the authorization token provided by another cluster needed by this cluster to join. This field supports secret lookup. See the operator documentation for more details.| +|tunnel_addr|string|ReverseTunnelAddress is the address of the SSH proxy server of the cluster to join. If not set, it is derived from `:`.| +|web_proxy_addr|string|ProxyAddress is the address of the web proxy server of the cluster to join. If not set, it is derived from `:`.| + +### spec.role_map items + +|Field|Type|Description| +|---|---|---| +|local|[]string|Local specifies local roles to map to| +|remote|string|Remote specifies remote role name to map from| + diff --git a/examples/chart/teleport-cluster/charts/teleport-operator/operator-crds/resources.teleport.dev_trustedclustersv2.yaml b/examples/chart/teleport-cluster/charts/teleport-operator/operator-crds/resources.teleport.dev_trustedclustersv2.yaml new file mode 100644 index 0000000000000..4cf1410472b64 --- /dev/null +++ b/examples/chart/teleport-cluster/charts/teleport-operator/operator-crds/resources.teleport.dev_trustedclustersv2.yaml @@ -0,0 +1,149 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + creationTimestamp: null + name: teleporttrustedclustersv2.resources.teleport.dev +spec: + group: resources.teleport.dev + names: + kind: TeleportTrustedClusterV2 + listKind: TeleportTrustedClusterV2List + plural: teleporttrustedclustersv2 + shortNames: + - trustedclusterv2 + - trustedclustersv2 + singular: teleporttrustedclusterv2 + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: TrustedClusterV2 is the Schema for the trustedclustersv2 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: TrustedCluster resource definition v2 from Teleport + properties: + enabled: + description: Enabled is a bool that indicates if the TrustedCluster + is enabled or disabled. Setting Enabled to false has a side effect + of deleting the user and host certificate authority (CA). + type: boolean + role_map: + description: RoleMap specifies role mappings to remote roles. + items: + properties: + local: + description: Local specifies local roles to map to + items: + type: string + nullable: true + type: array + remote: + description: Remote specifies remote role name to map from + type: string + type: object + type: array + token: + description: Token is the authorization token provided by another + cluster needed by this cluster to join. This field supports secret + lookup. See the operator documentation for more details. + type: string + tunnel_addr: + description: ReverseTunnelAddress is the address of the SSH proxy + server of the cluster to join. If not set, it is derived from `:`. + type: string + web_proxy_addr: + description: ProxyAddress is the address of the web proxy server of + the cluster to join. If not set, it is derived from `:`. + type: string + type: object + status: + description: Status defines the observed state of the Teleport resource + 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. + 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. + 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/role.yaml b/examples/chart/teleport-cluster/charts/teleport-operator/templates/role.yaml index 25b8c72416dc6..1b7c21935ce5c 100644 --- a/examples/chart/teleport-cluster/charts/teleport-operator/templates/role.yaml +++ b/examples/chart/teleport-cluster/charts/teleport-operator/templates/role.yaml @@ -36,6 +36,8 @@ rules: - teleportopensshserversv2/status - teleportopenssheiceserversv2 - teleportopenssheiceserversv2/status + - teleporttrustedclustersv2 + - teleporttrustedclustersv2/status verbs: - get - list diff --git a/examples/chart/teleport-cluster/templates/auth/config.yaml b/examples/chart/teleport-cluster/templates/auth/config.yaml index 99fe59e061c9c..d1c4bffcb5cf6 100644 --- a/examples/chart/teleport-cluster/templates/auth/config.yaml +++ b/examples/chart/teleport-cluster/templates/auth/config.yaml @@ -131,6 +131,14 @@ data: - read - update - delete + - resources: + - trusted_cluster + verbs: + - list + - create + - read + - update + - delete deny: {} version: v7 --- diff --git a/integrations/operator/README.md b/integrations/operator/README.md index 8e91c62d6d46c..d240ca82da84b 100644 --- a/integrations/operator/README.md +++ b/integrations/operator/README.md @@ -20,6 +20,10 @@ The operator supports reconciling the following Kubernetes CRs: - TeleportRoleV7 (creates role v7) - TeleportProvisionToken - TeleportGithubConnector +- TeleportAccessList +- TeleportOpenSSHEICEServerV2 +- TeleportOpenSSHServerV2 +- TeleportTrustedClusterV2 - TeleportSAMLConnector [1] - TeleportOIDCConnector [1] - TeleportLoginRule [1] diff --git a/integrations/operator/apis/resources/v1/trusted_cluster_types.go b/integrations/operator/apis/resources/v1/trusted_cluster_types.go new file mode 100644 index 0000000000000..0f6b8f753fac2 --- /dev/null +++ b/integrations/operator/apis/resources/v1/trusted_cluster_types.go @@ -0,0 +1,96 @@ +/* + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package v1 + +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(&TeleportTrustedClusterV2{}, &TeleportTrustedClusterV2List{}) +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// TeleportTrustedClusterV2 is the Schema for the trusted_clusters API +type TeleportTrustedClusterV2 struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec TeleportTrustedClusterV2Spec `json:"spec,omitempty"` + Status resources.Status `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// TeleportTrustedClusterV2List contains a list of TeleportTrustedClusterV2 +type TeleportTrustedClusterV2List struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []TeleportTrustedClusterV2 `json:"items"` +} + +// ToTeleport converts the resource to the teleport trusted_cluster API type. +func (r TeleportTrustedClusterV2) ToTeleport() types.TrustedCluster { + return &types.TrustedClusterV2{ + Kind: types.KindTrustedCluster, + Version: types.V2, + Metadata: types.Metadata{ + Name: r.Name, + Labels: r.Labels, + Description: r.Annotations[resources.DescriptionKey], + }, + Spec: types.TrustedClusterSpecV2(r.Spec), + } +} + +// TeleportTrustedClusterV2Spec defines the desired state of TeleportTrustedClusterV2 +type TeleportTrustedClusterV2Spec types.TrustedClusterSpecV2 + +// Marshal serializes a spec into binary data. +func (spec *TeleportTrustedClusterV2Spec) Marshal() ([]byte, error) { + return (*types.TrustedClusterSpecV2)(spec).Marshal() +} + +// Unmarshal deserializes a spec from binary data. +func (spec *TeleportTrustedClusterV2Spec) Unmarshal(data []byte) error { + return (*types.TrustedClusterSpecV2)(spec).Unmarshal(data) +} + +// DeepCopyInto deep-copies one trusted_cluster spec into another. +// Required to satisfy runtime.Object interface. +func (spec *TeleportTrustedClusterV2Spec) DeepCopyInto(out *TeleportTrustedClusterV2Spec) { + data, err := spec.Marshal() + if err != nil { + panic(err) + } + *out = TeleportTrustedClusterV2Spec{} + if err = out.Unmarshal(data); err != nil { + panic(err) + } +} + +// StatusConditions returns a pointer to Status.Conditions slice. +func (r *TeleportTrustedClusterV2) StatusConditions() *[]metav1.Condition { + return &r.Status.Conditions +} diff --git a/integrations/operator/apis/resources/v1/zz_generated.deepcopy.go b/integrations/operator/apis/resources/v1/zz_generated.deepcopy.go index e2f6b7ce932c1..6b803d79d2577 100644 --- a/integrations/operator/apis/resources/v1/zz_generated.deepcopy.go +++ b/integrations/operator/apis/resources/v1/zz_generated.deepcopy.go @@ -605,3 +605,72 @@ func (in *TeleportRoleV7Spec) DeepCopy() *TeleportRoleV7Spec { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TeleportTrustedClusterV2) DeepCopyInto(out *TeleportTrustedClusterV2) { + *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 TeleportTrustedClusterV2. +func (in *TeleportTrustedClusterV2) DeepCopy() *TeleportTrustedClusterV2 { + if in == nil { + return nil + } + out := new(TeleportTrustedClusterV2) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TeleportTrustedClusterV2) 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 *TeleportTrustedClusterV2List) DeepCopyInto(out *TeleportTrustedClusterV2List) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]TeleportTrustedClusterV2, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TeleportTrustedClusterV2List. +func (in *TeleportTrustedClusterV2List) DeepCopy() *TeleportTrustedClusterV2List { + if in == nil { + return nil + } + out := new(TeleportTrustedClusterV2List) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TeleportTrustedClusterV2List) 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 TeleportTrustedClusterV2Spec. +func (in *TeleportTrustedClusterV2Spec) DeepCopy() *TeleportTrustedClusterV2Spec { + if in == nil { + return nil + } + out := new(TeleportTrustedClusterV2Spec) + in.DeepCopyInto(out) + return out +} diff --git a/integrations/operator/config/crd/bases/resources.teleport.dev_trustedclustersv2.yaml b/integrations/operator/config/crd/bases/resources.teleport.dev_trustedclustersv2.yaml new file mode 100644 index 0000000000000..4cf1410472b64 --- /dev/null +++ b/integrations/operator/config/crd/bases/resources.teleport.dev_trustedclustersv2.yaml @@ -0,0 +1,149 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + creationTimestamp: null + name: teleporttrustedclustersv2.resources.teleport.dev +spec: + group: resources.teleport.dev + names: + kind: TeleportTrustedClusterV2 + listKind: TeleportTrustedClusterV2List + plural: teleporttrustedclustersv2 + shortNames: + - trustedclusterv2 + - trustedclustersv2 + singular: teleporttrustedclusterv2 + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: TrustedClusterV2 is the Schema for the trustedclustersv2 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: TrustedCluster resource definition v2 from Teleport + properties: + enabled: + description: Enabled is a bool that indicates if the TrustedCluster + is enabled or disabled. Setting Enabled to false has a side effect + of deleting the user and host certificate authority (CA). + type: boolean + role_map: + description: RoleMap specifies role mappings to remote roles. + items: + properties: + local: + description: Local specifies local roles to map to + items: + type: string + nullable: true + type: array + remote: + description: Remote specifies remote role name to map from + type: string + type: object + type: array + token: + description: Token is the authorization token provided by another + cluster needed by this cluster to join. This field supports secret + lookup. See the operator documentation for more details. + type: string + tunnel_addr: + description: ReverseTunnelAddress is the address of the SSH proxy + server of the cluster to join. If not set, it is derived from `:`. + type: string + web_proxy_addr: + description: ProxyAddress is the address of the web proxy server of + the cluster to join. If not set, it is derived from `:`. + type: string + type: object + status: + description: Status defines the observed state of the Teleport resource + 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. + 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. + 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/controllers/reconcilers/legacy_resource_without_labels.go b/integrations/operator/controllers/reconcilers/legacy_resource_without_labels.go index c7079240a9d7b..307c1283ee398 100644 --- a/integrations/operator/controllers/reconcilers/legacy_resource_without_labels.go +++ b/integrations/operator/controllers/reconcilers/legacy_resource_without_labels.go @@ -62,7 +62,7 @@ func (a ResourceWithoutLabelsAdapter[T]) SetResourceRevision(res T, revision str } // SetResourceLabels implements the Adapter interface. As the resource does not -// // support labels, it only sets the origin label. +// support labels, it only sets the origin label. func (a ResourceWithoutLabelsAdapter[T]) SetResourceLabels(res T, labels map[string]string) { // We don't set all labels as the Resource doesn't support them // Only the origin diff --git a/integrations/operator/controllers/resources/setup.go b/integrations/operator/controllers/resources/setup.go index a2e78a8cdc68c..fffceccbf8c39 100644 --- a/integrations/operator/controllers/resources/setup.go +++ b/integrations/operator/controllers/resources/setup.go @@ -47,6 +47,7 @@ func SetupAllControllers(log logr.Logger, mgr manager.Manager, teleportClient *c {"TeleportProvisionToken", NewProvisionTokenReconciler}, {"TeleportOpenSSHServerV2", NewOpenSSHServerV2Reconciler}, {"TeleportOpenSSHEICEServerV2", NewOpenSSHEICEServerV2Reconciler}, + {"TeleportTrustedClusterV2", NewTrustedClusterV2Reconciler}, } oidc := modules.GetProtoEntitlement(features, entitlements.OIDC) diff --git a/integrations/operator/controllers/resources/testlib/env.go b/integrations/operator/controllers/resources/testlib/env.go index 9de19230826c3..e41a4f22677eb 100644 --- a/integrations/operator/controllers/resources/testlib/env.go +++ b/integrations/operator/controllers/resources/testlib/env.go @@ -139,6 +139,7 @@ func defaultTeleportServiceConfig(t *testing.T) (*helpers.TeleInstance, string) types.NewRule(types.KindOktaImportRule, unrestricted), types.NewRule(types.KindAccessList, unrestricted), types.NewRule(types.KindNode, unrestricted), + types.NewRule(types.KindTrustedCluster, unrestricted), }, }, }) diff --git a/integrations/operator/controllers/resources/trusted_cluster_controller.go b/integrations/operator/controllers/resources/trusted_cluster_controller.go new file mode 100644 index 0000000000000..a3154bed00b42 --- /dev/null +++ b/integrations/operator/controllers/resources/trusted_cluster_controller.go @@ -0,0 +1,91 @@ +/* + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package resources + +import ( + "context" + + "github.com/gravitational/trace" + kclient "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/gravitational/teleport/api/client" + "github.com/gravitational/teleport/api/types" + resourcesv1 "github.com/gravitational/teleport/integrations/operator/apis/resources/v1" + "github.com/gravitational/teleport/integrations/operator/controllers" + "github.com/gravitational/teleport/integrations/operator/controllers/reconcilers" + "github.com/gravitational/teleport/integrations/operator/controllers/resources/secretlookup" +) + +// trustedClusterClient implements TeleportResourceClient and offers CRUD +// methods needed to reconcile trusted_clusters. +type trustedClusterClient struct { + teleportClient *client.Client + kubeClient kclient.Client +} + +// Get gets the Teleport trusted_cluster of a given name. +func (r trustedClusterClient) Get(ctx context.Context, name string) (types.TrustedCluster, error) { + trustedCluster, err := r.teleportClient.GetTrustedCluster(ctx, name) + return trustedCluster, trace.Wrap(err) +} + +// Create creates a Teleport trusted_cluster. +func (r trustedClusterClient) Create(ctx context.Context, trustedCluster types.TrustedCluster) error { + _, err := r.teleportClient.CreateTrustedCluster(ctx, trustedCluster) + return trace.Wrap(err) +} + +// Update updates a Teleport trusted_cluster. +func (r trustedClusterClient) Update(ctx context.Context, trustedCluster types.TrustedCluster) error { + _, err := r.teleportClient.UpdateTrustedCluster(ctx, trustedCluster) + return trace.Wrap(err) +} + +// Delete deletes a Teleport trusted_cluster. +func (r trustedClusterClient) Delete(ctx context.Context, name string) error { + return trace.Wrap(r.teleportClient.DeleteTrustedCluster(ctx, name)) +} + +// Mutate mutates a Teleport trusted_cluster. +func (r trustedClusterClient) Mutate(ctx context.Context, new, existing types.TrustedCluster, crKey kclient.ObjectKey) error { + secret := new.GetToken() + if secretlookup.IsNeeded(secret) { + resolvedSecret, err := secretlookup.Try(ctx, r.kubeClient, crKey.Name, crKey.Namespace, secret) + if err != nil { + return trace.Wrap(err) + } + new.SetToken(resolvedSecret) + } + return nil +} + +// NewTrustedClusterV2Reconciler instantiates a new Kubernetes controller reconciling trusted_cluster v2 resources +func NewTrustedClusterV2Reconciler(client kclient.Client, tClient *client.Client) (controllers.Reconciler, error) { + trustedClusterClient := &trustedClusterClient{ + teleportClient: tClient, + kubeClient: client, + } + + resourceReconciler, err := reconcilers.NewTeleportResourceWithoutLabelsReconciler[types.TrustedCluster, *resourcesv1.TeleportTrustedClusterV2]( + client, + trustedClusterClient, + ) + + return resourceReconciler, trace.Wrap(err, "building teleport resource reconciler") +} diff --git a/integrations/operator/controllers/resources/trusted_clusterv2_controller_test.go b/integrations/operator/controllers/resources/trusted_clusterv2_controller_test.go new file mode 100644 index 0000000000000..a3b1dc98de5ba --- /dev/null +++ b/integrations/operator/controllers/resources/trusted_clusterv2_controller_test.go @@ -0,0 +1,356 @@ +/* + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package resources_test + +import ( + "context" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/uuid" + "github.com/gravitational/trace" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/util/retry" + kclient "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/integration/helpers" + resourcesv1 "github.com/gravitational/teleport/integrations/operator/apis/resources/v1" + "github.com/gravitational/teleport/integrations/operator/controllers/reconcilers" + "github.com/gravitational/teleport/integrations/operator/controllers/resources/secretlookup" + "github.com/gravitational/teleport/integrations/operator/controllers/resources/testlib" + "github.com/gravitational/teleport/lib" + "github.com/gravitational/teleport/lib/service/servicecfg" + "github.com/gravitational/teleport/lib/utils" +) + +type trustedClusterV2TestingPrimitives struct { + // remoteCluster specifies the remote trusted cluster instance. + remoteCluster *helpers.TeleInstance + // trustedClusterSpec specifies the trusted cluster specs. + trustedClusterSpec types.TrustedClusterSpecV2 + + setup *testSetup + reconcilers.ResourceWithoutLabelsAdapter[types.TrustedCluster] +} + +func (r *trustedClusterV2TestingPrimitives) Init(setup *testSetup) { + r.setup = setup +} + +func (r *trustedClusterV2TestingPrimitives) SetupTeleportFixtures(ctx context.Context) error { + return nil +} + +func (r *trustedClusterV2TestingPrimitives) CreateTeleportResource(ctx context.Context, name string) error { + trustedCluster, err := types.NewTrustedCluster(name, r.trustedClusterSpec) + if err != nil { + return trace.Wrap(err) + } + trustedCluster.SetOrigin(types.OriginKubernetes) + _, err = r.setup.TeleportClient.CreateTrustedCluster(ctx, trustedCluster) + return trace.Wrap(err) +} + +func (r *trustedClusterV2TestingPrimitives) GetTeleportResource(ctx context.Context, name string) (types.TrustedCluster, error) { + return r.setup.TeleportClient.GetTrustedCluster(ctx, name) +} + +func (r *trustedClusterV2TestingPrimitives) DeleteTeleportResource(ctx context.Context, name string) error { + return trace.Wrap(r.setup.TeleportClient.DeleteTrustedCluster(ctx, name)) +} + +func (r *trustedClusterV2TestingPrimitives) CreateKubernetesResource(ctx context.Context, name string) error { + trustedCluster := &resourcesv1.TeleportTrustedClusterV2{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: r.setup.Namespace.Name, + }, + Spec: resourcesv1.TeleportTrustedClusterV2Spec(r.trustedClusterSpec), + } + return trace.Wrap(r.setup.K8sClient.Create(ctx, trustedCluster)) +} + +func (r *trustedClusterV2TestingPrimitives) DeleteKubernetesResource(ctx context.Context, name string) error { + trustedCluster := &resourcesv1.TeleportTrustedClusterV2{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: r.setup.Namespace.Name, + }, + } + return trace.Wrap(r.setup.K8sClient.Delete(ctx, trustedCluster)) +} + +func (r *trustedClusterV2TestingPrimitives) GetKubernetesResource(ctx context.Context, name string) (*resourcesv1.TeleportTrustedClusterV2, error) { + trustedCluster := &resourcesv1.TeleportTrustedClusterV2{} + obj := kclient.ObjectKey{ + Name: name, + Namespace: r.setup.Namespace.Name, + } + err := r.setup.K8sClient.Get(ctx, obj, trustedCluster) + return trustedCluster, trace.Wrap(err) +} + +func (r *trustedClusterV2TestingPrimitives) ModifyKubernetesResource(ctx context.Context, name string) error { + trustedCluster, err := r.GetKubernetesResource(ctx, name) + if err != nil { + return trace.Wrap(err) + } + trustedCluster.Spec.RoleMap[0] = types.RoleMapping{ + Remote: "remote-admin", + Local: []string{"local-dev"}, + } + return trace.Wrap(r.setup.K8sClient.Update(ctx, trustedCluster)) +} + +func (r *trustedClusterV2TestingPrimitives) CompareTeleportAndKubernetesResource(tResource types.TrustedCluster, kubeResource *resourcesv1.TeleportTrustedClusterV2) (bool, string) { + diff := cmp.Diff(tResource, kubeResource.ToTeleport(), testlib.CompareOptions()...) + return diff == "", diff +} + +// setupTest initializes a remote cluster for testing trusted clusters. +func (r *trustedClusterV2TestingPrimitives) setupTest(t *testing.T, clusterName string) { + ctx := context.Background() + + remoteCluster := helpers.NewInstance(t, helpers.InstanceConfig{ + ClusterName: clusterName, + HostID: uuid.New().String(), + NodeName: helpers.Loopback, + Logger: utils.NewSlogLoggerForTests(), + }) + r.remoteCluster = remoteCluster + + rcConf := servicecfg.MakeDefaultConfig() + rcConf.DataDir = t.TempDir() + rcConf.Auth.Enabled = true + rcConf.Proxy.Enabled = true + rcConf.Proxy.DisableWebInterface = true + rcConf.Version = "v2" + + lib.SetInsecureDevMode(true) + t.Cleanup(func() { lib.SetInsecureDevMode(false) }) + + require.NoError(t, remoteCluster.CreateEx(t, nil, rcConf)) + require.NoError(t, remoteCluster.Start()) + t.Cleanup(func() { require.NoError(t, remoteCluster.StopAll()) }) + + // Create trusted cluster join token + token := "secret_token" + tokenResource, err := types.NewProvisionToken(token, []types.SystemRole{types.RoleTrustedCluster}, time.Time{}) + require.NoError(t, err) + remoteCluster.Process.GetAuthServer().UpsertToken(ctx, tokenResource) + + // Create required role + localDev := "local-dev" + require.NoError(t, teleportCreateDummyRole(ctx, localDev, r.setup.TeleportClient)) + + r.trustedClusterSpec = types.TrustedClusterSpecV2{ + Enabled: true, + Token: token, + ProxyAddress: remoteCluster.Web, + ReverseTunnelAddress: remoteCluster.ReverseTunnel, + RoleMap: []types.RoleMapping{ + { + Remote: "remote-dev", + Local: []string{localDev}, + }, + }, + } +} + +func TestTrustedClusterV2Creation(t *testing.T) { + test := &trustedClusterV2TestingPrimitives{} + setup := testlib.SetupTestEnv(t) + test.Init(setup) + ctx := context.Background() + + resourceName := "remote.example.com" + test.setupTest(t, resourceName) + + require.NoError(t, test.CreateKubernetesResource(ctx, resourceName)) + + var resource types.TrustedCluster + var err error + testlib.FastEventually(t, func() bool { + resource, err = test.GetTeleportResource(ctx, resourceName) + return !trace.IsNotFound(err) + }) + require.NoError(t, err) + require.Equal(t, resourceName, test.GetResourceName(resource)) + require.Equal(t, types.OriginKubernetes, test.GetResourceOrigin(resource)) + + err = test.DeleteKubernetesResource(ctx, resourceName) + require.NoError(t, err) + + testlib.FastEventually(t, func() bool { + _, err = test.GetTeleportResource(ctx, resourceName) + return trace.IsNotFound(err) + }) +} + +func TestTrustedClusterV2DeletionDrift(t *testing.T) { + test := &trustedClusterV2TestingPrimitives{} + setup := testlib.SetupTestEnv(t) + test.Init(setup) + ctx := context.Background() + + resourceName := "remote.example.com" + test.setupTest(t, resourceName) + + require.NoError(t, test.CreateKubernetesResource(ctx, resourceName)) + + var resource types.TrustedCluster + var err error + testlib.FastEventually(t, func() bool { + resource, err = test.GetTeleportResource(ctx, resourceName) + return !trace.IsNotFound(err) + }) + require.NoError(t, err) + require.Equal(t, resourceName, test.GetResourceName(resource)) + require.Equal(t, types.OriginKubernetes, test.GetResourceOrigin(resource)) + + // We cause a drift by altering the Teleport resource. + // To make sure the operator does not reconcile while we're finished we suspend the operator + setup.StopKubernetesOperator() + + err = test.DeleteTeleportResource(ctx, resourceName) + require.NoError(t, err) + testlib.FastEventually(t, func() bool { + _, err = test.GetTeleportResource(ctx, resourceName) + return trace.IsNotFound(err) + }) + + // We flag the resource for deletion in Kubernetes (it won't be fully removed until the operator has processed it and removed the finalizer) + err = test.DeleteKubernetesResource(ctx, resourceName) + require.NoError(t, err) + + // Test section: We resume the operator, it should reconcile and recover from the drift + setup.StartKubernetesOperator(t) + + // The operator should handle the failed Teleport deletion gracefully and unlock the Kubernetes resource deletion + testlib.FastEventually(t, func() bool { + _, err = test.GetKubernetesResource(ctx, resourceName) + return kerrors.IsNotFound(err) + }) +} + +func TestTrustedClusterV2Update(t *testing.T) { + test := &trustedClusterV2TestingPrimitives{} + setup := testlib.SetupTestEnv(t) + test.Init(setup) + ctx := context.Background() + + resourceName := "remote.example.com" + test.setupTest(t, resourceName) + + // The resource is created in Teleport + require.NoError(t, test.CreateTeleportResource(ctx, resourceName)) + + // The resource is created in Kubernetes, with at least a field altered + require.NoError(t, test.CreateKubernetesResource(ctx, resourceName)) + + // Check the resource was updated in Teleport + testlib.FastEventuallyWithT(t, func(c *assert.CollectT) { + tResource, err := test.GetTeleportResource(ctx, resourceName) + require.NoError(c, err) + + kubeResource, err := test.GetKubernetesResource(ctx, resourceName) + require.NoError(c, err) + + // Kubernetes and Teleport resources are in-sync + equal, diff := test.CompareTeleportAndKubernetesResource(tResource, kubeResource) + if !equal { + t.Logf("Kubernetes and Teleport resources not sync-ed yet: %s", diff) + } + assert.True(c, equal) + }) + + // Updating the resource in Kubernetes + // The modification can fail because of a conflict with the resource controller. We retry if that happens. + err := retry.RetryOnConflict(retry.DefaultRetry, func() error { + return test.ModifyKubernetesResource(ctx, resourceName) + }) + require.NoError(t, err) + + // Check the resource was updated in Teleport + testlib.FastEventuallyWithT(t, func(c *assert.CollectT) { + kubeResource, err := test.GetKubernetesResource(ctx, resourceName) + require.NoError(c, err) + + tResource, err := test.GetTeleportResource(ctx, resourceName) + require.NoError(c, err) + + // Kubernetes and Teleport resources are in-sync + equal, diff := test.CompareTeleportAndKubernetesResource(tResource, kubeResource) + if !equal { + t.Logf("Kubernetes and Teleport resources not sync-ed yet: %s", diff) + } + assert.True(c, equal) + }) + + // Delete the resource to avoid leftover state. + err = test.DeleteTeleportResource(ctx, resourceName) + require.NoError(t, err) +} + +func TestTrustedClusterV2SecretLookup(t *testing.T) { + test := &trustedClusterV2TestingPrimitives{} + setup := testlib.SetupTestEnv(t) + test.Init(setup) + ctx := context.Background() + + resourceName := "remote.example.com" + test.setupTest(t, resourceName) + + secretName := validRandomResourceName("trusted-cluster-secret") + secretKey := "token" + secretValue := test.trustedClusterSpec.Token + + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: setup.Namespace.Name, + Annotations: map[string]string{ + secretlookup.AllowLookupAnnotation: resourceName, + }, + }, + StringData: map[string]string{ + secretKey: secretValue, + }, + Type: v1.SecretTypeOpaque, + } + kubeClient := setup.K8sClient + require.NoError(t, kubeClient.Create(ctx, secret)) + + test.trustedClusterSpec.Token = "secret://" + secretName + "/" + secretKey + require.NoError(t, test.CreateKubernetesResource(ctx, resourceName)) + + testlib.FastEventually(t, func() bool { + trustedCluster, err := test.GetTeleportResource(ctx, resourceName) + if err != nil { + return false + } + return trustedCluster.GetToken() == secretValue + }) +} diff --git a/integrations/operator/crdgen/additional_doc.go b/integrations/operator/crdgen/additional_doc.go index 2458af6779bbc..396c94410808f 100644 --- a/integrations/operator/crdgen/additional_doc.go +++ b/integrations/operator/crdgen/additional_doc.go @@ -29,4 +29,7 @@ var additionalDescription = map[string]map[string]string{ "OIDCConnectorSpecV3": { "ClientSecret": supportsSecretLookupDescription, }, + "TrustedClusterSpecV2": { + "Token": supportsSecretLookupDescription, + }, } diff --git a/integrations/operator/crdgen/handlerequest.go b/integrations/operator/crdgen/handlerequest.go index 66d90324cc0f4..57f479de185e3 100644 --- a/integrations/operator/crdgen/handlerequest.go +++ b/integrations/operator/crdgen/handlerequest.go @@ -213,6 +213,7 @@ func generateSchema(file *File, groupName string, format crdFormatFunc, resp *go withAdditionalColumns(serverColumns), }, }, + {name: "TrustedClusterV2", opts: []resourceSchemaOption{withVersionInKindOverride()}}, } for _, resource := range resources { diff --git a/integrations/operator/crdgen/ignored.go b/integrations/operator/crdgen/ignored.go index 596e79b4d291a..7b647756f12cc 100644 --- a/integrations/operator/crdgen/ignored.go +++ b/integrations/operator/crdgen/ignored.go @@ -44,4 +44,7 @@ var ignoredFields = map[string]stringSet{ // allows remote exec on agentful nodes. "CmdLabels": struct{}{}, }, + "TrustedClusterSpecV2": { + "Roles": struct{}{}, // Deprecated, use RoleMap instead. + }, } diff --git a/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_openssheiceserversv2.yaml b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_openssheiceserversv2.yaml index 3617909ae6a67..bad8469a76fb6 100644 --- a/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_openssheiceserversv2.yaml +++ b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_openssheiceserversv2.yaml @@ -88,6 +88,20 @@ spec: type: string type: object type: object + github: + description: GitHub contains info about GitHub proxies where each + server represents a GitHub organization. + nullable: true + properties: + integration: + description: Integration is the integration that is associated + with this Server. + type: string + organization: + description: Organization specifies the name of the organization + for the GitHub integration. + type: string + type: object hostname: description: Hostname is server hostname type: string diff --git a/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_opensshserversv2.yaml b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_opensshserversv2.yaml index ad7dfd4174776..fe3d76a8db7a4 100644 --- a/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_opensshserversv2.yaml +++ b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_opensshserversv2.yaml @@ -87,6 +87,20 @@ spec: type: string type: object type: object + github: + description: GitHub contains info about GitHub proxies where each + server represents a GitHub organization. + nullable: true + properties: + integration: + description: Integration is the integration that is associated + with this Server. + type: string + organization: + description: Organization specifies the name of the organization + for the GitHub integration. + type: string + type: object hostname: description: Hostname is server hostname type: string diff --git a/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_roles.yaml b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_roles.yaml index 5d1c5ddfb9809..9e3a0f46e9334 100644 --- a/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_roles.yaml +++ b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_roles.yaml @@ -157,6 +157,18 @@ spec: type: string nullable: true type: array + github_permissions: + description: GitHubPermissions defines GitHub integration related + permissions. + items: + properties: + orgs: + items: + type: string + nullable: true + type: array + type: object + type: array group_labels: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -595,6 +607,17 @@ spec: type: string nullable: true type: array + workload_identity_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: WorkloadIdentityLabels controls whether or not specific + WorkloadIdentity resources can be invoked. Further authorization + controls exist on the WorkloadIdentity resource itself. + type: object + workload_identity_labels_expression: + description: WorkloadIdentityLabelsExpression is a predicate expression + used to allow/deny access to issuing a WorkloadIdentity. + type: string type: object deny: description: Deny is the set of conditions evaluated to deny access. @@ -722,6 +745,18 @@ spec: type: string nullable: true type: array + github_permissions: + description: GitHubPermissions defines GitHub integration related + permissions. + items: + properties: + orgs: + items: + type: string + nullable: true + type: array + type: object + type: array group_labels: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -1160,6 +1195,17 @@ spec: type: string nullable: true type: array + workload_identity_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: WorkloadIdentityLabels controls whether or not specific + WorkloadIdentity resources can be invoked. Further authorization + controls exist on the WorkloadIdentity resource itself. + type: object + workload_identity_labels_expression: + description: WorkloadIdentityLabelsExpression is a predicate expression + used to allow/deny access to issuing a WorkloadIdentity. + type: string type: object options: description: Options is for OpenSSH options like agent forwarding. @@ -1584,6 +1630,18 @@ spec: type: string nullable: true type: array + github_permissions: + description: GitHubPermissions defines GitHub integration related + permissions. + items: + properties: + orgs: + items: + type: string + nullable: true + type: array + type: object + type: array group_labels: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -2022,6 +2080,17 @@ spec: type: string nullable: true type: array + workload_identity_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: WorkloadIdentityLabels controls whether or not specific + WorkloadIdentity resources can be invoked. Further authorization + controls exist on the WorkloadIdentity resource itself. + type: object + workload_identity_labels_expression: + description: WorkloadIdentityLabelsExpression is a predicate expression + used to allow/deny access to issuing a WorkloadIdentity. + type: string type: object deny: description: Deny is the set of conditions evaluated to deny access. @@ -2149,6 +2218,18 @@ spec: type: string nullable: true type: array + github_permissions: + description: GitHubPermissions defines GitHub integration related + permissions. + items: + properties: + orgs: + items: + type: string + nullable: true + type: array + type: object + type: array group_labels: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -2587,6 +2668,17 @@ spec: type: string nullable: true type: array + workload_identity_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: WorkloadIdentityLabels controls whether or not specific + WorkloadIdentity resources can be invoked. Further authorization + controls exist on the WorkloadIdentity resource itself. + type: object + workload_identity_labels_expression: + description: WorkloadIdentityLabelsExpression is a predicate expression + used to allow/deny access to issuing a WorkloadIdentity. + type: string type: object options: description: Options is for OpenSSH options like agent forwarding. diff --git a/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_rolesv6.yaml b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_rolesv6.yaml index f0af70fc7cf2f..5e1ff2a359184 100644 --- a/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_rolesv6.yaml +++ b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_rolesv6.yaml @@ -160,6 +160,18 @@ spec: type: string nullable: true type: array + github_permissions: + description: GitHubPermissions defines GitHub integration related + permissions. + items: + properties: + orgs: + items: + type: string + nullable: true + type: array + type: object + type: array group_labels: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -598,6 +610,17 @@ spec: type: string nullable: true type: array + workload_identity_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: WorkloadIdentityLabels controls whether or not specific + WorkloadIdentity resources can be invoked. Further authorization + controls exist on the WorkloadIdentity resource itself. + type: object + workload_identity_labels_expression: + description: WorkloadIdentityLabelsExpression is a predicate expression + used to allow/deny access to issuing a WorkloadIdentity. + type: string type: object deny: description: Deny is the set of conditions evaluated to deny access. @@ -725,6 +748,18 @@ spec: type: string nullable: true type: array + github_permissions: + description: GitHubPermissions defines GitHub integration related + permissions. + items: + properties: + orgs: + items: + type: string + nullable: true + type: array + type: object + type: array group_labels: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -1163,6 +1198,17 @@ spec: type: string nullable: true type: array + workload_identity_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: WorkloadIdentityLabels controls whether or not specific + WorkloadIdentity resources can be invoked. Further authorization + controls exist on the WorkloadIdentity resource itself. + type: object + workload_identity_labels_expression: + description: WorkloadIdentityLabelsExpression is a predicate expression + used to allow/deny access to issuing a WorkloadIdentity. + type: string type: object options: description: Options is for OpenSSH options like agent forwarding. diff --git a/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_rolesv7.yaml b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_rolesv7.yaml index 88056b0b54a53..fb682402d11e3 100644 --- a/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_rolesv7.yaml +++ b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_rolesv7.yaml @@ -160,6 +160,18 @@ spec: type: string nullable: true type: array + github_permissions: + description: GitHubPermissions defines GitHub integration related + permissions. + items: + properties: + orgs: + items: + type: string + nullable: true + type: array + type: object + type: array group_labels: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -598,6 +610,17 @@ spec: type: string nullable: true type: array + workload_identity_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: WorkloadIdentityLabels controls whether or not specific + WorkloadIdentity resources can be invoked. Further authorization + controls exist on the WorkloadIdentity resource itself. + type: object + workload_identity_labels_expression: + description: WorkloadIdentityLabelsExpression is a predicate expression + used to allow/deny access to issuing a WorkloadIdentity. + type: string type: object deny: description: Deny is the set of conditions evaluated to deny access. @@ -725,6 +748,18 @@ spec: type: string nullable: true type: array + github_permissions: + description: GitHubPermissions defines GitHub integration related + permissions. + items: + properties: + orgs: + items: + type: string + nullable: true + type: array + type: object + type: array group_labels: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -1163,6 +1198,17 @@ spec: type: string nullable: true type: array + workload_identity_labels: + additionalProperties: + x-kubernetes-preserve-unknown-fields: true + description: WorkloadIdentityLabels controls whether or not specific + WorkloadIdentity resources can be invoked. Further authorization + controls exist on the WorkloadIdentity resource itself. + type: object + workload_identity_labels_expression: + description: WorkloadIdentityLabelsExpression is a predicate expression + used to allow/deny access to issuing a WorkloadIdentity. + type: string type: object options: description: Options is for OpenSSH options like agent forwarding. diff --git a/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_trustedclustersv2.yaml b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_trustedclustersv2.yaml new file mode 100644 index 0000000000000..4cf1410472b64 --- /dev/null +++ b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_trustedclustersv2.yaml @@ -0,0 +1,149 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + creationTimestamp: null + name: teleporttrustedclustersv2.resources.teleport.dev +spec: + group: resources.teleport.dev + names: + kind: TeleportTrustedClusterV2 + listKind: TeleportTrustedClusterV2List + plural: teleporttrustedclustersv2 + shortNames: + - trustedclusterv2 + - trustedclustersv2 + singular: teleporttrustedclusterv2 + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: TrustedClusterV2 is the Schema for the trustedclustersv2 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: TrustedCluster resource definition v2 from Teleport + properties: + enabled: + description: Enabled is a bool that indicates if the TrustedCluster + is enabled or disabled. Setting Enabled to false has a side effect + of deleting the user and host certificate authority (CA). + type: boolean + role_map: + description: RoleMap specifies role mappings to remote roles. + items: + properties: + local: + description: Local specifies local roles to map to + items: + type: string + nullable: true + type: array + remote: + description: Remote specifies remote role name to map from + type: string + type: object + type: array + token: + description: Token is the authorization token provided by another + cluster needed by this cluster to join. This field supports secret + lookup. See the operator documentation for more details. + type: string + tunnel_addr: + description: ReverseTunnelAddress is the address of the SSH proxy + server of the cluster to join. If not set, it is derived from `:`. + type: string + web_proxy_addr: + description: ProxyAddress is the address of the web proxy server of + the cluster to join. If not set, it is derived from `:`. + type: string + type: object + status: + description: Status defines the observed state of the Teleport resource + 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. + 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. + 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/testdata/golden/resources.teleport.dev_users.yaml b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_users.yaml index 504c3695c4532..0c68b6dec714f 100644 --- a/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_users.yaml +++ b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_users.yaml @@ -57,6 +57,10 @@ spec: description: SAMLSingleLogoutURL is the SAML Single log-out URL to initiate SAML SLO (single log-out), if applicable. type: string + user_id: + description: UserID is the ID of the identity. Some connectors + like GitHub have an unique ID apart from the username. + type: string username: description: Username is username supplied by external identity provider @@ -76,6 +80,10 @@ spec: description: SAMLSingleLogoutURL is the SAML Single log-out URL to initiate SAML SLO (single log-out), if applicable. type: string + user_id: + description: UserID is the ID of the identity. Some connectors + like GitHub have an unique ID apart from the username. + type: string username: description: Username is username supplied by external identity provider @@ -101,6 +109,10 @@ spec: description: SAMLSingleLogoutURL is the SAML Single log-out URL to initiate SAML SLO (single log-out), if applicable. type: string + user_id: + description: UserID is the ID of the identity. Some connectors + like GitHub have an unique ID apart from the username. + type: string username: description: Username is username supplied by external identity provider diff --git a/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/client/proto/authservice.proto b/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/client/proto/authservice.proto index 03b6f9ac35439..fc6dc146ff248 100644 --- a/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/client/proto/authservice.proto +++ b/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/client/proto/authservice.proto @@ -333,7 +333,13 @@ message RouteToApp { // GCPServiceAccount is the GCP service account to assume when accessing GCP API. string GCPServiceAccount = 7 [(gogoproto.jsontag) = "gcp_service_account,omitempty"]; // URI is the URI of the app. This is the internal endpoint where the application is running and isn't user-facing. + // Used merely for audit events and mirrors the URI from the app spec. Not used as a source of + // truth when routing connections. string URI = 8 [(gogoproto.jsontag) = "uri,omitempty"]; + // TargetPort signifies that the cert grants access to a specific port in a multi-port TCP app, as + // long as the port is defined in the app spec. When specified, it must be between 1 and 65535. + // Used only for routing, should not be used in other contexts (e.g., access requests). + uint32 TargetPort = 9 [(gogoproto.jsontag) = "target_port,omitempty"]; } // GetUserRequest specifies parameters for the GetUser method. @@ -1934,6 +1940,60 @@ message CreateRegisterChallengeRequest { DeviceUsage DeviceUsage = 3 [(gogoproto.jsontag) = "device_usage,omitempty"]; } +// IdentityCenterAccount holds information about an Identity Center account +// within an IdentityCenterAccountAssignment +message IdentityCenterAccount { + // ID is the AWS-assigned account ID + string ID = 1; + + // ARN is the full Amazon Resource Name for the AWS account + string ARN = 2; + + // AccountName is the human-readable name of the account + string AccountName = 3; + + // Description is a free text description of the account + string Description = 4; +} + +// IdentityCenterPermissionSet holds information about an Identity Center +// permission set within an IdentityCenterAccountAssignment +message IdentityCenterPermissionSet { + // ARN is the full Amazon Resource Name for the Permission Set + string ARN = 1; + + // Name is the human readable name for the Permission Set + string Name = 2; +} + +// IdentityCenterAccountAssignment represents a requestable Identity Center +// Account Assignment. This is strictly a wire-format object for use with the +// Unfied resource cache, and the types defined in the `identitycenter` package +// should be used for actual processing. +message IdentityCenterAccountAssignment { + // Kind is the database server resource kind. + string Kind = 1 [(gogoproto.jsontag) = "kind"]; + // SubKind is an optional resource subkind. + string SubKind = 2 [(gogoproto.jsontag) = "sub_kind,omitempty"]; + // Version is the resource version. + string Version = 3 [(gogoproto.jsontag) = "version"]; + // Metadata is the account metadata. + types.Metadata Metadata = 4 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "metadata" + ]; + + // DisplayName is a human-readable name for the Account assignment + string DisplayName = 5; + + // Account is the Identity Center Account this assigment references + IdentityCenterAccount Account = 6; + + // PermissionSet is the Identity Center Permission Set this assignment + // references + IdentityCenterPermissionSet PermissionSet = 7; +} + // PaginatedResource represents one of the supported resources. message PaginatedResource { // Resource is the resource itself. @@ -1962,6 +2022,12 @@ message PaginatedResource { types.AppServerOrSAMLIdPServiceProviderV1 AppServerOrSAMLIdPServiceProvider = 11 [deprecated = true]; // SAMLIdPServiceProvider represents a SAML IdP service provider resource. types.SAMLIdPServiceProviderV1 SAMLIdPServiceProvider = 12 [(gogoproto.jsontag) = "saml_idp_service_provider,omitempty"]; + // GitServer represents a Git server resource. + types.ServerV2 git_server = 15; + + // IdentityCenterAccountAssignment represents a requestable Identity Center + // Account Assignment + IdentityCenterAccountAssignment IdentityCenterAccountAssignment = 16 [(gogoproto.jsontag) = "identity_center_account_assignment,omitempty"]; } // Logins allowed for the included resource. Only to be populated for SSH and Desktops. @@ -2079,6 +2145,36 @@ message ListResourcesRequest { bool IncludeLogins = 13 [(gogoproto.jsontag) = "include_logins,omitempty"]; } +// ResolveSSHTargetRequest provides details about a server to be resolved in +// an equivalent manner to a ssh dial request. +// +// Resolution can happen in two modes: +// 1) searching for hosts based on labels, a predicate expression, or keywords +// 2) searching based on hostname +// +// If a Host is provided, resolution will only operate in the second mode and +// will not perform any resolution based on labels. In order to resolve via +// labels the Host must not be populated. +message ResolveSSHTargetRequest { + // The target host as would be sent to the proxy during a dial request. + string host = 1; + // The ssh port. This value is optional, and both empty string and "0" are typically + // treated as meaning that any port should match. + string port = 2; + // If not empty, a label-based matcher. + map labels = 3; + // Boolean conditions that will be matched against the resource. + string predicate_expression = 4; + // A list of search keywords to match against resource field values. + repeated string search_keywords = 5; +} + +// GetSSHTargetsResponse holds ssh servers that match an ssh targets request. +message ResolveSSHTargetResponse { + // The target matching the supplied request. + types.ServerV2 server = 1; +} + // GetSSHTargetsRequest gets all servers that might match an equivalent ssh dial request. message GetSSHTargetsRequest { // Host is the target host as would be sent to the proxy during a dial request. @@ -2349,16 +2445,21 @@ message DownstreamInventoryOneOf { } } -// DownstreamInventoryPing is sent down the inventory control stream for testing/debug -// purposes. +// DownstreamInventoryPing is sent down the inventory control stream. message DownstreamInventoryPing { uint64 ID = 1; } // UpstreamInventoryPong is sent up the inventory control stream in response to a downstream -// ping (used for testing/debug purposes). +// ping including the system clock of the downstream. message UpstreamInventoryPong { uint64 ID = 1; + // SystemClock advertises the system clock of the upstream. + google.protobuf.Timestamp SystemClock = 2 [ + (gogoproto.stdtime) = true, + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "system_clock,omitempty" + ]; } // UpstreamInventoryHello is the hello message sent up the inventory control stream. @@ -2553,10 +2654,10 @@ message InventoryPingRequest { // ServerID is the ID of the instance to ping. string ServerID = 1; - // ControlLog forces the ping to use the standard "commit then act" model of control log synchronization - // for the ping. This significantly increases the amount of time it takes for the ping request to - // complete, but is useful for testing/debugging control log issues. - bool ControlLog = 2; + // ControlLog used to signal that the ping should use the control log synchronization. + // + // Deprecated: the control log is unsupported and unsound to use. + bool ControlLog = 2 [deprecated = true]; } // InventoryPingResponse returns the result of an inventory ping initiated via an @@ -3207,7 +3308,11 @@ service AuthService { // GetTrustedClusters gets all current Trusted Cluster resources. rpc GetTrustedClusters(google.protobuf.Empty) returns (types.TrustedClusterV2List); // UpsertTrustedCluster upserts a Trusted Cluster in a backend. - rpc UpsertTrustedCluster(types.TrustedClusterV2) returns (types.TrustedClusterV2); + // + // Deprecated: Use [teleport.trust.v1.UpsertTrustedCluster] instead. + rpc UpsertTrustedCluster(types.TrustedClusterV2) returns (types.TrustedClusterV2) { + option deprecated = true; + } // DeleteTrustedCluster deletes an existing Trusted Cluster in a backend by name. rpc DeleteTrustedCluster(types.ResourceRequest) returns (google.protobuf.Empty); @@ -3482,6 +3587,9 @@ service AuthService { // but may result in confusing behavior if it is used outside of those contexts. rpc GetSSHTargets(GetSSHTargetsRequest) returns (GetSSHTargetsResponse); + // ResolveSSHTarget returns the server that would be resolved in an equivalent ssh dial request. + rpc ResolveSSHTarget(ResolveSSHTargetRequest) returns (ResolveSSHTargetResponse); + // GetDomainName returns local auth domain of the current auth server rpc GetDomainName(google.protobuf.Empty) returns (GetDomainNameResponse); // GetClusterCACert returns the PEM-encoded TLS certs for the local cluster diff --git a/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/client/proto/event.proto b/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/client/proto/event.proto index 7c0cd043eb13d..b8c39fec6054e 100644 --- a/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/client/proto/event.proto +++ b/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/client/proto/event.proto @@ -34,6 +34,7 @@ import "teleport/secreports/v1/secreports.proto"; import "teleport/userloginstate/v1/userloginstate.proto"; import "teleport/userprovisioning/v2/statichostuser.proto"; import "teleport/usertasks/v1/user_tasks.proto"; +import "teleport/workloadidentity/v1/resource.proto"; option go_package = "github.com/gravitational/teleport/api/client/proto"; @@ -206,5 +207,9 @@ message Event { // IdentityCenterAccountlAssignment is a resource representing a potential // Permission Set grant on a specific AWS account. teleport.identitycenter.v1.AccountAssignment IdentityCenterAccountAssignment = 74; + // PluginStaticCredentials is filled in PluginStaticCredentials related events + types.PluginStaticCredentialsV1 PluginStaticCredentials = 75; + // WorkloadIdentity is a resource for workload identity. + teleport.workloadidentity.v1.WorkloadIdentity WorkloadIdentity = 76; } } diff --git a/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/types/events/events.proto b/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/types/events/events.proto index bd61c99381b62..c82a6e6976e0b 100644 --- a/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/types/events/events.proto +++ b/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/types/events/events.proto @@ -1547,6 +1547,33 @@ message AccessRequestCreate { ]; } +// AccessRequestExpire is emitted when access request has expired. +message AccessRequestExpire { + // Metadata is a common event metadata + Metadata Metadata = 1 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ResourceMetadata is a common resource event metadata + ResourceMetadata Resource = 2 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // RequestID is access request ID + string RequestID = 3 [(gogoproto.jsontag) = "id"]; + + // ResourceExpiry is the time at which the access request resource will expire. + google.protobuf.Timestamp ResourceExpiry = 4 [ + (gogoproto.stdtime) = true, + (gogoproto.nullable) = true, + (gogoproto.jsontag) = "expiry,omitempty" + ]; +} + // ResourceID is a unique identifier for a teleport resource. This is duplicated // from api/types/types.proto to decouple the api and events types and because // neither file currently imports the other. @@ -1617,6 +1644,21 @@ message PortForward { // Addr is a target port forwarding address string Addr = 5 [(gogoproto.jsontag) = "addr"]; + + // KubernetesCluster has information about a kubernetes cluster, if + // applicable. + KubernetesClusterMetadata KubernetesCluster = 6 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // KubernetesPod has information about a kubernetes pod, if applicable. + KubernetesPodMetadata KubernetesPod = 7 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; } // X11Forward is emitted when a user requests X11 protocol forwarding @@ -2675,6 +2717,9 @@ message AppMetadata { ]; // AppName is the configured application name. string AppName = 4 [(gogoproto.jsontag) = "app_name,omitempty"]; + // AppTargetPort signifies that the app is a multi-port TCP app and says which port was used to + // access the app. This field is not set for other types of apps, including single-port TCP apps. + uint32 AppTargetPort = 5 [(gogoproto.jsontag) = "app_target_port,omitempty"]; } // AppCreate is emitted when a new application resource is created. @@ -3105,6 +3150,12 @@ message DatabaseSessionStart { // connection. This can be useful for backend process cancellation or // termination and it is not a sensitive or secret value. uint32 PostgresPID = 8 [(gogoproto.jsontag) = "postgres_pid,omitempty"]; + // Client is the common client event metadata. + ClientMetadata Client = 9 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; } // DatabaseSessionQuery is emitted when a user executes a database query. @@ -4330,6 +4381,8 @@ message IntegrationMetadata { AWSOIDCIntegrationMetadata AWSOIDC = 2 [(gogoproto.jsontag) = "aws_oidc,omitempty"]; // AzureOIDC contains metadata for Azure OIDC integrations. AzureOIDCIntegrationMetadata AzureOIDC = 3 [(gogoproto.jsontag) = "azure_oidc,omitempty"]; + // GitHub contains metadata for GitHub integrations. + GitHubIntegrationMetadata GitHub = 4 [(gogoproto.jsontag) = "github,omitempty"]; } // AWSOIDCIntegrationMetadata contains metadata for AWS OIDC integrations. @@ -4351,6 +4404,12 @@ message AzureOIDCIntegrationMetadata { string ClientID = 2 [(gogoproto.jsontag) = "client_id,omitempty"]; } +// GitHubIntegrationMetadata contains metadata for GitHub integrations. +message GitHubIntegrationMetadata { + // Organization specifies the name of the organization for the GitHub integration. + string Organization = 1 [(gogoproto.jsontag) = "organization,omitempty"]; +} + // PluginCreate is emitted when a plugin resource is created. message PluginCreate { // Metadata is a common event metadata. @@ -4676,6 +4735,14 @@ message OneOf { events.UserTaskUpdate UserTaskUpdate = 189; events.UserTaskDelete UserTaskDelete = 190; events.SFTPSummary SFTPSummary = 191; + events.ContactCreate ContactCreate = 192; + events.ContactDelete ContactDelete = 193; + events.WorkloadIdentityCreate WorkloadIdentityCreate = 194; + events.WorkloadIdentityUpdate WorkloadIdentityUpdate = 195; + events.WorkloadIdentityDelete WorkloadIdentityDelete = 196; + events.GitCommand GitCommand = 197; + events.UserLoginAccessListInvalid UserLoginAccessListInvalid = 198; + events.AccessRequestExpire AccessRequestExpire = 199; } } @@ -4829,6 +4896,9 @@ message RouteToApp { string GCPServiceAccount = 7 [(gogoproto.jsontag) = "gcp_service_account,omitempty"]; // URI is the application URI. string URI = 8 [(gogoproto.jsontag) = "uri,omitempty"]; + // TargetPort signifies that the user accessed a specific port in a multi-port TCP app. The value + // must be between 1 and 65535. + uint32 TargetPort = 9 [(gogoproto.jsontag) = "target_port,omitempty"]; } // RouteToDatabase combines parameters for database service routing information. @@ -6697,6 +6767,12 @@ message SPIFFESVIDIssued { // Audiences is the list of audiences in the issued SVID. // Only present if the SVID is a JWT. repeated string Audiences = 11 [(gogoproto.jsontag) = "audiences,omitempty"]; + // The WorkloadIdentity resource that was used to issue the SVID, this will + // be empty if the legacy RPCs were used. + string WorkloadIdentity = 12 [(gogoproto.jsontag) = "workload_identity,omitempty"]; + // The revision of the WorkloadIdentity resource that was used to issue the + // SVID. This will be empty if the legacy RPCs were used. + string WorkloadIdentityRevision = 13 [(gogoproto.jsontag) = "workload_identity_revision,omitempty"]; } // AuthPreferenceUpdate is emitted when the auth preference is updated. @@ -7566,3 +7642,305 @@ message UserTaskDelete { (gogoproto.jsontag) = "" ]; } + +// ContactCreate is emitted when a contact is created. +message ContactCreate { + // Metadata is a common event metadata + Metadata Metadata = 1 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ResourceMetadata is a common resource event metadata + ResourceMetadata Resource = 2 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // User is a common user event metadata + UserMetadata User = 3 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ConnectionMetadata holds information about the connection + ConnectionMetadata Connection = 4 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // Status indicates whether the creation was successful. + Status Status = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // Email is the Email of the contact being deleted + string Email = 6 [(gogoproto.jsontag) = "email"]; + + // ContactType is the type of the contact being deleted ('Business' or 'Security') + ContactType ContactType = 7 [(gogoproto.jsontag) = "contact_type"]; +} + +// ContactDelete is emitted when a contact is deleted. +message ContactDelete { + // Metadata is a common event metadata + Metadata Metadata = 1 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ResourceMetadata is a common resource event metadata + ResourceMetadata Resource = 2 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // User is a common user event metadata + UserMetadata User = 3 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ConnectionMetadata holds information about the connection + ConnectionMetadata Connection = 4 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // Status indicates whether the deletion was successful. + Status Status = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // Email is the Email of the contact being deleted + string Email = 6 [(gogoproto.jsontag) = "email"]; + + // ContactType is the type of the contact being deleted ('Business' or 'Security') + ContactType ContactType = 7 [(gogoproto.jsontag) = "contact_type"]; +} + +// ContactType is the type of contact being added. +enum ContactType { + CONTACT_TYPE_UNSPECIFIED = 0; + CONTACT_TYPE_BUSINESS = 1; + CONTACT_TYPE_SECURITY = 2; +} + +// WorkloadIdentityCreate is emitted when a WorkloadIdentity is created. +message WorkloadIdentityCreate { + // Metadata is a common event metadata + Metadata Metadata = 1 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ResourceMetadata is a common resource event metadata + ResourceMetadata Resource = 2 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // User is a common user event metadata + UserMetadata User = 3 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ConnectionMetadata holds information about the connection + ConnectionMetadata Connection = 4 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // WorkloadIdentityData is a copy of the WorkloadIdentity resource + google.protobuf.Struct WorkloadIdentityData = 5 [ + (gogoproto.jsontag) = "workload_identity_data,omitempty", + (gogoproto.casttype) = "Struct" + ]; +} + +// WorkloadIdentityUpdate is emitted when a WorkloadIdentity is updated. +message WorkloadIdentityUpdate { + // Metadata is a common event metadata + Metadata Metadata = 1 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ResourceMetadata is a common resource event metadata + ResourceMetadata Resource = 2 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // User is a common user event metadata + UserMetadata User = 3 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ConnectionMetadata holds information about the connection + ConnectionMetadata Connection = 4 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // WorkloadIdentityData is a copy of the WorkloadIdentity resource + google.protobuf.Struct WorkloadIdentityData = 5 [ + (gogoproto.jsontag) = "workload_identity_data,omitempty", + (gogoproto.casttype) = "Struct" + ]; +} + +// WorkloadIdentityDelete is emitted when a WorkloadIdentity is deleted. +message WorkloadIdentityDelete { + // Metadata is a common event metadata + Metadata Metadata = 1 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ResourceMetadata is a common resource event metadata + ResourceMetadata Resource = 2 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // User is a common user event metadata + UserMetadata User = 3 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ConnectionMetadata holds information about the connection + ConnectionMetadata Connection = 4 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; +} + +// GitCommand is emitted when a user performs a Git fetch or push command. +message GitCommand { + // Metadata is a common event metadata + Metadata Metadata = 1 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // User is a common user event metadata + UserMetadata User = 2 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ConnectionMetadata holds information about the connection + ConnectionMetadata Connection = 3 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // SessionMetadata is a common event session metadata + SessionMetadata Session = 4 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // ServerMetadata is a common server metadata + ServerMetadata Server = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // CommandMetadata is a common command metadata + CommandMetadata Command = 6 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // Service is the type of the git request like git-upload-pack or + // git-receive-pack. + string service = 8 [(gogoproto.jsontag) = "service"]; + // Path is the Git repo path, usually /. + string path = 9 [(gogoproto.jsontag) = "path"]; + + // Actions defines details for a Git push. + repeated GitCommandAction actions = 10 [(gogoproto.jsontag) = "actions,omitempty"]; +} + +// GitCommandAction defines details for a Git push. +message GitCommandAction { + // Action type like create or update. + string Action = 1 [(gogoproto.jsontag) = "action,omitempty"]; + // Reference name like ref/main/my_branch. + string Reference = 2 [(gogoproto.jsontag) = "reference,omitempty"]; + // Old is the old hash. + string Old = 3 [(gogoproto.jsontag) = "old,omitempty"]; + // New is the new hash. + string New = 4 [(gogoproto.jsontag) = "new,omitempty"]; +} + +// AccessListInvalidMetadata contains metadata about invalid access lists. +message AccessListInvalidMetadata { + // AccessListName is the name of the invalid access list. + string AccessListName = 1 [(gogoproto.jsontag) = "access_list_name, omitempty"]; + // User is the username of the access list member who attempted to log in. + string User = 2 [(gogoproto.jsontag) = "user,omitempty"]; + // MissingRoles are the names of the non-existent roles being referenced by the access list, causing it to be invalid. + repeated string MissingRoles = 3 [(gogoproto.jsontag) = "missing_roles,omitempty"]; +} + +// UserLoginAccessListInvalid is emitted when a user who is a member of an invalid +// access list logs in. It is used to indicate that the access list could not be +// applied to the user's session. +message UserLoginAccessListInvalid { + // Metadata is common event metadata + Metadata Metadata = 1 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // AccessListInvalidMetadata is the metadata for this access list invalid event. + AccessListInvalidMetadata AccessListInvalidMetadata = 2 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; + + // Status contains fields to indicate whether attempt was successful or not. + Status Status = 3 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; +} diff --git a/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/types/trusted_device_requirement.proto b/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/types/trusted_device_requirement.proto new file mode 100644 index 0000000000000..9f074c9e76465 --- /dev/null +++ b/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/types/trusted_device_requirement.proto @@ -0,0 +1,37 @@ +// Copyright 2024 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. + +syntax = "proto3"; + +package types; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/gravitational/teleport/api/types"; +option (gogoproto.goproto_getters_all) = false; +option (gogoproto.marshaler_all) = true; +option (gogoproto.unmarshaler_all) = true; + +// TrustedDeviceRequirement indicates whether access may be hindered by the lack +// of a trusted device. +enum TrustedDeviceRequirement { + // Device requirement not determined. + // Does not mean that a device is not required, only that the necessary data + // was not considered. + TRUSTED_DEVICE_REQUIREMENT_UNSPECIFIED = 0; + // Trusted device not required. + TRUSTED_DEVICE_REQUIREMENT_NOT_REQUIRED = 1; + // Trusted device required by either cluster mode or user roles. + TRUSTED_DEVICE_REQUIREMENT_REQUIRED = 2; +} diff --git a/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/types/types.proto b/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/types/types.proto index 7739ecad6c7a0..f241f6501956e 100644 --- a/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/types/types.proto +++ b/integrations/operator/crdgen/testdata/protofiles/teleport/legacy/types/types.proto @@ -21,6 +21,7 @@ import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; import "google/protobuf/wrappers.proto"; import "teleport/attestation/v1/attestation.proto"; +import "teleport/legacy/types/trusted_device_requirement.proto"; import "teleport/legacy/types/wrappers/wrappers.proto"; option go_package = "github.com/gravitational/teleport/api/types"; @@ -711,6 +712,31 @@ message InstanceSpecV1 { // ExternalUpgraderVersion identifies the external upgrader version. Empty if no upgrader is defined. string ExternalUpgraderVersion = 8 [(gogoproto.jsontag) = "ext_upgrader_version,omitempty"]; + + // LastMeasurement stores information about the latest measurement between services. + SystemClockMeasurement LastMeasurement = 9; +} + +// SystemClockMeasurement represents the measurement state of the systems clock difference. +message SystemClockMeasurement { + // ControllerSystemClock is the system clock of the inventory controller. + google.protobuf.Timestamp ControllerSystemClock = 1 [ + (gogoproto.stdtime) = true, + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "controller_system_clock,omitempty" + ]; + // SystemClock is the system clock of the upstream. + google.protobuf.Timestamp SystemClock = 2 [ + (gogoproto.stdtime) = true, + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "system_clock,omitempty" + ]; + // RequestDuration stores information about the request duration between auth and remote service. + google.protobuf.Duration RequestDuration = 3 [ + (gogoproto.jsontag) = "request_duration", + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true + ]; } // InstanceControlLogEntry represents an entry in a given instance's control log. The control log of @@ -842,6 +868,9 @@ message ServerSpecV2 { // CloudMetadata contains info about the cloud instance the server is running // on, if any. CloudMetadata CloudMetadata = 14 [(gogoproto.jsontag) = "cloud_metadata,omitempty"]; + // GitHub contains info about GitHub proxies where each server represents a + // GitHub organization. + GitHubServerMetadata git_hub = 15 [(gogoproto.jsontag) = "github,omitempty"]; reserved 8; reserved 10; @@ -875,6 +904,15 @@ message CloudMetadata { AWSInfo AWS = 1 [(gogoproto.jsontag) = "aws,omitempty"]; } +// GitHubServerMetadata contains info about GitHub proxies where each server +// represents a GitHub organization. +message GitHubServerMetadata { + // Organization specifies the name of the organization for the GitHub integration. + string organization = 1 [(gogoproto.jsontag) = "organization,omitempty"]; + // Integration is the integration that is associated with this Server. + string integration = 2 [(gogoproto.jsontag) = "integration,omitempty"]; +} + // AppServerV3 represents a single proxied web app. message AppServerV3 { option (gogoproto.goproto_stringer) = false; @@ -971,6 +1009,10 @@ message IdentityCenterPermissionSet { // Name is the human-readable name of the Permission Set. string Name = 2 [(gogoproto.jsontag) = "name,omitempty"]; + + // AssignmentID is the ID of the Teelport Account Assignment resource that + // represents this permission being assigned on the enclosing Account. + string AssignmentID = 3 [(gogoproto.jsontag) = "assignment_name,omitempty"]; } // AppIdentityCenter encapsulates information about an AWS Identity Center @@ -1016,6 +1058,11 @@ message AppSpecV3 { // IdentityCenter encasulates AWS identity-center specific information. Only // valid for Identity Center account apps. AppIdentityCenter IdentityCenter = 12 [(gogoproto.jsontag) = "identity_center,omitempty"]; + // TCPPorts is a list of ports and port ranges that an app agent can forward connections to. + // Only applicable to TCP App Access. + // If this field is not empty, URI is expected to contain no port number and start with the tcp + // protocol. + repeated PortRange TCPPorts = 13 [(gogoproto.jsontag) = "tcp_ports,omitempty"]; } // AppServerOrSAMLIdPServiceProviderV1 holds either an AppServerV3 or a SAMLIdPServiceProviderV1 resource (never both). @@ -1057,6 +1104,20 @@ message Header { string Value = 2 [(gogoproto.jsontag) = "value"]; } +// PortRange describes a port range for TCP apps. The range starts with Port and ends with EndPort. +// PortRange can be used to describe a single port in which case the Port field is the port and the +// EndPort field is 0. +message PortRange { + option (gogoproto.goproto_stringer) = false; + option (gogoproto.stringer) = false; + // Port describes the start of the range. It must be between 1 and 65535. + uint32 Port = 1 [(gogoproto.jsontag) = "port"]; + // EndPort describes the end of the range, inclusive. If set, it must be between 2 and 65535 and + // be greater than Port when describing a port range. When omitted or set to zero, it signifies + // that the port range defines a single port. + uint32 EndPort = 2 [(gogoproto.jsontag) = "end_port,omitempty"]; +} + // CommandLabelV2 is a label that has a value as a result of the // output generated by running command, e.g. hostname message CommandLabelV2 { @@ -2668,6 +2729,13 @@ message AccessRequestSpecV3 { (gogoproto.nullable) = true, (gogoproto.jsontag) = "assume_start_time,omitempty" ]; + + // ResourceExpiry is the time at which the access request resource will expire. + google.protobuf.Timestamp ResourceExpiry = 22 [ + (gogoproto.stdtime) = true, + (gogoproto.nullable) = true, + (gogoproto.jsontag) = "expiry,omitempty" + ]; } enum AccessRequestScope { @@ -2759,6 +2827,7 @@ message RequestKubernetesResource { } // ResourceID is a unique identifier for a teleport resource. +// Must be kept in sync with teleport.decision.v1alpha1.ResourceId. message ResourceID { // ClusterName is the name of the cluster the resource is in. string ClusterName = 1 [(gogoproto.jsontag) = "cluster"]; @@ -3372,6 +3441,24 @@ message RoleConditions { (gogoproto.nullable) = false, (gogoproto.jsontag) = "account_assignments,omitempty" ]; + + // GitHubPermissions defines GitHub integration related permissions. + repeated GitHubPermission git_hub_permissions = 43 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "github_permissions,omitempty" + ]; + + // WorkloadIdentityLabels controls whether or not specific WorkloadIdentity + // resources can be invoked. Further authorization controls exist on the + // WorkloadIdentity resource itself. + wrappers.LabelValues WorkloadIdentityLabels = 44 [ + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "workload_identity_labels,omitempty", + (gogoproto.customtype) = "Labels" + ]; + // WorkloadIdentityLabelsExpression is a predicate expression used to + // allow/deny access to issuing a WorkloadIdentity. + string WorkloadIdentityLabelsExpression = 45 [(gogoproto.jsontag) = "workload_identity_labels_expression,omitempty"]; } // IdentityCenterAccountAssignment captures an AWS Identity Center account @@ -3381,6 +3468,11 @@ message IdentityCenterAccountAssignment { string Account = 2 [(gogoproto.jsontag) = "account,omitempty"]; } +// GitHubPermission defines GitHub integration related permissions. +message GitHubPermission { + repeated string organizations = 1 [(gogoproto.jsontag) = "orgs,omitempty"]; +} + // SPIFFERoleCondition sets out which SPIFFE identities this role is allowed or // denied to generate. The Path matcher is required, and is evaluated first. If, // the Path does not match then the other matcher fields are not evaluated. @@ -3803,6 +3895,10 @@ message ExternalIdentity { // SAMLSingleLogoutURL is the SAML Single log-out URL to initiate SAML SLO (single log-out), if applicable. string SAMLSingleLogoutURL = 3 [(gogoproto.jsontag) = "samlSingleLogoutUrl,omitempty"]; + + // UserID is the ID of the identity. Some connectors like GitHub have an + // unique ID apart from the username. + string UserID = 4 [(gogoproto.jsontag) = "user_id,omitempty"]; } // LoginStatus is a login status of the user @@ -4246,19 +4342,6 @@ message WebSessionSpecV2 { bytes TLSPriv = 15 [(gogoproto.jsontag) = "tls_priv,omitempty"]; } -// TrustedDeviceRequirement indicates whether access may be hindered by the lack -// of a trusted device. -enum TrustedDeviceRequirement { - // Device requirement not determined. - // Does not mean that a device is not required, only that the necessary data - // was not considered. - TRUSTED_DEVICE_REQUIREMENT_UNSPECIFIED = 0; - // Trusted device not required. - TRUSTED_DEVICE_REQUIREMENT_NOT_REQUIRED = 1; - // Trusted device required by either cluster mode or user roles. - TRUSTED_DEVICE_REQUIREMENT_REQUIRED = 2; -} - // Web-focused view of teleport.devicetrust.v1.DeviceWebToken. message DeviceWebToken { // Opaque token identifier. @@ -5193,7 +5276,7 @@ message GithubAuthRequest { string KubernetesCluster = 13 [(gogoproto.jsontag) = "kubernetes_cluster,omitempty"]; // SSOTestFlow indicates if the request is part of the test flow. bool SSOTestFlow = 14 [(gogoproto.jsontag) = "sso_test_flow"]; - // ConnectorSpec is embedded connector spec for use in test flow. + // ConnectorSpec is embedded connector spec for use in test flow or authenticated user flow. GithubConnectorSpecV3 ConnectorSpec = 15 [(gogoproto.jsontag) = "connector_spec,omitempty"]; // AttestationStatement is an attestation statement for the given public key. // @@ -5217,6 +5300,10 @@ message GithubAuthRequest { teleport.attestation.v1.AttestationStatement ssh_attestation_statement = 21 [(gogoproto.jsontag) = "ssh_attestation_statement,omitempty"]; // TlsAttestationStatement is an attestation statement for the given TLS public key. teleport.attestation.v1.AttestationStatement tls_attestation_statement = 22 [(gogoproto.jsontag) = "tls_attestation_statement,omitempty"]; + // AuthenticatedUser is the username of an authenticated Teleport user. This + // OAuth flow is used to retrieve GitHub identity info which will be added to + // the existing user. + string authenticated_user = 23 [(gogoproto.jsontag) = "authenticated_user,omitempty"]; } // SSOWarnings conveys a user-facing main message along with auxiliary warnings. @@ -5387,6 +5474,12 @@ message GithubClaims { // Teams is the users team membership repeated string Teams = 3 [(gogoproto.jsontag) = "teams"]; + + // UserID is a global unique integer that is assigned to each GitHub user. The + // user ID is immutable (unlike the GitHub username) and can be found in APIs + // like get user. + // https://docs.github.com/en/rest/users/users + string UserID = 4 [(gogoproto.jsontag) = "user_id,omitempty"]; } // TeamMapping represents a single team membership mapping. @@ -6384,6 +6477,8 @@ message PluginSpecV1 { PluginEmailSettings email = 17; // Settings for the Microsoft Teams plugin PluginMSTeamsSettings msteams = 18; + // Settings for the OpenTex NetIQ plugin. + PluginNetIQSettings net_iq = 19; } // generation contains a unique ID that should: @@ -6810,6 +6905,18 @@ message PluginMSTeamsSettings { string default_recipient = 5; } +// PluginNetIQSettings defines the settings for a NetIQ integration plugin +message PluginNetIQSettings { + option (gogoproto.equal) = true; + // oauth_issuer_endpoint is the NetIQ Oauth Issuer endpoint. + // Usually, it's equal to https://osp.domain.ext/a/idm/auth/oauth2 + string oauth_issuer_endpoint = 1; + // api_endpoint is the IDM PROV Rest API location. + string api_endpoint = 2; + // insecure_skip_verify controls whether the NetIQ certificate validation should be skipped. + bool insecure_skip_verify = 3; +} + message PluginBootstrapCredentialsV1 { oneof credentials { PluginOAuth2AuthorizationCodeCredentials oauth2_authorization_code = 1; @@ -6848,6 +6955,8 @@ message PluginStatusV1 { PluginOktaStatusV1 okta = 7; // AWSIC holds status details for the AWS Identity Center plugin. PluginAWSICStatusV1 aws_ic = 8; + // NetIQ holds status details for the NetIQ plugin. + PluginNetIQStatusV1 net_iq = 9; } // last_raw_error variable stores the most recent raw error message received from an API or service. @@ -6857,6 +6966,18 @@ message PluginStatusV1 { string last_raw_error = 6; } +// PluginNetIQStatusV1 is the status details for the NetIQ plugin. +message PluginNetIQStatusV1 { + // imported_users is the number of users imported from NetIQ eDirectory. + uint32 imported_users = 1; + // imported_groups is the number of groups imported from NetIQ eDirectory. + uint32 imported_groups = 2; + // imported_roles is the number of roles imported from NetIQ eDirectory. + uint32 imported_roles = 3; + // imported_resources is the number of resources imported from NetIQ eDirectory. + uint32 imported_resources = 4; +} + // PluginGitlabStatusV1 is the status details for the Gitlab plugin. message PluginGitlabStatusV1 { // imported_users is the number of users imported from Gitlab. @@ -7119,6 +7240,7 @@ message PluginStaticCredentialsSpecV1 { string APIToken = 1; PluginStaticCredentialsBasicAuth BasicAuth = 2; PluginStaticCredentialsOAuthClientSecret OAuthClientSecret = 3; + PluginStaticCredentialsSSHCertAuthorities SSHCertAuthorities = 4; } } @@ -7140,6 +7262,14 @@ message PluginStaticCredentialsOAuthClientSecret { string ClientSecret = 2 [(gogoproto.jsontag) = "client_secret"]; } +// PluginStaticCredentialsSSHCertAuthorities contains the active SSH CAs used +// for the integration or plugin. +message PluginStaticCredentialsSSHCertAuthorities { + // CertAuthorities contains the active SSH CAs used for the integration or + // plugin. + repeated SSHKeyPair cert_authorities = 1; +} + // SAMLIdPServiceProviderV1 is the representation of a SAML IdP service provider. message SAMLIdPServiceProviderV1 { option (gogoproto.goproto_stringer) = false; @@ -7487,7 +7617,12 @@ message IntegrationSpecV1 { AWSOIDCIntegrationSpecV1 AWSOIDC = 1 [(gogoproto.jsontag) = "aws_oidc,omitempty"]; // AzureOIDC contains the specific fields to handle the Azure OIDC Integration subkind AzureOIDCIntegrationSpecV1 AzureOIDC = 2 [(gogoproto.jsontag) = "azure_oidc,omitempty"]; + // GitHub contains the specific fields to handle the GitHub integration subkind. + GitHubIntegrationSpecV1 GitHub = 3 [(gogoproto.jsontag) = "github,omitempty"]; } + + // Credentials contains credentials for the integration. + PluginCredentialsV1 credentials = 4; } // AWSOIDCIntegrationSpecV1 contains the spec properties for the AWS OIDC SubKind Integration. @@ -7532,6 +7667,12 @@ message AzureOIDCIntegrationSpecV1 { string ClientID = 2 [(gogoproto.jsontag) = "client_id,omitempty"]; } +// GitHubIntegrationSpecV1 contains the specific fields to handle the GitHub integration subkind. +message GitHubIntegrationSpecV1 { + // Organization specifies the name of the organization for the GitHub integration. + string Organization = 1 [(gogoproto.jsontag) = "organization,omitempty"]; +} + // HeadlessAuthentication holds data for an ongoing headless authentication attempt. message HeadlessAuthentication { // Header is the resource header. @@ -7920,12 +8061,14 @@ message OktaOptions { message AccessGraphSync { // AWS is a configuration for AWS Access Graph service poll service. repeated AccessGraphAWSSync AWS = 1 [(gogoproto.jsontag) = "aws,omitempty"]; - // PollInterval is the frequency at which to poll for AWS resources + // PollInterval is the frequency at which to poll for resources google.protobuf.Duration PollInterval = 2 [ (gogoproto.jsontag) = "poll_interval,omitempty", (gogoproto.nullable) = false, (gogoproto.stdduration) = true ]; + // Azure is a configuration for Azure Access Graph service poll service. + repeated AccessGraphAzureSync Azure = 3 [(gogoproto.jsontag) = "azure,omitempty"]; } // AccessGraphAWSSync is a configuration for AWS Access Graph service poll service. @@ -7937,3 +8080,11 @@ message AccessGraphAWSSync { // Integration is the integration name used to generate credentials to interact with AWS APIs. string Integration = 4 [(gogoproto.jsontag) = "integration,omitempty"]; } + +// AccessGraphAzureSync is a configuration for Azure Access Graph service poll service. +message AccessGraphAzureSync { + // SubscriptionID Is the ID of the Azure subscription to sync resources from + string SubscriptionID = 1 [(gogoproto.jsontag) = "subscription_id,omitempty"]; + // Integration is the integration name used to generate credentials to interact with AWS APIs. + string Integration = 2 [(gogoproto.jsontag) = "integration,omitempty"]; +} diff --git a/integrations/operator/hack/fixture-operator-role.yaml b/integrations/operator/hack/fixture-operator-role.yaml index e9925b19a106c..ac6e88a6dfbd1 100644 --- a/integrations/operator/hack/fixture-operator-role.yaml +++ b/integrations/operator/hack/fixture-operator-role.yaml @@ -73,5 +73,13 @@ spec: - read - update - delete + - resources: + - trusted_cluster + verbs: + - list + - create + - read + - update + - delete deny: {} version: v7 diff --git a/lib/auth/trustedcluster.go b/lib/auth/trustedcluster.go index a02e8f4b74de6..c6a11a6d5e5db 100644 --- a/lib/auth/trustedcluster.go +++ b/lib/auth/trustedcluster.go @@ -84,7 +84,6 @@ func (a *Server) UpdateTrustedCluster(ctx context.Context, tc types.TrustedClust if err != nil { return nil, trace.Wrap(err) } - updated, err := a.updateTrustedCluster(ctx, tc, existingCluster) return updated, trace.Wrap(err) }