Skip to content

Commit

Permalink
Edit Authn-K8s namespace-label design
Browse files Browse the repository at this point in the history
  • Loading branch information
john-odonnell committed Jul 20, 2022
1 parent 1b45d3f commit aa3deda
Showing 1 changed file with 78 additions and 71 deletions.
149 changes: 78 additions & 71 deletions design/proposals/namespace_label_authenticator.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Solution Design - Namespace Label Selector for the Kubernetes Authenticator
# Solution Design - Namespace Label Identity Scope for the Kubernetes Authenticator
[//]: # "Change the title above from 'Template' to your design's title"

[//]: # "General notes:"
Expand All @@ -10,7 +10,7 @@
## Table of Contents
[//]: # "You can use this tool to generate a TOC - https://ecotrust-canada.github.io/markdown-toc/"

- [Solution Design - Namespace Label Selector for the Kubernetes Authenticator](#solution-design---namespace-label-selector-for-the-kubernetes-authenticator)
- [Solution Design - Namespace-Label Identity Scope for the Kubernetes Authenticator](#solution-design---namespace-label-identity-scope-for-the-kubernetes-authenticator)
* [Table of Contents](#table-of-contents)
* [Glossary](#glossary)
* [Useful Links](#useful-links)
Expand All @@ -32,9 +32,9 @@
* [Backwards Compatibility](#backwards-compatibility)
* [Affected Components](#affected-components)
* [Work in Parallel](#work-in-parallel)
+ [Development Tasks](#development-tasks)
* [Test Plan](#test-plan)
+ [Test Environments](#test-environments)
+ [Prerequisites](#prerequisites)
+ [Test Cases (Including Performance)](#test-cases--including-performance-)
- [Functional Tests](#functional-tests)
- [Security Tests](#security-tests)
Expand All @@ -49,6 +49,7 @@

<small><i><a href='http://ecotrust-canada.github.io/markdown-toc/'>Table of contents generated with markdown-toc</a></i></small>


## Glossary
[//]: # "Describe terms that will be used throughout the design"
[//]: # "You can use this tool to generate a table - https://www.tablesgenerator.com/markdown_tables#"
Expand All @@ -65,7 +66,8 @@
|-------------|----------|
| Feature Doc | |
| Issue | |
| POC branch | https://github.com/cyberark/conjur/compare/master...authn-k8s-label-selector |
| Kubernetes Docs: Labels and Selectors | https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ |
| Proof-of-Concept branch | https://github.com/cyberark/conjur/compare/master...authn-k8s-label-selector |

## Background
[//]: # "Give relevant background for the designed feature. What is the motivation for this solution?"
Expand Down Expand Up @@ -184,42 +186,20 @@ The minimum subset of label selector flavors we need to support are
an identity to scope to a single group of similarly-labeled namespaces.

Should this feature also support:
- **Multiple** positive equality-based selectors?

**Nice to have, but not required.** Multiple equality-based selectors can be
used to reduce scope from a single equality-based selector. An identity could
authenticate workloads in a subset of namespaces in a single group based on
additional labels.

- **Negative** selectors (`!=`, `notin`, and `!`)?

**No.** This feature should not support negative selectors, which would serve as a
denylist in this context. Theoretically, new workloads would be able to
authenticate by default, unless they reside in a denylisted namespace. An
identity's scope should be meaningfully curated, not open-ended.

- Positive **set-based** selectors (`in`)?

**No.** Set-based selectors can be used to increase scope from a single equality-based
selector. Support for this selector flavor would imply that a single host
identity could authenticate on behalf of any number of namespace groups. This
runs counter to having intentionally-scoped host identities - if two workloads
are in separate namespaces, and those namespaces are designated to different
sets, they should authenticate with separate identities.

- Positive **existence-based** selectors?

**No.** Existence based selectors filter results based on label keys, and does no
validation on label values. While this could technically be leveraged to mimic
positive equality-based selectors by using unique label keys, this would mean
thoroughly polluting the label namespace.

Under the hood, Kubernetes' native label selector implementation lists all
namespaces in a given cluster, then reduces the result scope to only those that
satisfy the condition. Using this implementation with the Kubernetes
Authenticator requires permitting its identity to `list` all namespaces in the
target cluster. This operation could be negatively affected if the target
cluster is host to so many namespaces that the query operation becomes costly.
| **Selector Type** | **Support?** | **Comments** |
|-------------------|--------------|--------------|
| **Multiple** positive equality-based selectors | **Nice to have, but not required** | Multiple equality-based selectors can be used to reduce scope from a single equality-based selector. An identity could authenticate workloads in a subset of namespaces in a single group based on additional labels. |
| **Negative** selectors (`!=`, `notin`, and `!`) | **No** | This feature should not support negative selectors, which would serve as a denylist in this context. Theoretically, new workloads would be able to authenticate by default, unless they reside in a denylisted namespace. An identity's scope should be meaningfully curated, not open-ended. |
| Positive **set-based** selectors (`in`) | **No** | Set-based selectors can be used to increase scope from a single equality-based selector. Support for this selector flavor would imply that a single host identity could authenticate on behalf of any number of namespace groups. This runs counter to having intentionally-scoped host identities - if two workloads are in separate namespaces, and those namespaces are designated to different sets, they should authenticate with separate identities. |
| Positive **existence-based** selectors | **No** | Existence based selectors filter results based on label keys, and does no validation on label values. While this could technically be leveraged to mimic positive equality-based selectors by using unique label keys, this would mean thoroughly polluting the label namespace. |

In Kubernetes, label selectors can only be applied to functions that list
instances of a resource type - for example, a client can query a list of
namespaces in a cluster, but limit the results to only those namespaces that
match a given selector. Using label selectors with the Kubernetes Authenticator
will require permitting its identity to `list` all namespaces in the target
cluster. This operation could be negatively affected if the target cluster is
host to so many namespaces that the list operation becomes costly.

```rb
# Retrieves all namespaces in the cluster, enforcing field selection based on
Expand Down Expand Up @@ -265,7 +245,7 @@ example:
- !host
id: test-app
annotations:
authn-k8s/namespace-label-selector: "conjur.org/authn-k8s-project=dev"
authn-k8s/namespace-label: "conjur.org/authn-k8s-project=dev"
authn-k8s/service-account: "test-app-sa"
```

Expand Down Expand Up @@ -311,7 +291,7 @@ policy. All other factors in Authenticator setup are unchanged.
- !host
id: test-app
annotations:
authn-k8s/namespace-label-selector: "key1=value1"
authn-k8s/namespace-label: "key1=value1"
```

Rancher creates a unique ID for Projects at creation. This ID needs to inform
Expand Down Expand Up @@ -341,7 +321,7 @@ This value then informs host configuration:
- !host
id: test-app
annotations:
authn-k8s/namespace-label-selector: "field.cattle.io/projectId=${PROJECT_ID}"
authn-k8s/namespace-label: "field.cattle.io/projectId=${PROJECT_ID}"
```

## Design
Expand Down Expand Up @@ -391,28 +371,43 @@ following modules:

### Development Tasks

- Implement and test a new Constraint, where exactly one of many restrictions is
required.
- Use the new Constraint to add a resource restriction to an AuthnK8s host,
where exactly one of either `namespace` or `namespace-label-selector` is
required.
- Implement namespace retrieval and label parsing to validate the new
restriction.
- Test the public interface with in-process K8s API mock: hosts configured with
the new restriction, when authenticating from a properly labeled namespace,
should be authenticated.
- [ ] Community and Integrations team ramp-up: current Kubernetes Authenticator
behavior, new Authn-K8s and existing project testing strategies, solution
details.
- [ ] Given the name of a namespace, retrieve its labels from the K8s API server.
- [ ] Given a map of a namespace's labels and a desired label selector, validate
that the label map adheres to the selector.
- [ ] Create and test a new `Authentication::Constraint` class that can enforce
logical XOR on two or more resource restrictions.
- [ ] Update Authn-K8s request validation logic to use the new `Constraint`
class to validate a request's origin namespace against a desired label.
- [ ] Test the new Authn-K8s request validation end-to-end, using either a mock
K8s API server or live infrastructure.
- [ ] [Spike]: How should we suggest customers secure their labels?
- [ ] Perform final security review.
- [ ] Assist TW with updating Kubernetes Authenticator documentation. This
should include:
1. Updates to the Kubernetes/OpenShift integration documentation. This should
include only general language about using labels to group namespaces and
assign identity scope.
2. Updates to the Rancher integration documentation. This can include
Rancher-specific language regarding scoping identities to Projects.
- [ ] Perform UX review by manually following the new documentation. This will
include creating a label-scoped identity, creating a labeled namespace,
and authenticating a workload using labels.

## Test Plan

### Test Environments
[//]: # "Including build number, platforms etc. Considering the OS and version of PAS (PVWA, CPM), Conjur, Synchronizer etc."

| **Feature** | **OS** | **Version Number** |
|-------------|--------|--------------------|
| | | |
End-to-end tests should be run against resources deployed in full-fledged
Kubernetes environments, to best replicate the typical user experience.

### Prerequisites
[//]: # "List any expected infrastructure requirements here"
| **Platform** | **Versions** |
|--------------|--------------|
| OpenShift | v4.8 & 9 |
| GKE | >= v1.21 |

While this solution is designed to easily extend to a Rancher use-case, Rancher
infrastructure is not required. Testing against labeled namespaces in standard
Expand All @@ -433,8 +428,8 @@ for infrastructure setup and long CI builds.

| **Title** | **Given** | **When** | **Then** | **Comment** |
|-----------|-----------|----------|----------|-------------|
| **Login Happy Path** | Given the Kubernetes Authenticator `${service_id}` is configured to validate namespaces with label `x=y` | When a certificate injection request is received from a namespace labeled `x=y` | The the Authenticator responds with a 202 status and injects the certificate | |
| **Authentication Happy Path** | Given the Kubernetes Authenticator `${service_id}` is configured to validate namespaces with label `x=y`` | When an authentication request is received from a namespace labeled `x=y` | Then the Authenticator responds with a 200 status and an access token | |
| **Login Happy Path** | Given the Kubernetes Authenticator `${service_id}` is configured to validate namespaces with label `x=y` | When a certificate injection request is received from a namespace labeled `x=y` | Then authentication succeeds with a 202 status, certificate injection, and [new log **4**](#logs) | |
| **Authentication Happy Path** | Given the Kubernetes Authenticator `${service_id}` is configured to validate namespaces with label `x=y` | When an authentication request is received from a namespace labeled `x=y` | Then authentication succeeds with a 200 status, an access token, and [new log **4**](#logs) | |

#### Security Tests

Expand All @@ -453,9 +448,10 @@ for infrastructure setup and long CI builds.

| **Title** | **Given** | **When** | **Then** | **Comment** |
|-----------|-----------|----------|----------|-------------|
| **Misconfigured Authenticator** | Given that a policy file defines a Kubernetes Authenticator instance with both `authn-k8s.[namespace\|namespace-label-selector]` annotations | When the identity authenticates with Conjur | Authentication should fail with a 401 response and an `Errors::Authentication::Constraints` log. | |
| **Misconfigured Identity** | Given that the Kubernetes Authenticator is configured to validate a namespace label, and its identity does not have permission to `get` namespaces in a cluster | When an authentication request is received | Then the request fails with a 500 response | |
| **Misconfigured Namespace** | Given that a Kubernetes Authenticator is configured to authenticate requests from namespaces with the label `x=y` | When a requesting namespace does not have the label `x=y` | Then the request fails with a 401 response | |
| **Misconfigured Authenticator** | Given that a policy file defines a Kubernetes Authenticator identity with both `authn-k8s.[namespace\|namespace-label]` annotations | When the identity authenticates with Conjur | Authentication should fail with a 401 response and [new log **0**](#logs). | |
| **Misconfigured Identity** | Given that the Kubernetes Authenticator is configured to validate a namespace label, and its identity does not have permission to `get` namespaces in a cluster | When an authentication request is received | Then the request fails with a 404 response and a KubeClient log indicating the namespace was not found | |
| **Misconfigured Namespace** | Given that a Kubernetes Authenticator is configured to authenticate requests from namespaces with the label `x=y` | When a requesting namespace does not have the label `x=y` | Then the request fails with a 401 response and [new log **1**](#logs) | |
| **Misconfigured Label** | Given that a policy file defines a Kubernetes Authenticator identity with the `authn-k8s/namespace-label` annotation, but its value does not adhere to the format `<key>=<value>` | When the identity authenticates with Conjur | Authentication should fail with a 403 response and [new log **2**](#logs) | |

#### Performance Tests

Expand All @@ -470,13 +466,13 @@ for infrastructure setup and long CI builds.
[//]: # "If the logs are listed in the feature doc, add a link to that section. If not, list them here."
[//]: # "You can use this tool to generate a table - https://www.tablesgenerator.com/markdown_tables#"

| **Scenario** | **Log message** |
|--------------|-----------------|
| An authenticating host is configured with either both or none of `authn-k8s/namespace` or `authn-k8s/namespace-label-selector` | Role must have exactly one of the following required constraints: {0} |
| An authenticating host is configured with `authn-k8s/namespace-label-selector`, but the authenticating namespace does not conform | Kubernetes namespace {0} does not match label-selector {1} |
| An authenticating host is configured with `authn-k8s/namespace-label-selector`, but its format does not match `"<key1>=<value1>"` | Invalid namespace label selector {0}: must adhere to format "\<key1>=\<value1>" |
| Kubernetes Authenticator begins validating `authn-k8s/namespace-label-selector` restriction | Validating resource restriction on request: 'namespace-label-selector' |
| Kubernetes Authenticator successfully validates `authn-k8s/namespace-label-selector` restriction | Validated K8s resource. Type:'namespace-label-selector', Selector:'{0}', Namespace:'{1}' |
| **ID** | **Scenario** | **Log message** |
|--------|--------------|-----------------|
| 0 | An authenticating host is configured with either both or none of `authn-k8s/namespace` or `authn-k8s/namespace-label` | Role must have exactly one of the following required constraints: {0} |
| 1 | An authenticating host is configured with `authn-k8s/namespace-label`, but the authenticating namespace does not conform | Kubernetes namespace {0} does not match label-selector {1} |
| 2 | An authenticating host is configured with `authn-k8s/namespace-label`, but its format does not match `"<key1>=<value1>"` | Invalid namespace label selector {0}: must adhere to format "\<key1>=\<value1>" |
| 3 | Kubernetes Authenticator begins validating `authn-k8s/namespace-label` restriction | Validating resource restriction on request: 'namespace-label' |
| 4 | Kubernetes Authenticator successfully validates `authn-k8s/namespace-label` restriction | Validated K8s resource. Type:'namespace-label', Selector:'{0}', Namespace:'{1}' |

## Documentation
[//]: # "Add notes on what should be documented in this solution. Elaborate on where this should be documented, including GitHub READMEs and/or official documentation."
Expand Down Expand Up @@ -517,7 +513,7 @@ live currently.
- !host
id: test-app
annotations:
authn-k8s/namespace-label-selector: "conjur.org/project=devNamespaces"
authn-k8s/namespace-label: "conjur.org/project=devNamespaces"
authn-k8s/authentication-container-name: "authenticator"
authn-k8s/service-account: "test-app-sa"
```
Expand Down Expand Up @@ -545,6 +541,17 @@ live currently.
This restriction may not be a barrier to acceptance, and shouldn't obstruct a
complete solution.

2. Rancher uses
[field management](https://kubernetes.io/docs/reference/using-api/server-side-apply/#field-management)
to maintain labels describing Project memberships server-side.

For our users that want to use labeled namespace groups in vanilla
Kubernetes, should we suggest field management to secure labels? Should we
provide a suggested implementation?

Is this something that can be achieved by simply restricting `create`,
`update` and `patch` permissions on namespace resources?

## Definition of Done

- Solution designed is approved
Expand Down

0 comments on commit aa3deda

Please sign in to comment.