Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
42276fc
Create scs-XXXX-vN-key-manager-standard.md
josephineSei May 23, 2024
b1c030d
Update scs-XXXX-vN-key-manager-standard.md
josephineSei May 23, 2024
fdedfc5
Create check-for-key-manager.py
josephineSei May 24, 2024
b708eb3
Update scs-XXXX-vN-key-manager-standard.md
josephineSei May 24, 2024
178b2aa
Update scs-XXXX-vN-key-manager-standard.md
josephineSei Jun 6, 2024
3e22081
Apply suggestions from code review
josephineSei Jun 6, 2024
b5f17db
Create scs-XXXX-wN-key-manager-implementation-testing.md
josephineSei Jun 12, 2024
c4a2a56
Update scs-XXXX-vN-key-manager-standard.md
josephineSei Jun 17, 2024
d0cc443
Update scs-XXXX-wN-key-manager-implementation-testing.md
josephineSei Jun 17, 2024
f9a43a8
Update check-for-key-manager.py
josephineSei Jun 17, 2024
15c5ad9
Apply suggestions from code review
josephineSei Jun 18, 2024
1a7d192
unify all times "Key Manager" is written
josephineSei Jun 18, 2024
9d7a7cb
Update scs-XXXX-wN-key-manager-implementation-testing.md
josephineSei Jun 18, 2024
6fe1f69
Rephrasing many sentences for a better understanding.
josephineSei Jun 25, 2024
22d456d
Rephrasing
josephineSei Jun 25, 2024
8c86fe8
Add section for the Key Manager Policies
josephineSei Jul 3, 2024
86ddfc5
Update scs-XXXX-vN-key-manager-standard.md
josephineSei Jul 3, 2024
0acc7b4
add policies to the implementation notes
josephineSei Jul 3, 2024
5f9d30c
Update scs-XXXX-vN-key-manager-standard.md
josephineSei Jul 3, 2024
11befc8
Update scs-XXXX-wN-key-manager-implementation-testing.md
josephineSei Jul 3, 2024
6405a42
Update check-for-key-manager.py
josephineSei Jul 4, 2024
559425e
Update check-for-key-manager.py
josephineSei Jul 4, 2024
e62340e
Merge branch 'main' into key-manager-standard
josephineSei Jul 15, 2024
aa607f1
Update check-for-key-manager.py
josephineSei Jul 16, 2024
ad9e3f9
Update check-for-key-manager.py
josephineSei Jul 17, 2024
438a257
Update check-for-key-manager.py
josephineSei Jul 17, 2024
5025d74
Update check-for-key-manager.py
josephineSei Aug 15, 2024
e3c5a37
Merge branch 'main' into key-manager-standard
josephineSei Aug 15, 2024
7a1e49f
Adjust Spelling and some phrases.
josephineSei Aug 15, 2024
e146ca0
Merge branch 'main' into key-manager-standard
josephineSei Aug 19, 2024
725d37f
Update scs-XXXX-wN-key-manager-implementation-testing.md
josephineSei Aug 21, 2024
5bbb13d
Merge branch 'main' into key-manager-standard
josephineSei Aug 21, 2024
8b22978
Merge branch 'main' into key-manager-standard
josephineSei Aug 26, 2024
b4b6d7e
Rename scs-XXXX-vN-key-manager-standard.md to scs-0116-v1-key-manager…
josephineSei Aug 26, 2024
d103ea3
Update and rename scs-XXXX-wN-key-manager-implementation-testing.md t…
josephineSei Aug 26, 2024
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
105 changes: 105 additions & 0 deletions Standards/scs-0116-v1-key-manager-standard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
---
title: Key Manager Standard
type: Standard
status: Draft
track: IaaS
---

## Introduction

To encrypt user data like volumes or in the future also Images and ephemeral storage for VMs, the key has to be present in the infrastructure.
A Key Manager service within the infrastructure can be utilized to store keys.
Consequently providing keys for every encryption or decryption is possible without including the user.
Also authorization policies can be applied on every request to the Key Manager service.
OpenStack offers a Key Manager implementation that is named Barbican, which provides these features.
This standard aims to provide a base level of security for Cloud Service Providers that integrate a Key Manager into their deployments.

## Terminology

| Term | Meaning |
|---|---|
| API | Application Programming Interface, often referring to the REST API interfaces provided by OpenStack and related services |
| Barbican | The Key Manager implementation in OpenStack |
| CSP | Cloud Service Provider, provider managing the OpenStack infrastructure |
| IaaS | Infrastructure-as-a-Service |
| HSM | Hardware Security Module |
| KEK | Key Encryption Key |
| RBAC | Role Based Access Control |

## Motivation

User data encryption requires an encryption key to be known during encryption and decryption processes.
Key Managers like Barbican provide this functionality on the IaaS-Level.
Not every IaaS deployment currently offers user data encryption as part of their standard offering.
This standard should encourage CSPs to integrate a Key Manager and thus increase the amount of Clouds with offerings of data encryption.
It is also important to take a closer look into the Key Manager and analyze how such a service can be configured securely.

A Key Manager service manages keys in a secure manner, but this can be achieved differently and is not primarily in scope of this standard.
The OpenStack Key Manager Barbican stores keys encrypted with a project specific KEK in the database.
The KEKs are also stored encrypted in the same database.
The Master-KEK, used to encrypt the project specific KEKs is not stored in the database and is stored differently depending on the backend storage plugin used.
This standard also abstracts the used plugins and wants to ensure that the Master-KEK is protected, too.

## Design Considerations

While discussing what this standard should aim for it was discovered that some CSPs don't use Barbican or another Key Manager at all and do not provide the feature to encrypt user data to their customers.
This should change, but the exact change comes with financial burden, when choosing a plugin in Barbican to store the Master-KEK or choosing to integrate another Key Manager service instead.
To minimize the burden and enable more CSPs to step up and provide encryption, this standard will only make recommendations about plugins from Barbican.

### Options considered

#### Recommend or even mandate specific Key Manager plugins

It was considered to only recommend a certain set of plugins or backends for the Key Manager, but this may be very prone to change if e.g. Barbican adds a new plugin.
As the SCS only wants to mandate the API that can be abstracted through the Castellan library in OpenStack, integrating any other Key Manager implementation is not uncommon, so this standard needs to consider other possible Key Managers as well.
Due to these reasons this option was disregarded.

#### Recommendation regarding the handling of the Master KEK

Looking into the available Barbican plugins and possible attack vectors one design decision in the plugins is very important: where and how to store the Master-KEK.
Because the Plugins might use different technologies there are many locations for the Master KEK possible.
Most of the Plugins increase the security level by not storing the Master-KEK in plain text on the physical machine Barbican is running on.
This mechanism as a whole, is something that CSPs should aim to do.

#### Standardization of the Key Manager Policy

Because this standards recommends or even eventually mandates the presence of a Key Manager, the situation about the policy of the Key Manager needs to be discussed.
The policy of an IaaS service should use the same roles as the other IaaS services.
Unfortunately this does not apply to the Key Manager implementation Barbican.
It has the roles `reader`, `audit` and `creator`, which are not present in the Keystone role concept.
The roles a customer usually gets through the Identity API is `member`.
Leaving it this way will prevent users from creating and using secrets even when a Key Manager is integrated.

To unify the roles among all IaaS services, there is currently work done in the OpenStack Community.
This initiative is called secure RBAC[^1].
Also the SCS is discussing a standard concerning the roles[^2].
When this is done, there is no further work needed.
But as of the 2024.1 release, this is still under development.

In conclusion this standard should mandate everyone who uses a Key Manager that does not include the secure RBAC, to adjust the policies to have a mapping between the internal `creator` and the identity-based `member` role.
This will result in a `member` being allowed to do everything a `creator` can do.

[^1]: [Secure RBAC work in OpenStack](https://etherpad.opendev.org/p/rbac-goal-tracking)
[^2]: [Issue for a role standard in SCS](https://github.com/SovereignCloudStack/issues/issues/396)

## Key Manager Standard

To increase security and allow user data encryption, CSPs SHOULD implement the Key Manager API (e.g. implemented by Barbican).
The Keys managed by this Key Manager MUST be stored encrypted and the Master-KEK of the Key Manager MUST be stored in another place than the Keys.

If possible CSPs SHOULD NOT store the Master-KEK in plain-text on the physical host the Key Manager is running on.

### Key Manager Policies

If a Key Manager without secure RBAC enabled is used, the policies MUST be adjusted to let the `member` role of the Identity service be equivalent to the Key Manager internal `creator` role.

## Related Documents

[Barbican Plugins](https://docs.openstack.org/de/security-guide/secrets-management/barbican.html)

## Conformance Tests

Conformance must be tested in two steps.

1. The check whether a Key Manager is present can be done in a similar way as in the mandatory OpenStack service APIs standard and the test should be merged into the mandatory service test as soon as a Key Manager is required in scs-conformant infrastructures.
2. The check, that there is no Master-KEK present on the Key Manager Node, has to be done by the CSP themself.
51 changes: 51 additions & 0 deletions Standards/scs-0116-w1-key-manager-implementation-testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
title: "SCS Key Manager Standard: Implementation and Testing Notes"
type: Supplement
track: IaaS
status: Proposal
supplements:
- scs-0116-v1-key-manager-standard.md
---

## Implementation

A Key Manager service can have different backends.
For Barbican these are called Plugins.
The standard plugin is `simple_crypto`, which has the Master-KEK written in the Barbican config file.
In that case the Master-KEK needs additional protection.
When the `simple_crypto` plugin is used, securing the Master-KEK can be achieved through protection of the Barbican config e.g. through running Barbican in an enclave.

Another option to secure the Master-KEK would be using an HSM with a corresponding plugin in Barbican.
In that case the Master-KEK will be stored inside the HSM and encryption and decryption of the Project-KEKs will also happen in the HSM.
There are also software HSMs available, that should be tested for their integration into the Barbican workflow.

Other Plugins in Barbican are the KMIP plugin and Vault[^1].
They are storing the keys differently and CSPs need to make sure, that the access to the keys is configured securely.

:::tip

Barbican supports deploying out-of-tree drivers what enables operators to satisfy their specific needs.

:::

[^1]:[Barbican Plugins](https://docs.openstack.org/barbican/latest/install/barbican-backend.html)

### Policies

When a Key Manager is used, but it uses the old policies and does not enforce the new secure RBAC work, the roles between Barbican and the other IaaS services differ.
This can be done with a small change in the policy.yaml file. The `creator` has to be defined like this:

```yaml
"creator": "role:member"
```

## Automated Tests

The check for the presence of a Key Manager is done with a test script, that checks the presence of a Key Manager service in the catalog endpoint of Openstack.
This check can eventually be moved to the checks for the mandatory an supported service/API list, in case of a promotion of the Key Manager to the mandatory list.

## Manual Tests

It is not possible to check a deployment for a correctly protected Master KEK automatically from the outside.
Even audits would need to check the complete host for plain-text keys.
CSPs are responsible for ensuring the protection of the Master KEK and they have to make at least their architecture for that protection auditable.
190 changes: 190 additions & 0 deletions Tests/iaas/key-manager/check-for-key-manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
"""Mandatory APIs checker
This script retrieves the endpoint catalog from Keystone using the OpenStack
SDK and checks whether a key manager APi endpoint is present.
The script relies on an OpenStack SDK compatible clouds.yaml file for
authentication with Keystone.
"""

import argparse
import json
import logging
import os

import openstack


logger = logging.getLogger(__name__)


def connect(cloud_name: str) -> openstack.connection.Connection:
"""Create a connection to an OpenStack cloud
:param string cloud_name:
The name of the configuration to load from clouds.yaml.
:returns: openstack.connnection.Connection
"""
return openstack.connect(
cloud=cloud_name,
)


def check_for_member_role(conn: openstack.connection.Connection
) -> None:
"""Checks whether the current user has at maximum privileges
of the member role.
:param connection:
The current connection to an OpenStack cloud.
:returns: boolean, when role with most priviledges is member
"""

auth_data = conn.auth
auth_dict = {
"identity": {
"methods": ["password"],
"password": {
"user": {
"name": auth_data['username'],
"domain": {"name": auth_data['project_domain_name']},
"password": auth_data['password']
}
},
},
"scope": {
"project": {
"domain": {"name": auth_data['project_domain_name']},
"name": auth_data['project_name']
}
}
}

has_member_role = False
request = conn.session.request(auth_data['auth_url'] + '/v3/auth/tokens',
'POST',
json={'auth': auth_dict})
for role in json.loads(request.content)["token"]["roles"]:
role_name = role["name"]
if role_name == "admin" or role_name == "manager":
Comment thread
artificial-intelligence marked this conversation as resolved.
return False
elif role_name == "member":
print("User has member role.")
has_member_role = True
elif role_name == "reader":
print("User has reader role.")
else:
print("User has custom role.")
return False
return has_member_role


def check_presence_of_key_manager(cloud_name: str):
try:
connection = connect(cloud_name)
services = connection.service_catalog
except Exception as e:
print(str(e))
raise Exception(
f"Connection to cloud '{cloud_name}' was not successfully. "
f"The Catalog endpoint could not be accessed. "
f"Please check your cloud connection and authorization."
)

for svc in services:
svc_type = svc['type']
if svc_type == "key-manager":
# key-manager is present
# now we want to check whether a user with member role
# can create and access secrets
check_key_manager_permissions(connection)
return 0

# we did not find the key-manager service
logger.warning("There is no key-manager endpoint in the cloud.")
# we do not fail, until a key-manager MUST be present
return 0


def check_key_manager_permissions(conn: openstack.connection.Connection
) -> None:
"""
After checking that the current user only has the member and maybe the
reader role, this method verifies that the user with a member role
has sufficient access to the Key Manager API functionality.
"""
secret_name = "scs-member-role-test-secret"
if not check_for_member_role(conn):
logger.warning("Cannot test key-manager permissions. "
"User has wrong roles")
return None

def _find_secret(secret_name_or_id: str):
"""Replacement method for finding secrets.

Mimicks the behavior of Connection.key_manager.find_secret()
but fixes an issue with the internal implementation raising an
exception due to an unexpected microversion parameter.
"""
secrets = conn.key_manager.secrets()
for s in secrets:
if s.name == secret_name_or_id or s.id == secret_name_or_id:
return s
return None

try:
existing_secret = _find_secret(secret_name)
if existing_secret:
conn.key_manager.delete_secret(existing_secret)

conn.key_manager.create_secret(
name=secret_name,
payload_content_type="text/plain",
secret_type="opaque",
payload="foo"
)

new_secret = _find_secret(secret_name)
assert new_secret, (
f"Secret created with name '{secret_name}' was not discoverable by "
f"the user"
)
conn.key_manager.delete_secret(new_secret)
except openstack.exceptions.ForbiddenException as e:
print(
"Users of the 'member' role can use Key Manager API: FAIL"
)
print(
f"ERROR: {str(e)}"
)
exit(1)
print(
"Users of the 'member' role can use Key Manager API: PASS"
)


def main():
parser = argparse.ArgumentParser(
description="SCS Mandatory IaaS Service Checker")
parser.add_argument(
"--os-cloud", type=str,
help="Name of the cloud from clouds.yaml, alternative "
"to the OS_CLOUD environment variable"
)
parser.add_argument(
"--debug", action="store_true",
help="Enable OpenStack SDK debug logging"
)
args = parser.parse_args()
openstack.enable_logging(debug=args.debug)

# parse cloud name for lookup in clouds.yaml
cloud = os.environ.get("OS_CLOUD", None)
if args.os_cloud:
cloud = args.os_cloud
assert cloud, (
"You need to have the OS_CLOUD environment variable set to your cloud "
"name or pass it via --os-cloud"
)

return check_presence_of_key_manager(cloud)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we include the requirement of giving members access to the Key Manager API if present, we should move the corresponding test from the Roles Standard over to here:

def check_key_manager_permissions(conn: openstack.connection.Connection
) -> None:
"""
Limits the authentication to the "member" role using an application
credentials restricted to that role and verifies that the member role
has sufficient access to the Key Manager API functionality.
"""
secret_name = "scs-member-role-test-secret"
member_conn = reconnect_with_role(conn, "member")
def _find_secret(secret_name_or_id: str):
"""Replacement method for finding secrets.
Mimicks the behavior of Connection.key_manager.find_secret()
but fixes an issue with the internal implementation raising an
exception due to an unexpected microversion parameter.
"""
secrets = member_conn.key_manager.secrets()
for s in secrets:
if s.name == secret_name_or_id or s.id == secret_name_or_id:
return s
return None
try:
existing_secret = _find_secret(secret_name)
if existing_secret:
member_conn.key_manager.delete_secret(existing_secret)
member_conn.key_manager.create_secret(
name=secret_name,
payload_content_type="text/plain",
secret_type="opaque",
payload="foo"
)
new_secret = _find_secret(secret_name)
assert new_secret, (
f"Secret created with name '{secret_name}' was not discoverable by "
f"the user"
)
member_conn.key_manager.delete_secret(new_secret)
except openstack.exceptions.ForbiddenException as e:
print(
"Users of the 'member' role can use Key Manager API: FAIL"
)
print(
f"ERROR: {str(e)}"
)
exit(1)
finally:
delete_application_credential(conn, APP_CREDENTIAL_NAME)
print(
"Users of the 'member' role can use Key Manager API: PASS"
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started integrating the code until I stumbled about this:

keystoneauth1.exceptions.http.Unauthorized: Error authenticating with application credential: Application credentials cannot request a scope. (HTTP 401) (Request-ID: req-afca1cf7-0c4f-49d9-b716-44c7bb4f6f72)

Now we are investigating, whether there are other ways to ensure that a logged in user only has the member role within the specified project.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started integrating the code until I stumbled about this:

keystoneauth1.exceptions.http.Unauthorized: Error authenticating with application credential: Application credentials cannot request a scope. (HTTP 401) (Request-ID: req-afca1cf7-0c4f-49d9-b716-44c7bb4f6f72)

Now we are investigating, whether there are other ways to ensure that a logged in user only has the member role within the specified project.

@josephineSei mhm, weird, I didn't test it myself, but this should work?

see: https://opendev.org/openstack/keystone/commit/52da4d0e129048d1b808bdde07364cde698cf475

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a devstack that already partly includes the new secure RBAC roles. They are enabled in Keystone (and Nova and Glance I think), but not in Barbican. Maybe that is the reason for this behavior. I can take another look into this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I now check the roles of the current user, when there is an admin or manager role present (or any custom role), the check for the creation of a secret will be skipped and a warning logged.



if __name__ == "__main__":
main()