From 3399ded2acf6abe5f4ef0457615daba65aac1cf7 Mon Sep 17 00:00:00 2001 From: Guillaume Boutry Date: Fri, 18 Aug 2023 11:03:34 +0200 Subject: [PATCH 01/13] feat: add secrets interface --- docs/json_schemas/secrets/v0/provider.json | 81 ++++++++++++++++++++++ docs/json_schemas/secrets/v0/requirer.json | 41 +++++++++++ interfaces/secrets/v0/README.md | 67 ++++++++++++++++++ interfaces/secrets/v0/charms.yaml | 5 ++ interfaces/secrets/v0/schema.py | 52 ++++++++++++++ 5 files changed, 246 insertions(+) create mode 100644 docs/json_schemas/secrets/v0/provider.json create mode 100644 docs/json_schemas/secrets/v0/requirer.json create mode 100644 interfaces/secrets/v0/README.md create mode 100644 interfaces/secrets/v0/charms.yaml create mode 100644 interfaces/secrets/v0/schema.py diff --git a/docs/json_schemas/secrets/v0/provider.json b/docs/json_schemas/secrets/v0/provider.json new file mode 100644 index 00000000..6a1fa7b3 --- /dev/null +++ b/docs/json_schemas/secrets/v0/provider.json @@ -0,0 +1,81 @@ +{ + "title": "ProviderSchema", + "description": "The schema for the provider side of this interface.", + "type": "object", + "properties": { + "unit": { + "$ref": "#/definitions/BaseModel" + }, + "app": { + "$ref": "#/definitions/SecretsProviderSchema" + } + }, + "required": [ + "app" + ], + "definitions": { + "BaseModel": { + "title": "BaseModel", + "type": "object", + "properties": {} + }, + "UnitCredentialsSchema": { + "title": "UnitCredentialsSchema", + "type": "object", + "properties": { + "role_id": { + "title": "Role Id", + "description": "The role ID to use to authenticate to Vault.", + "type": "string" + }, + "role_secret_id": { + "title": "Role Secret Id", + "description": "The role secret ID to use to authenticate to Vault.", + "type": "string" + } + }, + "required": [ + "role_id", + "role_secret_id" + ] + }, + "CredentialsSchema": { + "title": "CredentialsSchema", + "default": "Units' credentials", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/UnitCredentialsSchema" + } + }, + "SecretsProviderSchema": { + "title": "SecretsProviderSchema", + "type": "object", + "properties": { + "vault_url": { + "title": "Vault Url", + "description": "The URL of the Vault server to connect to.", + "type": "string" + }, + "kv_mountpoint": { + "title": "Kv Mountpoint", + "description": "The mountpoint of the KV store to use.", + "type": "string" + }, + "credentials": { + "title": "Credentials", + "description": "The credentials to use to authenticate to Vault.", + "allOf": [ + { + "$ref": "#/definitions/CredentialsSchema" + } + ] + } + }, + "required": [ + "vault_url", + "kv_mountpoint", + "credentials" + ] + } + } +} \ No newline at end of file diff --git a/docs/json_schemas/secrets/v0/requirer.json b/docs/json_schemas/secrets/v0/requirer.json new file mode 100644 index 00000000..f509aa40 --- /dev/null +++ b/docs/json_schemas/secrets/v0/requirer.json @@ -0,0 +1,41 @@ +{ + "title": "RequirerSchema", + "description": "The schema for the requirer side of this interface.", + "type": "object", + "properties": { + "unit": { + "$ref": "#/definitions/UnitSecretsRequirerSchema" + }, + "app": { + "$ref": "#/definitions/AppSecretsProviderSchema" + } + }, + "required": [ + "unit", + "app" + ], + "definitions": { + "UnitSecretsRequirerSchema": { + "title": "UnitSecretsRequirerSchema", + "type": "object", + "properties": { + "egress_subnet": { + "title": "Egress Subnet", + "default": "Egress subnet to use.", + "type": "string" + } + } + }, + "AppSecretsProviderSchema": { + "title": "AppSecretsProviderSchema", + "type": "object", + "properties": { + "secret_backend": { + "title": "Secret Backend", + "default": "The name of the secret backend to use.", + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/interfaces/secrets/v0/README.md b/interfaces/secrets/v0/README.md new file mode 100644 index 00000000..4a4ed0b0 --- /dev/null +++ b/interfaces/secrets/v0/README.md @@ -0,0 +1,67 @@ +# `secrets` + +## Usage + +Some charms require a secure key value store. This relation interface describes the expected behavior of any charm claiming to interact with Vault Key Value stores. + +## Direction + +```mermaid +flowchart TD + Requirer -- secret_backend, egress_subnet --> Provider + Provider -- vault_url, kv_mountpoint, credentials --> Requirer +``` + +## Behavior + +Both the Requirer and the Provider need to adhere to criteria to be considered compatible with the interface. + +### Provider + +Provider expectations + +- Must provide the vault url +- Must provide a key value mountpoint +- Must provide a role_id and role_secret_id for each unit, with access protected by unit's egress_subnet + +### Requirer + +Requirer expectations + +- Must provide a secret backend name, which must start with "charm-" +- Must provide an egress subnet for each unit used to protect access to the secret backend + +## Relation Data + +Describe the contents of the databags, and provide schemas for them. + +[\[Pydantic Schema\]](./schema.py) + +#### Example + +```yaml +provider: + app: + vault_url: http://10.152.183.104:8200 + kv_mountpoint: charm-barbican + credentials: | + { + "barbican-0": { + "role_id": 158778a2-04fc-39c5-ba13-0cb5faddb5eb", + "role_secret_id": "41c3e4eb-39ec-5c68-2f41-fbc1bc1e9c52" + }, + "barbican-1": { + "role_id": "38eb72db-60d0-082a-4847-6b9d1690cf02", + "role_secret_id": "fa1db047-3c90-e614-7ca0-76ce2ec1b6fc" + } + } + unit: {} +requirer: + app: + secret_backend: charm-barbican + unit: + barbican-0: + egress_subnet: 10.1.166.206/32 + barbican-1: + egress_subnet: 10.1.166.230/32 +``` diff --git a/interfaces/secrets/v0/charms.yaml b/interfaces/secrets/v0/charms.yaml new file mode 100644 index 00000000..b499b64e --- /dev/null +++ b/interfaces/secrets/v0/charms.yaml @@ -0,0 +1,5 @@ +providers: [] + +# list of charms using this interface as requirers. +# same structure as providers +requirers: [] diff --git a/interfaces/secrets/v0/schema.py b/interfaces/secrets/v0/schema.py new file mode 100644 index 00000000..751f13dd --- /dev/null +++ b/interfaces/secrets/v0/schema.py @@ -0,0 +1,52 @@ +"""This file defines the schemas for the provider and requirer sides of this relation interface. + +It must expose two interfaces.schema_base.DataBagSchema subclasses called: +- ProviderSchema +- RequirerSchema +""" + +from typing import Mapping + +from pydantic import BaseModel, Field, Json + +from interface_tester.schema_base import DataBagSchema + + +class UnitCredentialsSchema(BaseModel): + role_id: str = Field(description="The role ID to use to authenticate to Vault.") + role_secret_id: str = Field( + description="The role secret ID to use to authenticate to Vault." + ) + + +class CredentialsSchema(BaseModel): + __root__: Mapping[str, UnitCredentialsSchema] = Field("Units' credentials") + + +class SecretsProviderSchema(BaseModel): + vault_url: str = Field(description="The URL of the Vault server to connect to.") + kv_mountpoint: str = Field(description="The mountpoint of the KV store to use.") + credentials: Json[CredentialsSchema] = Field( + description="The credentials to use to authenticate to Vault." + ) + + +class AppSecretsProviderSchema(BaseModel): + secret_backend: str = Field("The name of the secret backend to use.") + + +class UnitSecretsRequirerSchema(BaseModel): + egress_subnet: str = Field("Egress subnet to use.") + + +class ProviderSchema(DataBagSchema): + """The schema for the provider side of this interface.""" + + app: SecretsProviderSchema + + +class RequirerSchema(DataBagSchema): + """The schema for the requirer side of this interface.""" + + app: AppSecretsProviderSchema + unit: UnitSecretsRequirerSchema From a4141d150f23d02914ce5120404a2e22dcb1097f Mon Sep 17 00:00:00 2001 From: Guillaume Boutry Date: Fri, 18 Aug 2023 11:43:23 +0200 Subject: [PATCH 02/13] feat: rename interface to vault-kv vault-kv reflects the interface purpose better than `secrets` --- .../{secrets => vault_kv}/v0/provider.json | 6 +++--- .../{secrets => vault_kv}/v0/requirer.json | 12 ++++++------ interfaces/{secrets => vault_kv}/v0/README.md | 2 +- interfaces/{secrets => vault_kv}/v0/charms.yaml | 0 interfaces/{secrets => vault_kv}/v0/schema.py | 12 ++++++------ 5 files changed, 16 insertions(+), 16 deletions(-) rename docs/json_schemas/{secrets => vault_kv}/v0/provider.json (93%) rename docs/json_schemas/{secrets => vault_kv}/v0/requirer.json (71%) rename interfaces/{secrets => vault_kv}/v0/README.md (99%) rename interfaces/{secrets => vault_kv}/v0/charms.yaml (100%) rename interfaces/{secrets => vault_kv}/v0/schema.py (85%) diff --git a/docs/json_schemas/secrets/v0/provider.json b/docs/json_schemas/vault_kv/v0/provider.json similarity index 93% rename from docs/json_schemas/secrets/v0/provider.json rename to docs/json_schemas/vault_kv/v0/provider.json index 6a1fa7b3..aabed54b 100644 --- a/docs/json_schemas/secrets/v0/provider.json +++ b/docs/json_schemas/vault_kv/v0/provider.json @@ -7,7 +7,7 @@ "$ref": "#/definitions/BaseModel" }, "app": { - "$ref": "#/definitions/SecretsProviderSchema" + "$ref": "#/definitions/VaultKvProviderSchema" } }, "required": [ @@ -47,8 +47,8 @@ "$ref": "#/definitions/UnitCredentialsSchema" } }, - "SecretsProviderSchema": { - "title": "SecretsProviderSchema", + "VaultKvProviderSchema": { + "title": "VaultKvProviderSchema", "type": "object", "properties": { "vault_url": { diff --git a/docs/json_schemas/secrets/v0/requirer.json b/docs/json_schemas/vault_kv/v0/requirer.json similarity index 71% rename from docs/json_schemas/secrets/v0/requirer.json rename to docs/json_schemas/vault_kv/v0/requirer.json index f509aa40..463aac84 100644 --- a/docs/json_schemas/secrets/v0/requirer.json +++ b/docs/json_schemas/vault_kv/v0/requirer.json @@ -4,10 +4,10 @@ "type": "object", "properties": { "unit": { - "$ref": "#/definitions/UnitSecretsRequirerSchema" + "$ref": "#/definitions/UnitVaultKvRequirerSchema" }, "app": { - "$ref": "#/definitions/AppSecretsProviderSchema" + "$ref": "#/definitions/AppVaultKvProviderSchema" } }, "required": [ @@ -15,8 +15,8 @@ "app" ], "definitions": { - "UnitSecretsRequirerSchema": { - "title": "UnitSecretsRequirerSchema", + "UnitVaultKvRequirerSchema": { + "title": "UnitVaultKvRequirerSchema", "type": "object", "properties": { "egress_subnet": { @@ -26,8 +26,8 @@ } } }, - "AppSecretsProviderSchema": { - "title": "AppSecretsProviderSchema", + "AppVaultKvProviderSchema": { + "title": "AppVaultKvProviderSchema", "type": "object", "properties": { "secret_backend": { diff --git a/interfaces/secrets/v0/README.md b/interfaces/vault_kv/v0/README.md similarity index 99% rename from interfaces/secrets/v0/README.md rename to interfaces/vault_kv/v0/README.md index 4a4ed0b0..63a17670 100644 --- a/interfaces/secrets/v0/README.md +++ b/interfaces/vault_kv/v0/README.md @@ -1,4 +1,4 @@ -# `secrets` +# `vault-kv` ## Usage diff --git a/interfaces/secrets/v0/charms.yaml b/interfaces/vault_kv/v0/charms.yaml similarity index 100% rename from interfaces/secrets/v0/charms.yaml rename to interfaces/vault_kv/v0/charms.yaml diff --git a/interfaces/secrets/v0/schema.py b/interfaces/vault_kv/v0/schema.py similarity index 85% rename from interfaces/secrets/v0/schema.py rename to interfaces/vault_kv/v0/schema.py index 751f13dd..5d9f4b86 100644 --- a/interfaces/secrets/v0/schema.py +++ b/interfaces/vault_kv/v0/schema.py @@ -23,7 +23,7 @@ class CredentialsSchema(BaseModel): __root__: Mapping[str, UnitCredentialsSchema] = Field("Units' credentials") -class SecretsProviderSchema(BaseModel): +class VaultKvProviderSchema(BaseModel): vault_url: str = Field(description="The URL of the Vault server to connect to.") kv_mountpoint: str = Field(description="The mountpoint of the KV store to use.") credentials: Json[CredentialsSchema] = Field( @@ -31,22 +31,22 @@ class SecretsProviderSchema(BaseModel): ) -class AppSecretsProviderSchema(BaseModel): +class AppVaultKvProviderSchema(BaseModel): secret_backend: str = Field("The name of the secret backend to use.") -class UnitSecretsRequirerSchema(BaseModel): +class UnitVaultKvRequirerSchema(BaseModel): egress_subnet: str = Field("Egress subnet to use.") class ProviderSchema(DataBagSchema): """The schema for the provider side of this interface.""" - app: SecretsProviderSchema + app: VaultKvProviderSchema class RequirerSchema(DataBagSchema): """The schema for the requirer side of this interface.""" - app: AppSecretsProviderSchema - unit: UnitSecretsRequirerSchema + app: AppVaultKvProviderSchema + unit: UnitVaultKvRequirerSchema From 7f2892581ae1aa83032978b19a8d59976bc5a177 Mon Sep 17 00:00:00 2001 From: Guillaume Boutry Date: Tue, 22 Aug 2023 09:11:17 +0200 Subject: [PATCH 03/13] fix: update egress_subnet description --- docs/json_schemas/vault_kv/v0/requirer.json | 2 +- interfaces/vault_kv/v0/schema.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/json_schemas/vault_kv/v0/requirer.json b/docs/json_schemas/vault_kv/v0/requirer.json index 463aac84..7238fd04 100644 --- a/docs/json_schemas/vault_kv/v0/requirer.json +++ b/docs/json_schemas/vault_kv/v0/requirer.json @@ -21,7 +21,7 @@ "properties": { "egress_subnet": { "title": "Egress Subnet", - "default": "Egress subnet to use.", + "default": "Egress subnet to use, in CIDR notation.", "type": "string" } } diff --git a/interfaces/vault_kv/v0/schema.py b/interfaces/vault_kv/v0/schema.py index 5d9f4b86..68cca6c6 100644 --- a/interfaces/vault_kv/v0/schema.py +++ b/interfaces/vault_kv/v0/schema.py @@ -36,7 +36,7 @@ class AppVaultKvProviderSchema(BaseModel): class UnitVaultKvRequirerSchema(BaseModel): - egress_subnet: str = Field("Egress subnet to use.") + egress_subnet: str = Field("Egress subnet to use, in CIDR notation.") class ProviderSchema(DataBagSchema): From bb21a09eb7072fa315415ee7e62023b6f972b72a Mon Sep 17 00:00:00 2001 From: Guillaume Boutry Date: Wed, 23 Aug 2023 21:44:26 +0200 Subject: [PATCH 04/13] fix: update mount related information Vault documentation refers to kv_mountpoint as `mounts`, let's reflect that in the relation data. A `mount` shall respect the following pattern: charm-- --- docs/json_schemas/vault_kv/v0/provider.json | 8 ++++---- docs/json_schemas/vault_kv/v0/requirer.json | 6 +++--- interfaces/vault_kv/v0/README.md | 14 ++++++-------- interfaces/vault_kv/v0/schema.py | 9 +++++++-- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/docs/json_schemas/vault_kv/v0/provider.json b/docs/json_schemas/vault_kv/v0/provider.json index aabed54b..e9d476a6 100644 --- a/docs/json_schemas/vault_kv/v0/provider.json +++ b/docs/json_schemas/vault_kv/v0/provider.json @@ -56,9 +56,9 @@ "description": "The URL of the Vault server to connect to.", "type": "string" }, - "kv_mountpoint": { - "title": "Kv Mountpoint", - "description": "The mountpoint of the KV store to use.", + "mount": { + "title": "Mount", + "description": "The KV mount available for the requirer application, respecting the pattern 'charm--'.", "type": "string" }, "credentials": { @@ -73,7 +73,7 @@ }, "required": [ "vault_url", - "kv_mountpoint", + "mount", "credentials" ] } diff --git a/docs/json_schemas/vault_kv/v0/requirer.json b/docs/json_schemas/vault_kv/v0/requirer.json index 7238fd04..fc30ca62 100644 --- a/docs/json_schemas/vault_kv/v0/requirer.json +++ b/docs/json_schemas/vault_kv/v0/requirer.json @@ -30,9 +30,9 @@ "title": "AppVaultKvProviderSchema", "type": "object", "properties": { - "secret_backend": { - "title": "Secret Backend", - "default": "The name of the secret backend to use.", + "mount_suffix": { + "title": "Mount Suffix", + "default": "Suffix to append to the mount name to get the KV mount.", "type": "string" } } diff --git a/interfaces/vault_kv/v0/README.md b/interfaces/vault_kv/v0/README.md index 63a17670..be39b278 100644 --- a/interfaces/vault_kv/v0/README.md +++ b/interfaces/vault_kv/v0/README.md @@ -8,8 +8,8 @@ Some charms require a secure key value store. This relation interface describes ```mermaid flowchart TD - Requirer -- secret_backend, egress_subnet --> Provider - Provider -- vault_url, kv_mountpoint, credentials --> Requirer + Requirer -- mount_suffix, egress_subnet --> Provider + Provider -- vault_url, mount, credentials --> Requirer ``` ## Behavior @@ -21,20 +21,18 @@ Both the Requirer and the Provider need to adhere to criteria to be considered c Provider expectations - Must provide the vault url -- Must provide a key value mountpoint +- Must provide a key value mount, the mount name shall respect the following pattern: charm-- - Must provide a role_id and role_secret_id for each unit, with access protected by unit's egress_subnet ### Requirer Requirer expectations -- Must provide a secret backend name, which must start with "charm-" +- Must provide a mount suffix - Must provide an egress subnet for each unit used to protect access to the secret backend ## Relation Data -Describe the contents of the databags, and provide schemas for them. - [\[Pydantic Schema\]](./schema.py) #### Example @@ -43,7 +41,7 @@ Describe the contents of the databags, and provide schemas for them. provider: app: vault_url: http://10.152.183.104:8200 - kv_mountpoint: charm-barbican + mount: charm-barbican-secrets credentials: | { "barbican-0": { @@ -58,7 +56,7 @@ provider: unit: {} requirer: app: - secret_backend: charm-barbican + mount_suffix: secrets unit: barbican-0: egress_subnet: 10.1.166.206/32 diff --git a/interfaces/vault_kv/v0/schema.py b/interfaces/vault_kv/v0/schema.py index 68cca6c6..efd7ef5c 100644 --- a/interfaces/vault_kv/v0/schema.py +++ b/interfaces/vault_kv/v0/schema.py @@ -25,14 +25,19 @@ class CredentialsSchema(BaseModel): class VaultKvProviderSchema(BaseModel): vault_url: str = Field(description="The URL of the Vault server to connect to.") - kv_mountpoint: str = Field(description="The mountpoint of the KV store to use.") + mount: str = Field( + description=( + "The KV mount available for the requirer application, " + "respecting the pattern 'charm--'." + ) + ) credentials: Json[CredentialsSchema] = Field( description="The credentials to use to authenticate to Vault." ) class AppVaultKvProviderSchema(BaseModel): - secret_backend: str = Field("The name of the secret backend to use.") + mount_suffix: str = Field("Suffix to append to the mount name to get the KV mount.") class UnitVaultKvRequirerSchema(BaseModel): From c65496c349cc1d48db8a06834d45dcb8fdccec7c Mon Sep 17 00:00:00 2001 From: Guillaume Boutry Date: Wed, 23 Aug 2023 21:58:00 +0200 Subject: [PATCH 05/13] fix: wrong name for app requirer schema --- docs/json_schemas/vault_kv/v0/requirer.json | 6 +++--- interfaces/vault_kv/v0/schema.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/json_schemas/vault_kv/v0/requirer.json b/docs/json_schemas/vault_kv/v0/requirer.json index fc30ca62..05723e36 100644 --- a/docs/json_schemas/vault_kv/v0/requirer.json +++ b/docs/json_schemas/vault_kv/v0/requirer.json @@ -7,7 +7,7 @@ "$ref": "#/definitions/UnitVaultKvRequirerSchema" }, "app": { - "$ref": "#/definitions/AppVaultKvProviderSchema" + "$ref": "#/definitions/AppVaultKvRequirerSchema" } }, "required": [ @@ -26,8 +26,8 @@ } } }, - "AppVaultKvProviderSchema": { - "title": "AppVaultKvProviderSchema", + "AppVaultKvRequirerSchema": { + "title": "AppVaultKvRequirerSchema", "type": "object", "properties": { "mount_suffix": { diff --git a/interfaces/vault_kv/v0/schema.py b/interfaces/vault_kv/v0/schema.py index efd7ef5c..7e7e9fa0 100644 --- a/interfaces/vault_kv/v0/schema.py +++ b/interfaces/vault_kv/v0/schema.py @@ -36,7 +36,7 @@ class VaultKvProviderSchema(BaseModel): ) -class AppVaultKvProviderSchema(BaseModel): +class AppVaultKvRequirerSchema(BaseModel): mount_suffix: str = Field("Suffix to append to the mount name to get the KV mount.") @@ -53,5 +53,5 @@ class ProviderSchema(DataBagSchema): class RequirerSchema(DataBagSchema): """The schema for the requirer side of this interface.""" - app: AppVaultKvProviderSchema + app: AppVaultKvRequirerSchema unit: UnitVaultKvRequirerSchema From 3569268b250ee76588eb2352d373951e8af5a6bd Mon Sep 17 00:00:00 2001 From: Guillaume Boutry Date: Thu, 24 Aug 2023 09:28:52 +0200 Subject: [PATCH 06/13] fix: remove intermediate credentials object Credentials intermediate class is unnecessary. Fix typo in databag example --- docs/json_schemas/vault_kv/v0/provider.json | 17 ++++------------- interfaces/vault_kv/v0/README.md | 2 +- interfaces/vault_kv/v0/schema.py | 6 +----- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/docs/json_schemas/vault_kv/v0/provider.json b/docs/json_schemas/vault_kv/v0/provider.json index e9d476a6..c97e7cda 100644 --- a/docs/json_schemas/vault_kv/v0/provider.json +++ b/docs/json_schemas/vault_kv/v0/provider.json @@ -39,14 +39,6 @@ "role_secret_id" ] }, - "CredentialsSchema": { - "title": "CredentialsSchema", - "default": "Units' credentials", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/UnitCredentialsSchema" - } - }, "VaultKvProviderSchema": { "title": "VaultKvProviderSchema", "type": "object", @@ -64,11 +56,10 @@ "credentials": { "title": "Credentials", "description": "The credentials to use to authenticate to Vault.", - "allOf": [ - { - "$ref": "#/definitions/CredentialsSchema" - } - ] + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/UnitCredentialsSchema" + } } }, "required": [ diff --git a/interfaces/vault_kv/v0/README.md b/interfaces/vault_kv/v0/README.md index be39b278..75314269 100644 --- a/interfaces/vault_kv/v0/README.md +++ b/interfaces/vault_kv/v0/README.md @@ -45,7 +45,7 @@ provider: credentials: | { "barbican-0": { - "role_id": 158778a2-04fc-39c5-ba13-0cb5faddb5eb", + "role_id": "158778a2-04fc-39c5-ba13-0cb5faddb5eb", "role_secret_id": "41c3e4eb-39ec-5c68-2f41-fbc1bc1e9c52" }, "barbican-1": { diff --git a/interfaces/vault_kv/v0/schema.py b/interfaces/vault_kv/v0/schema.py index 7e7e9fa0..d37278cf 100644 --- a/interfaces/vault_kv/v0/schema.py +++ b/interfaces/vault_kv/v0/schema.py @@ -19,10 +19,6 @@ class UnitCredentialsSchema(BaseModel): ) -class CredentialsSchema(BaseModel): - __root__: Mapping[str, UnitCredentialsSchema] = Field("Units' credentials") - - class VaultKvProviderSchema(BaseModel): vault_url: str = Field(description="The URL of the Vault server to connect to.") mount: str = Field( @@ -31,7 +27,7 @@ class VaultKvProviderSchema(BaseModel): "respecting the pattern 'charm--'." ) ) - credentials: Json[CredentialsSchema] = Field( + credentials: Json[Mapping[str, UnitCredentialsSchema]] = Field( description="The credentials to use to authenticate to Vault." ) From 1bcabfcfcbea1b84ac6024aa328a90995d853e0d Mon Sep 17 00:00:00 2001 From: Guillaume Boutry Date: Thu, 24 Aug 2023 14:00:47 +0200 Subject: [PATCH 07/13] fix: Add entry to top level readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 81664485..7c1441d0 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ To quickly get started, see the [template interface](https://github.com/canonica | | [`tls_certificates/v1`](interfaces/tls_certificates/v1/README.md) | ![Status: Draft](https://img.shields.io/badge/Status-Draft-orange) | | Metadata | [`k8s-service`](interfaces/k8s-service/v0/README.md) | ![Status: Draft](https://img.shields.io/badge/Status-Draft-orange) | | Storage | [`s3`](interfaces/s3/v0/README.md) | ![Status: Draft](https://img.shields.io/badge/Status-Draft-orange) | +| | [`vault-kv`](interfaces/vault_kv/v0/README.md) | ![Status: Draft](https://img.shields.io/badge/Status-Draft-orange) | ## Project-internal Interfaces From b41853e45b4268d9a46920e479347060138be570 Mon Sep 17 00:00:00 2001 From: Guillaume Boutry Date: Fri, 25 Aug 2023 10:05:48 +0200 Subject: [PATCH 08/13] fix: refactor credentials, elaborate on egress_subnet, change category Refactor credentials: role_id and role_secret_id will be passed as a Juju secret. Elaborate on egress_subnet: usage of role_id and role_secret_id is restricted to unit's egress_subnet, meaning only the designated can use this tuple of credentials. Change category: Move `vault-kv` from Storage to Security --- README.md | 2 +- docs/json_schemas/vault_kv/v0/provider.json | 24 ++------------------- interfaces/vault_kv/v0/README.md | 17 ++++++--------- interfaces/vault_kv/v0/schema.py | 14 +++++------- 4 files changed, 15 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 7c1441d0..e9c55c01 100644 --- a/README.md +++ b/README.md @@ -57,9 +57,9 @@ To quickly get started, see the [template interface](https://github.com/canonica | Security | [`mutual_tls`](interfaces/mutual_tls/v0/README.md) | ![Status: Draft](https://img.shields.io/badge/Status-Draft-orange) | | | [`tls_certificates/v0`](interfaces/tls_certificates/v0/README.md) | ![Status: Live](https://img.shields.io/badge/Status-Live-darkgreen) | | | [`tls_certificates/v1`](interfaces/tls_certificates/v1/README.md) | ![Status: Draft](https://img.shields.io/badge/Status-Draft-orange) | +| | [`vault-kv`](interfaces/vault_kv/v0/README.md) | ![Status: Draft](https://img.shields.io/badge/Status-Draft-orange) | | Metadata | [`k8s-service`](interfaces/k8s-service/v0/README.md) | ![Status: Draft](https://img.shields.io/badge/Status-Draft-orange) | | Storage | [`s3`](interfaces/s3/v0/README.md) | ![Status: Draft](https://img.shields.io/badge/Status-Draft-orange) | -| | [`vault-kv`](interfaces/vault_kv/v0/README.md) | ![Status: Draft](https://img.shields.io/badge/Status-Draft-orange) | ## Project-internal Interfaces diff --git a/docs/json_schemas/vault_kv/v0/provider.json b/docs/json_schemas/vault_kv/v0/provider.json index c97e7cda..5c9af233 100644 --- a/docs/json_schemas/vault_kv/v0/provider.json +++ b/docs/json_schemas/vault_kv/v0/provider.json @@ -19,26 +19,6 @@ "type": "object", "properties": {} }, - "UnitCredentialsSchema": { - "title": "UnitCredentialsSchema", - "type": "object", - "properties": { - "role_id": { - "title": "Role Id", - "description": "The role ID to use to authenticate to Vault.", - "type": "string" - }, - "role_secret_id": { - "title": "Role Secret Id", - "description": "The role secret ID to use to authenticate to Vault.", - "type": "string" - } - }, - "required": [ - "role_id", - "role_secret_id" - ] - }, "VaultKvProviderSchema": { "title": "VaultKvProviderSchema", "type": "object", @@ -55,10 +35,10 @@ }, "credentials": { "title": "Credentials", - "description": "The credentials to use to authenticate to Vault.", + "description": "Mapping of unit name and credentials for that unit. Credentials are a juju secret containing a role_id and role_secret_id.", "type": "object", "additionalProperties": { - "$ref": "#/definitions/UnitCredentialsSchema" + "type": "string" } } }, diff --git a/interfaces/vault_kv/v0/README.md b/interfaces/vault_kv/v0/README.md index 75314269..418db17f 100644 --- a/interfaces/vault_kv/v0/README.md +++ b/interfaces/vault_kv/v0/README.md @@ -22,14 +22,17 @@ Provider expectations - Must provide the vault url - Must provide a key value mount, the mount name shall respect the following pattern: charm-- -- Must provide a role_id and role_secret_id for each unit, with access protected by unit's egress_subnet +- Must provide a role_id and role_secret_id for each unit. + The role_id and role_secret_id shall be provided in the form of a Juju secret. + Usage of role_id and role_secret_id to access the KV backend shall be restricted to the unit's egress_subnet. ### Requirer Requirer expectations - Must provide a mount suffix -- Must provide an egress subnet for each unit used to protect access to the secret backend +- Must provide an egress subnet for each unit requiring access to the vault key value store. + The unit's egress_subnet shall be used to restrict access to the secret backend. ## Relation Data @@ -44,14 +47,8 @@ provider: mount: charm-barbican-secrets credentials: | { - "barbican-0": { - "role_id": "158778a2-04fc-39c5-ba13-0cb5faddb5eb", - "role_secret_id": "41c3e4eb-39ec-5c68-2f41-fbc1bc1e9c52" - }, - "barbican-1": { - "role_id": "38eb72db-60d0-082a-4847-6b9d1690cf02", - "role_secret_id": "fa1db047-3c90-e614-7ca0-76ce2ec1b6fc" - } + "barbican-0": "secret://4f7cc474-a23d-49a2-8b6e-9835c1e08325/cjk5slcrl3uc767oebp0", + "barbican-1": "secret://4f7cc474-a23d-49a2-8b6e-9835c1e08325/cjk5slcrl3uc767oebpg" } unit: {} requirer: diff --git a/interfaces/vault_kv/v0/schema.py b/interfaces/vault_kv/v0/schema.py index d37278cf..8dd3eabf 100644 --- a/interfaces/vault_kv/v0/schema.py +++ b/interfaces/vault_kv/v0/schema.py @@ -12,13 +12,6 @@ from interface_tester.schema_base import DataBagSchema -class UnitCredentialsSchema(BaseModel): - role_id: str = Field(description="The role ID to use to authenticate to Vault.") - role_secret_id: str = Field( - description="The role secret ID to use to authenticate to Vault." - ) - - class VaultKvProviderSchema(BaseModel): vault_url: str = Field(description="The URL of the Vault server to connect to.") mount: str = Field( @@ -27,8 +20,11 @@ class VaultKvProviderSchema(BaseModel): "respecting the pattern 'charm--'." ) ) - credentials: Json[Mapping[str, UnitCredentialsSchema]] = Field( - description="The credentials to use to authenticate to Vault." + credentials: Json[Mapping[str, str]] = Field( + description=( + "Mapping of unit name and credentials for that unit." + " Credentials are a juju secret containing a role_id and role_secret_id." + ) ) From 780a0a98ee5a7ab182d5b815535530e8647f0ab0 Mon Sep 17 00:00:00 2001 From: Guillaume Boutry Date: Fri, 25 Aug 2023 12:29:00 +0200 Subject: [PATCH 09/13] fix: secret keys cannot contain underscores Renaming role_id to role-id and role_secret_id to role-secret-id --- docs/json_schemas/vault_kv/v0/provider.json | 2 +- interfaces/vault_kv/v0/README.md | 6 +++--- interfaces/vault_kv/v0/schema.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/json_schemas/vault_kv/v0/provider.json b/docs/json_schemas/vault_kv/v0/provider.json index 5c9af233..c2025a74 100644 --- a/docs/json_schemas/vault_kv/v0/provider.json +++ b/docs/json_schemas/vault_kv/v0/provider.json @@ -35,7 +35,7 @@ }, "credentials": { "title": "Credentials", - "description": "Mapping of unit name and credentials for that unit. Credentials are a juju secret containing a role_id and role_secret_id.", + "description": "Mapping of unit name and credentials for that unit. Credentials are a juju secret containing a role-id and role-secret-id.", "type": "object", "additionalProperties": { "type": "string" diff --git a/interfaces/vault_kv/v0/README.md b/interfaces/vault_kv/v0/README.md index 418db17f..44332cdd 100644 --- a/interfaces/vault_kv/v0/README.md +++ b/interfaces/vault_kv/v0/README.md @@ -22,9 +22,9 @@ Provider expectations - Must provide the vault url - Must provide a key value mount, the mount name shall respect the following pattern: charm-- -- Must provide a role_id and role_secret_id for each unit. - The role_id and role_secret_id shall be provided in the form of a Juju secret. - Usage of role_id and role_secret_id to access the KV backend shall be restricted to the unit's egress_subnet. +- Must provide a role-id and role-secret-id for each unit. + The role-id and role-secret-id shall be provided in the form of a Juju secret. + Usage of role-id and role-secret-id to access the KV backend shall be restricted to the unit's egress_subnet. ### Requirer diff --git a/interfaces/vault_kv/v0/schema.py b/interfaces/vault_kv/v0/schema.py index 8dd3eabf..17eaa8d8 100644 --- a/interfaces/vault_kv/v0/schema.py +++ b/interfaces/vault_kv/v0/schema.py @@ -23,7 +23,7 @@ class VaultKvProviderSchema(BaseModel): credentials: Json[Mapping[str, str]] = Field( description=( "Mapping of unit name and credentials for that unit." - " Credentials are a juju secret containing a role_id and role_secret_id." + " Credentials are a juju secret containing a role-id and role-secret-id." ) ) From 6fa2e31792c04b2e11e0c5951851637bdb41146c Mon Sep 17 00:00:00 2001 From: Guillaume Boutry Date: Mon, 28 Aug 2023 09:20:25 +0200 Subject: [PATCH 10/13] fix: integrate comments on provider expectations --- interfaces/vault_kv/v0/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interfaces/vault_kv/v0/README.md b/interfaces/vault_kv/v0/README.md index 44332cdd..e2a9adb5 100644 --- a/interfaces/vault_kv/v0/README.md +++ b/interfaces/vault_kv/v0/README.md @@ -21,10 +21,10 @@ Both the Requirer and the Provider need to adhere to criteria to be considered c Provider expectations - Must provide the vault url -- Must provide a key value mount, the mount name shall respect the following pattern: charm-- -- Must provide a role-id and role-secret-id for each unit. - The role-id and role-secret-id shall be provided in the form of a Juju secret. - Usage of role-id and role-secret-id to access the KV backend shall be restricted to the unit's egress_subnet. +- Must provide a key value mount, the mount name shall respect the following pattern: `charm--` +- Must create an approle restricted to the requiring unit's egress subnet. +- Must create a Juju secret containing a role-id and role-secret-id for each unit +- Must provide the Juju secret ID in the relation data. ### Requirer From 6004fdbffa99fbb352657b2dbcdb2ede7e14579a Mon Sep 17 00:00:00 2001 From: Guillaume Boutry Date: Mon, 28 Aug 2023 14:41:46 +0200 Subject: [PATCH 11/13] fix: rephrase expections following established pattern --- interfaces/vault_kv/v0/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/interfaces/vault_kv/v0/README.md b/interfaces/vault_kv/v0/README.md index e2a9adb5..3ed84ffe 100644 --- a/interfaces/vault_kv/v0/README.md +++ b/interfaces/vault_kv/v0/README.md @@ -20,18 +20,18 @@ Both the Requirer and the Provider need to adhere to criteria to be considered c Provider expectations -- Must provide the vault url -- Must provide a key value mount, the mount name shall respect the following pattern: `charm--` -- Must create an approle restricted to the requiring unit's egress subnet. -- Must create a Juju secret containing a role-id and role-secret-id for each unit -- Must provide the Juju secret ID in the relation data. +- Is expected to provide the vault url +- Is expected to provide a key value mount, the mount name shall respect the following pattern: `charm--` +- Is expected to create an approle restricted to the requiring unit's egress subnet. +- Is expected to create a Juju secret containing a role-id and role-secret-id for each unit +- Is expected to provide the Juju secret ID in the relation data. ### Requirer Requirer expectations -- Must provide a mount suffix -- Must provide an egress subnet for each unit requiring access to the vault key value store. +- Is expected to provide a mount suffix +- Is expected to provide an egress subnet for each unit requiring access to the vault key value store. The unit's egress_subnet shall be used to restrict access to the secret backend. ## Relation Data From 55a704bc967e4ca92d54796ae493fad4f0b00cce Mon Sep 17 00:00:00 2001 From: Guillaume Boutry Date: Wed, 30 Aug 2023 13:59:18 +0200 Subject: [PATCH 12/13] fix: add nonce to uniquely identity units over CMR In case of CMRs, the provider will see an obfuscated name for an unit, which will cause the requiring to fail fetching its credentials from the relation. A requiring unit has to provide a unique string that will be used to store the secret in the relation data, allowing this unit to fetch the secret at a later time. Rename to . --- docs/json_schemas/vault_kv/v0/provider.json | 2 +- docs/json_schemas/vault_kv/v0/requirer.json | 5 +++++ interfaces/vault_kv/v0/README.md | 15 +++++++++------ interfaces/vault_kv/v0/schema.py | 5 ++++- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/docs/json_schemas/vault_kv/v0/provider.json b/docs/json_schemas/vault_kv/v0/provider.json index c2025a74..4b03a04b 100644 --- a/docs/json_schemas/vault_kv/v0/provider.json +++ b/docs/json_schemas/vault_kv/v0/provider.json @@ -35,7 +35,7 @@ }, "credentials": { "title": "Credentials", - "description": "Mapping of unit name and credentials for that unit. Credentials are a juju secret containing a role-id and role-secret-id.", + "description": "Mapping of unit name and credentials for that unit. Credentials are a juju secret containing a 'role-id' and a 'role-secret-id'.", "type": "object", "additionalProperties": { "type": "string" diff --git a/docs/json_schemas/vault_kv/v0/requirer.json b/docs/json_schemas/vault_kv/v0/requirer.json index 05723e36..3bd619fd 100644 --- a/docs/json_schemas/vault_kv/v0/requirer.json +++ b/docs/json_schemas/vault_kv/v0/requirer.json @@ -23,6 +23,11 @@ "title": "Egress Subnet", "default": "Egress subnet to use, in CIDR notation.", "type": "string" + }, + "nonce": { + "title": "Nonce", + "default": "Uniquely identifying value for this unit. `secrets.token_hex(16)` is recommended.", + "type": "string" } } }, diff --git a/interfaces/vault_kv/v0/README.md b/interfaces/vault_kv/v0/README.md index 3ed84ffe..4e6ec40d 100644 --- a/interfaces/vault_kv/v0/README.md +++ b/interfaces/vault_kv/v0/README.md @@ -8,7 +8,7 @@ Some charms require a secure key value store. This relation interface describes ```mermaid flowchart TD - Requirer -- mount_suffix, egress_subnet --> Provider + Requirer -- mount_suffix, nonce, egress_subnet --> Provider Provider -- vault_url, mount, credentials --> Requirer ``` @@ -21,10 +21,10 @@ Both the Requirer and the Provider need to adhere to criteria to be considered c Provider expectations - Is expected to provide the vault url -- Is expected to provide a key value mount, the mount name shall respect the following pattern: `charm--` +- Is expected to provide a key value mount, the mount name shall respect the following pattern: `charm--` - Is expected to create an approle restricted to the requiring unit's egress subnet. - Is expected to create a Juju secret containing a role-id and role-secret-id for each unit -- Is expected to provide the Juju secret ID in the relation data. +- Is expected to provide the Juju secret ID in the relation data, identified by the unit's nonce. ### Requirer @@ -33,6 +33,7 @@ Requirer expectations - Is expected to provide a mount suffix - Is expected to provide an egress subnet for each unit requiring access to the vault key value store. The unit's egress_subnet shall be used to restrict access to the secret backend. +- Is expected to provide a nonce, i.e. a string uniquely identifying the unit. ## Relation Data @@ -44,11 +45,11 @@ Requirer expectations provider: app: vault_url: http://10.152.183.104:8200 - mount: charm-barbican-secrets + mount: charm-barbican-secrets # in case of CMR, mount will look like `charm-remote-fd7bc6a8c2d54d748ec3822da5abf0bc-secrets` credentials: | { - "barbican-0": "secret://4f7cc474-a23d-49a2-8b6e-9835c1e08325/cjk5slcrl3uc767oebp0", - "barbican-1": "secret://4f7cc474-a23d-49a2-8b6e-9835c1e08325/cjk5slcrl3uc767oebpg" + "3081279da89c48a32923473c2c587019": "secret://4f7cc474-a23d-49a2-8b6e-9835c1e08325/cjk5slcrl3uc767oebp0", + "b49e6098f245344f1035c3aa0e0c9181": "secret://4f7cc474-a23d-49a2-8b6e-9835c1e08325/cjk5slcrl3uc767oebpg" } unit: {} requirer: @@ -57,6 +58,8 @@ requirer: unit: barbican-0: egress_subnet: 10.1.166.206/32 + nonce: 3081279da89c48a32923473c2c587019 barbican-1: egress_subnet: 10.1.166.230/32 + nonce: b49e6098f245344f1035c3aa0e0c9181 ``` diff --git a/interfaces/vault_kv/v0/schema.py b/interfaces/vault_kv/v0/schema.py index 17eaa8d8..45059926 100644 --- a/interfaces/vault_kv/v0/schema.py +++ b/interfaces/vault_kv/v0/schema.py @@ -23,7 +23,7 @@ class VaultKvProviderSchema(BaseModel): credentials: Json[Mapping[str, str]] = Field( description=( "Mapping of unit name and credentials for that unit." - " Credentials are a juju secret containing a role-id and role-secret-id." + " Credentials are a juju secret containing a 'role-id' and a 'role-secret-id'." ) ) @@ -34,6 +34,9 @@ class AppVaultKvRequirerSchema(BaseModel): class UnitVaultKvRequirerSchema(BaseModel): egress_subnet: str = Field("Egress subnet to use, in CIDR notation.") + nonce: str = Field( + "Uniquely identifying value for this unit. `secrets.token_hex(16)` is recommended." + ) class ProviderSchema(DataBagSchema): From 0f32d7bf83b6a5ce465d20a42075eb83d76d24b2 Mon Sep 17 00:00:00 2001 From: Guillaume Boutry Date: Mon, 11 Sep 2023 16:16:43 +0200 Subject: [PATCH 13/13] Document unvalid credentials when pod's identity changes --- interfaces/vault_kv/v0/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interfaces/vault_kv/v0/README.md b/interfaces/vault_kv/v0/README.md index 4e6ec40d..65ad9d54 100644 --- a/interfaces/vault_kv/v0/README.md +++ b/interfaces/vault_kv/v0/README.md @@ -25,6 +25,8 @@ Provider expectations - Is expected to create an approle restricted to the requiring unit's egress subnet. - Is expected to create a Juju secret containing a role-id and role-secret-id for each unit - Is expected to provide the Juju secret ID in the relation data, identified by the unit's nonce. +- Is expected to have out of date credentials when requirer unit's identity change, for some unspecified amount of time + until new credentials have been generated. For example, during an upgrade-charm event. ### Requirer