WORK IN PROGRESS
This page is intended to capture formative thoughts on how swift can handle name-based ACLs in the context of keystone v3 domains.
- 1 Problem statement
- 2 Discussion of forward looking options
- 3 Discussion of backwards compatibility issues
- 3.1 Option considered for determining tenant domain membership:
- 3.2 Alternative interpretation of goals
- 3.3 Recap
- 3.4 Multiple legacy domains
- 4 Miscellaneous
Swift keystoneauth middleware allows container ACLs to specify 'cross-tenant' access in the form tenant:user where tenant and user can be a UUID, a name or a wildcard *. With the introduction of domains with the keystone v3 API, names (of both tenants and users) are no longer globally unique and are only unique within a domain. Consequently, cross-tenant ACLs specified using unqualified names will be ambiguous.
Discussion of forward looking options
1. Allow only UUIDs (or wildcards) in ACLs when using keystone v3. This is enforced by patch https://review.openstack.org/#/c/86430/
2. Require domain qualified names in ACLs. An obstacle to this is that keystone currently has no reserved characters in names, and therefore no character that could be used as a separator in a qualified name. This may change if keystone adopts and enforces a normative way to express domain-qualified names (or hierarchical names). E.g. if keystone were to reserve the '@@' string then this could be used as a separator between user/tenant names and domain names then swift could accept ACLs having the form tenant_name@@domain_name:user_name@@domain_name.
3. Provide a new JSON encoded structured ACL specification in which names and domains are explicitly called out. This may require new header key(s) to discriminate from the legacy ACL format.
Discussion of backwards compatibility issues
Existing systems have unqualified names in ACLs. We need to continue to honor thoses ACLs as legacy users and tenants migrate to keystone v3.
For simplicity let's assume that all legacy users and tenants migrate to a single v3 domain which we will refer to as the 'legacy domain'. (keystone has the notion of a default domain, which may be named 'Default' and may have id 'default', but the default domain name and id are configurable during migration. Using the term 'legacy domain' may avoid confusion in this discussion.)
We assume that the legacy domain id is available to keystoneauth middleware via config.
(We will return later to discuss the implications of migrating legacy users and tenants to multiple domains).
We can state several goals:
Goal1: Existing unqualified-name ACLs ('legacy ACLs') continue to be honored for users and tenants migrated to the legacy domain.
Goal2: Users in the legacy domain can continue to specify ACLs using unqualified-names, with the restriction that they will only be granted to users also in the legacy domain.
Goal3: Users in non-legacy domains can specify ACLs using unqualified-names, with the restriction that they will only be granted to users also in the same domain.
To achieve Goal1 we need to verify that (a) a user is in the legacy domain and (b) that the tenant account being accessed is also in the legacy domain.
Achieving (a) is straightforward since the user's domain id will be included in the token info for the validated request token. Some caution is needed because keystone's authtoken middleware sets the user domain id to 'default' when no domain info is available (e.g. when processing a v2 token), and as discussed above this may not in fact be the id of the legacy domain.
Achieving (b) is harder since swift has no a priori knowledge of the tenant/account domain membership, and determining tenant domain membership is not straightforward...
Option considered for determining tenant domain membership:
1. Fetch from keystone
Request account/tenant domain id from keystone on-demand when legacy ACLs need to be applied. Note that swift's keystoneauth middleware does not currently make any requests to keystone (that is handled by the keystone authtoken middleware). Once retrieved from keystone the tenant domain id could be cached or persisted as account sysmeta.
2. Infer from ACL format
We could infer from the existence of a legacy ACL format that the tenant is in the legacy domain. This is only safe if we enforce that all ACLs created in non-legacy domains are domain-qualified. To enforce this we either need to know the domain membership of tenants when ACLs are set (i.e. we have moved the problem somewhere else) or we enforce that ALL new ACLs must use domain-qualified names, including new ACLs in the legacy domain, which prevents us achieving Goal2 and Goal3.
Discriminating permitted ACL format when ACLs are set
[On reflection, I'm not sure we can enforce that an ACL uses domain-qualified names when the ACL is being set, since the ACL value may actually be expressed in terms of id's, and I'm not sure we can discriminate between and id and a name. So this section may be irrelevant.]
Can we continue to allow legacy ACLs to be set in the legacy domain by discriminating when handling the container POST with an X-Container-[Read|Write]?
A. Allow legacy ACL format to be set only when request is scoped on a legacy tenant. When an ACL is set by a user with admin role the request token will be scoped to the tenant, so keystoneauth can easily determine if the domain is legacy domain or not. Unfortunately this is not the case when a user with reseller-admin role sets the ACL - those tokens do not need to be scoped to the tenant. REJECT.
B. Allow legacy ACL to be set only when user (granter) is in legacy domain. The user's domain id is available in the request's token info, including users with reseller-admin role. But this would allow legacy users to set legacy ACLs on non-legacy tenants if they are given the admin role. REJECT.
3. Discover from ACL POST request
When a container POST with an X-Container-[Read|Write] header is received, inspect the token info for tenant domain info.
- v3 token, admin user role: token is scoped on tenant so tenant domain id is available. Store this as sysmeta on container.
- v3 token, reseller admin role: token may not be scoped on tenant. Store tenant domain id sysmeta = unknown
- v2 token - tenant must be in legacy domain, store no tenant domain sysmeta
When validating ACL:
- if tenant domain sysmeta does not exist -> ACL was set using a v2 token -> tenant is legacy -> allow unqualified names in ACL
- if tenant domain sysmeta = id_x -> tenant domain id is known -> only allow unqualified ACL is user domain == tenant domain
- if tenant domain sysmeta = unknown -> tenant may be in a non-legacy domain -> do not allow unqualified names in ACL
This approach looks hopeful...The final case is the wrinkle i.e. a reseller admin with a v3 token sets an ACL using unqualified names, keystoneauth cannot learn the tenant domain id when the ACL is set, so the only safe option is to deny the ACL. Is it acceptable to require that reseller admins should know better??
Alternative interpretation of goals
How about if we allow legacy ACLs to be set in any domain but only grant a legacy ACL only when the granter and user requesting access are in the same domain?
To enable this we would persist the domain id of the granter when an ACL is set, and then compare this to the domain id of the user requesting access (both are always available in the token info). The granter request id could be persisted as an item of container metadata e.g. X-Container-Sysmeta-[Read|Write]-Granter.
This results in a slightly different semantic: a user in domainX could set a legacy ACL in a tenant in domainY but the ACL would only be granted to other users in domainX. Perhaps that is acceptable?
One potentially confusing consequence would be that a user in domainZ could not update the ACL without inadvertantly revoking access to users in domainX, because the granter's domain would be switched from domainX to domainZ.
We can achieve Goal1 (existing legacy ACLs continue to be honored in the legacy domain) most easily by requiring ALL future ACLs to use domain-qualified names, including new or updated in the legacy domain. This of course also assumes that keystone provides an unambiguous means to express domain-qualified names e.g. reserved separator characters.
[This requires being able to discriminate between names and IDs which I'm not sure is possible - see note above - so not sure we have yet achieved Goal1]
To achieve Goal2 we require swift keystoneauth to be aware of tenant's domain membership, and the only tractable way identified to achieve that is to make on demand queries to keystone.
Multiple legacy domains
It may be that legacy users and tenants are migrated to multiple domains. In that case the notion of legacy domain in the above discussion needs to change to a collection of domains. The ids of all domains in the legacy collection must be made known to keystoneauth in place of the single legacy domain id, or a regex pattern for the collection.
1. In some instances global name uniqueness will be enforced independently of keystone. In this case an override config flag could be used to permit unqualified names to continue to be used in ACLs. This is provided by the current patch.
2. We could re-write all existing ACLs to a new domain qualified format. Unlikely to be a popular choice.