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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 26 additions & 9 deletions api/v1/clusterextension_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,19 @@ const (
// ClusterExtensionSpec defines the desired state of ClusterExtension
type ClusterExtensionSpec struct {
// namespace is a reference to a Kubernetes namespace.
// This is the namespace in which the provided ServiceAccount must exist.
// It also designates the default namespace where namespace-scoped resources
//
// This designates the default namespace where namespace-scoped resources
// for the extension are applied to the cluster.
// Some extensions may contain namespace-scoped resources to be applied in other namespaces.
// This namespace must exist.
//
// <opcon:standard:description>
// This is also the namespace of the referenced service account.
// </opcon:standard:description>
// <opcon:experimental:description>
// This is also the namespace of the referenced service account, if specified.
// </opcon:experimental:description>
//
// namespace is required, immutable, and follows the DNS label standard
// as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters or hyphens (-),
// start and end with an alphanumeric character, and be no longer than 63 characters
Expand All @@ -69,12 +76,24 @@ type ClusterExtensionSpec struct {

// serviceAccount is a reference to a ServiceAccount used to perform all interactions
// with the cluster that are required to manage the extension.
// <opcon:standard:description>
// The ServiceAccount must be configured with the necessary permissions to perform these interactions.
// The ServiceAccount must exist in the namespace referenced in the spec.
//
// serviceAccount is required.
// </opcon:standard:description>
//
// +kubebuilder:validation:Required
ServiceAccount ServiceAccountReference `json:"serviceAccount"`
// <opcon:experimental:description>
// If serviceAccount is specified, OLM will authenticate as that service account.
// Otherwise, operator-controller will authenticate as:
// - User: "olm:clusterextension:<clusterExtensionName>"
// - Group: "olm:clusterextensions"
//
// The authenticated user must be configured with the necessary permissions to perform these interactions.
// </opcon:experimental:description>
//
// <opcon:standard:validation:Required>
// <opcon:experimental:validation:Optional>
ServiceAccount ServiceAccountReference `json:"serviceAccount,omitzero"`

// source is a required field which selects the installation source of content
// for this ClusterExtension. Selection is performed by setting the sourceType.
Expand Down Expand Up @@ -374,14 +393,12 @@ type CatalogFilter struct {
UpgradeConstraintPolicy UpgradeConstraintPolicy `json:"upgradeConstraintPolicy,omitempty"`
}

// ServiceAccountReference identifies the serviceAccount used fo install a ClusterExtension.
// ServiceAccountReference identifies the serviceAccount name used to manage a ClusterExtension.
type ServiceAccountReference struct {
// name is a required, immutable reference to the name of the ServiceAccount
// to be used for installation and management of the content for the package
// specified in the packageName field.
//
// This ServiceAccount must exist in the installNamespace.
//
// name follows the DNS subdomain standard as defined in [RFC 1123].
// It must contain only lowercase alphanumeric characters,
// hyphens (-) or periods (.), start and end with an alphanumeric character,
Expand All @@ -404,7 +421,7 @@ type ServiceAccountReference struct {
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="name is immutable"
// +kubebuilder:validation:XValidation:rule="self.matches(\"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\")",message="name must be a valid DNS1123 subdomain. It must contain only lowercase alphanumeric characters, hyphens (-) or periods (.), start and end with an alphanumeric character, and be no longer than 253 characters"
// +kubebuilder:validation:Required
Name string `json:"name"`
Name string `json:"name,omitempty"`
}

// PreflightConfig holds the configuration for the preflight checks. If used, at least one preflight check must be non-nil.
Expand Down
15 changes: 12 additions & 3 deletions cmd/operator-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
k8slabels "k8s.io/apimachinery/pkg/labels"
k8stypes "k8s.io/apimachinery/pkg/types"
apimachineryrand "k8s.io/apimachinery/pkg/util/rand"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/client-go/discovery"
"k8s.io/client-go/discovery/cached/memory"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
Expand Down Expand Up @@ -643,8 +644,7 @@ func setupHelm(
if err != nil {
return fmt.Errorf("unable to create core client: %w", err)
}
tokenGetter := authentication.NewTokenGetter(coreClient, authentication.WithExpirationDuration(1*time.Hour))
clientRestConfigMapper := action.ServiceAccountRestConfigMapper(tokenGetter)
clientRestConfigMapper := action.ServiceAccountRestConfigMapper()
if features.OperatorControllerFeatureGate.Enabled(features.SyntheticPermissions) {
clientRestConfigMapper = action.SyntheticUserRestConfigMapper(clientRestConfigMapper)
}
Expand All @@ -671,7 +671,16 @@ func setupHelm(
// determine if PreAuthorizer should be enabled based on feature gate
var preAuth authorization.PreAuthorizer
if features.OperatorControllerFeatureGate.Enabled(features.PreflightPermissions) {
preAuth = authorization.NewRBACPreAuthorizer(mgr.GetClient())
preAuth = authorization.NewRBACPreAuthorizer(mgr.GetClient(), func(ext *ocv1.ClusterExtension) (*user.DefaultInfo, error) {
if ext.Spec.ServiceAccount.Name == "" && features.OperatorControllerFeatureGate.Enabled(features.SyntheticPermissions) {
syntheticConfig := authentication.SyntheticImpersonationConfig(*ext)
return &user.DefaultInfo{Name: syntheticConfig.UserName, Groups: syntheticConfig.Groups}, nil
} else if ext.Spec.ServiceAccount.Name == "" || ext.Spec.Namespace == "" {
return nil, errors.New("service account name and namespace must be specified")
}
saConfig := authentication.ServiceAccountImpersonationConfig(*ext)
return &user.DefaultInfo{Name: saConfig.UserName, Groups: saConfig.Groups}, nil
})
}

cm := contentmanager.NewManager(clientRestConfigMapper, mgr.GetConfig(), mgr.GetRESTMapper())
Expand Down
12 changes: 7 additions & 5 deletions config/samples/olm_v1_clusterextension.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ kind: Namespace
metadata:
name: argocd
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: argocd-installer
namespace: argocd
# NOTE: The ServiceAccount resource is intentionally NOT created here.
# OLM v1 uses Kubernetes impersonation and does not require the ServiceAccount to exist.
# For security reasons, you should NOT create a ServiceAccount resource for the installer,
# as it would be highly privileged and could be mounted by other pods in the namespace.
# Instead, only the RBAC resources (ClusterRole, ClusterRoleBinding, Role, RoleBinding)
# are created, which reference the ServiceAccount name "argocd-installer".
# OLM will impersonate that ServiceAccount and be subject to these RBAC permissions.
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
Expand Down
8 changes: 4 additions & 4 deletions docs/api-reference/olmv1-api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,8 @@ _Appears in:_

| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `namespace` _string_ | namespace is a reference to a Kubernetes namespace.<br />This is the namespace in which the provided ServiceAccount must exist.<br />It also designates the default namespace where namespace-scoped resources<br />for the extension are applied to the cluster.<br />Some extensions may contain namespace-scoped resources to be applied in other namespaces.<br />This namespace must exist.<br />namespace is required, immutable, and follows the DNS label standard<br />as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters or hyphens (-),<br />start and end with an alphanumeric character, and be no longer than 63 characters<br />[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | MaxLength: 63 <br />Required: \{\} <br /> |
| `serviceAccount` _[ServiceAccountReference](#serviceaccountreference)_ | serviceAccount is a reference to a ServiceAccount used to perform all interactions<br />with the cluster that are required to manage the extension.<br />The ServiceAccount must be configured with the necessary permissions to perform these interactions.<br />The ServiceAccount must exist in the namespace referenced in the spec.<br />serviceAccount is required. | | Required: \{\} <br /> |
| `namespace` _string_ | namespace is a reference to a Kubernetes namespace.<br />This designates the default namespace where namespace-scoped resources<br />for the extension are applied to the cluster.<br />Some extensions may contain namespace-scoped resources to be applied in other namespaces.<br />This namespace must exist.<br /><opcon:standard:description><br />This is also the namespace of the referenced service account.<br /></opcon:standard:description><br /><opcon:experimental:description><br />This is also the namespace of the referenced service account, if specified.<br /></opcon:experimental:description><br />namespace is required, immutable, and follows the DNS label standard<br />as defined in [RFC 1123]. It must contain only lowercase alphanumeric characters or hyphens (-),<br />start and end with an alphanumeric character, and be no longer than 63 characters<br />[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | MaxLength: 63 <br />Required: \{\} <br /> |
| `serviceAccount` _[ServiceAccountReference](#serviceaccountreference)_ | serviceAccount is a reference to a ServiceAccount used to perform all interactions<br />with the cluster that are required to manage the extension.<br /><opcon:standard:description><br />The ServiceAccount must be configured with the necessary permissions to perform these interactions.<br />serviceAccount is required.<br /></opcon:standard:description><br /><opcon:experimental:description><br />If serviceAccount is specified, OLM will authenticate as that service account.<br />Otherwise, operator-controller will authenticate as:<br /> - User: "olm:clusterextension:<clusterExtensionName>"<br /> - Group: "olm:clusterextensions"<br />The authenticated user must be configured with the necessary permissions to perform these interactions.<br /></opcon:experimental:description><br /><opcon:standard:validation:Required><br /><opcon:experimental:validation:Optional> | | |
| `source` _[SourceConfig](#sourceconfig)_ | source is a required field which selects the installation source of content<br />for this ClusterExtension. Selection is performed by setting the sourceType.<br />Catalog is currently the only implemented sourceType, and setting the<br />sourcetype to "Catalog" requires the catalog field to also be defined.<br />Below is a minimal example of a source definition (in yaml):<br />source:<br /> sourceType: Catalog<br /> catalog:<br /> packageName: example-package | | Required: \{\} <br /> |
| `install` _[ClusterExtensionInstallConfig](#clusterextensioninstallconfig)_ | install is an optional field used to configure the installation options<br />for the ClusterExtension such as the pre-flight check configuration. | | |
| `config` _[ClusterExtensionConfig](#clusterextensionconfig)_ | config is an optional field used to specify bundle specific configuration<br />used to configure the bundle. Configuration is bundle specific and a bundle may provide<br />a configuration schema. When not specified, the default configuration of the resolved bundle will be used.<br />config is validated against a configuration schema provided by the resolved bundle. If the bundle does not provide<br />a configuration schema the final manifests will be derived on a best-effort basis. More information on how<br />to configure the bundle should be found in its end-user documentation.<br /><opcon:experimental> | | |
Expand Down Expand Up @@ -439,7 +439,7 @@ _Appears in:_



ServiceAccountReference identifies the serviceAccount used fo install a ClusterExtension.
ServiceAccountReference identifies the serviceAccount name used to manage a ClusterExtension.



Expand All @@ -448,7 +448,7 @@ _Appears in:_

| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `name` _string_ | name is a required, immutable reference to the name of the ServiceAccount<br />to be used for installation and management of the content for the package<br />specified in the packageName field.<br />This ServiceAccount must exist in the installNamespace.<br />name follows the DNS subdomain standard as defined in [RFC 1123].<br />It must contain only lowercase alphanumeric characters,<br />hyphens (-) or periods (.), start and end with an alphanumeric character,<br />and be no longer than 253 characters.<br />Some examples of valid values are:<br /> - some-serviceaccount<br /> - 123-serviceaccount<br /> - 1-serviceaccount-2<br /> - someserviceaccount<br /> - some.serviceaccount<br />Some examples of invalid values are:<br /> - -some-serviceaccount<br /> - some-serviceaccount-<br />[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | MaxLength: 253 <br />Required: \{\} <br /> |
| `name` _string_ | name is a required, immutable reference to the name of the ServiceAccount<br />to be used for installation and management of the content for the package<br />specified in the packageName field.<br />name follows the DNS subdomain standard as defined in [RFC 1123].<br />It must contain only lowercase alphanumeric characters,<br />hyphens (-) or periods (.), start and end with an alphanumeric character,<br />and be no longer than 253 characters.<br />Some examples of valid values are:<br /> - some-serviceaccount<br /> - 123-serviceaccount<br /> - 1-serviceaccount-2<br /> - someserviceaccount<br /> - some.serviceaccount<br />Some examples of invalid values are:<br /> - -some-serviceaccount<br /> - some-serviceaccount-<br />[RFC 1123]: https://tools.ietf.org/html/rfc1123 | | MaxLength: 253 <br />Required: \{\} <br /> |


#### SourceConfig
Expand Down
13 changes: 7 additions & 6 deletions docs/draft/howto/use-synthetic-permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Synthetic user permissions enables fine-grained configuration of ClusterExtensio
User can not only configure RBAC permissions governing the management across all ClusterExtensions, but also on a
case-by-case basis.

### Run OLM v1with Experimental Features Enabled
### Run OLM v1 with Experimental Features Enabled

```terminal title=Enable Experimental Features in a New Kind Cluster
make run-experimental
Expand All @@ -20,10 +20,11 @@ kubectl rollout status -n olmv1-system deployment/operator-controller-controller

### How does it work?

When managing a ClusterExtension, OLM will assume the identity of user "olm:clusterextensions:<clusterextension-name>"
and group "olm:clusterextensions" limiting Kubernetes API access scope to those defined for this user and group. These
users and group do not exist beyond being defined in Cluster/RoleBinding(s) and can only be impersonated by clients with
`impersonate` verb permissions on the `users` and `groups` resources.
When managing a ClusterExtension that does not specify a service account, OLM will assume the identity of user
"olm:clusterextension:<clusterextension-name>" and group "olm:clusterextensions" limiting Kubernetes API access scope
to those defined for this user and group. These users and group do not exist beyond being defined in
Cluster/RoleBinding(s) and can only be impersonated by clients with `impersonate` verb permissions on the `users` and
`groups` resources.

### Demo

Expand All @@ -50,7 +51,7 @@ subjects:
name: "olm:clusterextensions"
```

##### Scoped olm:clusterextension group + Added perms on specific extensions
##### Scoped olm:clusterextensions group + Added perms on specific extensions

Give ClusterExtension management group broad permissions to manage ClusterExtensions denying potentially dangerous
permissions such as being able to read cluster wide secrets:
Expand Down
20 changes: 14 additions & 6 deletions docs/howto/derive-service-account.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,12 +300,20 @@ Therefore, the following permissions must be given to the installer service acco
Once the installer service account required cluster-scoped and namespace-scoped permissions have been collected:

1. Create the installation namespace
2. Create the installer `ServiceAccount`
3. Create the installer `ClusterRole`
4. Create the `ClusterRoleBinding` between the installer service account and its cluster role
5. Create the installer `Role`
6. Create the `RoleBinding` between the installer service account and its role
7. Create the `ClusterExtension`
2. Create the installer `ClusterRole`
3. Create the `ClusterRoleBinding` between the installer service account and its cluster role
4. Create the installer `Role`
5. Create the `RoleBinding` between the installer service account and its role
6. Create the `ClusterExtension`

!!! important "Do Not Create the ServiceAccount Resource"
OLM v1 uses Kubernetes impersonation and does not require the ServiceAccount to exist as an actual resource.
For security reasons, you should **NOT** create a ServiceAccount resource for the installer,
as it would be highly privileged and could be mounted by other pods in the namespace.

Instead, only create the RBAC resources (ClusterRole, ClusterRoleBinding, Role, RoleBinding)
that reference the ServiceAccount name. OLM will impersonate that ServiceAccount and be subject
to these RBAC permissions without the ServiceAccount resource actually existing in the cluster.

A manifest with the full set of resources can be found [here](https://github.com/operator-framework/operator-controller/blob/main/config/samples/olm_v1_clusterextension.yaml).

Expand Down
8 changes: 1 addition & 7 deletions docs/tutorials/upgrade-extension.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,6 @@ saving it to a local file and then running `kubectl apply -f FILENAME`:
metadata:
name: argocd
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: argocd-installer
namespace: argocd
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
Expand Down Expand Up @@ -376,4 +370,4 @@ kubectl get clusterextension argocd -o jsonpath-as-json="{.status.install}"
After your upgrade, the contents of the `kubectl.kubernetes.io/last-applied-configuration` annotation field will
differ depending on your method of upgrade. If you apply a new ClusterExtension manifest as in the first method shown,
the last applied configuration will show the new version since we replaced the existing manifest. If you use the patch
method or `kubectl edit clusterextension`, then the last applied configuration will show the old version.
method or `kubectl edit clusterextension`, then the last applied configuration will show the old version.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ metadata:
name: argocd-operator
spec:
namespace: argocd-system
serviceAccount:
name: "olm.synthetic-user"
source:
sourceType: Catalog
catalog:
Expand Down
2 changes: 1 addition & 1 deletion hack/demo/synthetic-user-cluster-admin-demo-script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ bat --style=plain ${DEMO_RESOURCE_DIR}/synthetic-user-perms/cegroup-admin-bindin
# apply cluster role binding
kubectl apply -f ${DEMO_RESOURCE_DIR}/synthetic-user-perms/cegroup-admin-binding.yaml

# install cluster extension - for now .spec.serviceAccount = "olm.synthetic-user"
# install cluster extension without specifying .spec.serviceAccount
bat --style=plain ${DEMO_RESOURCE_DIR}/synthetic-user-perms/argocd-clusterextension.yaml

# apply cluster extension
Expand Down
Loading
Loading