Heat/Associate-users-with-templates-using-keystone

= Roadmap Feature: Associate users with templates using keystone =

Introduction
The goal of this document is to explore the authentication system and how it can be used to associate users with specific templates in Heat. It has also become a more generic look at authentication in general.

Authentication Overview
We are currently authenticating with Keystone. Keystone is implemented as a web service which the application calls into in order to perform authentication.

We are interested in getting the username in heat engine so that we can add that to the database as another key so that queries are unique to the user creating/deleting stacks.

Some Observations About the Code
While looking through the code to determine how authentication works, and how we might implement this feature, I noticed that we are only authenticating using the 'Parser' class. This means there are a few ways to sneak passed authentication in our API. For example, the 'list events' call never creates a Parser for a template and so no authentication is done. There are also cases such as 'delete' where the stack name is looked up and an error returned before the Parser class is instantiated and so another user could guess stack names (security flaw).

A lesser side effect of this is that we are actually authenticating in the 'Resource' class (which are created by the parser for different element/resource types). We could easily be calling authentication methods many times per call. Not a big deal but not ideal either. For example, the 'Instance' resource type will call to keystone every time it is instantiated and there can be many 'instances' in a template.

The resource class does serve some function however, it is authenticating against the specific service type. So for example to create an instance we have to authenticate against the 'nova' service type, where creating a volume requires authenticating against the 'volume' service.

Perhaps the right idea is to authenticate against the 'heat' service type immediately in the implementation and then authenticate against the others in the appropriate resource handler.

Keystone and EC2 euca tools.
Another question was around using EC2 style authentication with keystone and whether the username would map. It appears one solution is to set your "EC2_ACCESS_KEY" to your keystone username (or "$KEYSTONE_USERNAME:$KEYSTONE_TENANT" depending on how you set it up) and "EC2_SECRET_KEY" to your password. I haven't tested this but it seems like it should be supported in our infrastructure.

In order to use EC2 style access keys as per the fedora setup instructions:

http://fedoraproject.org/wiki/Getting_started_with_OpenStack_on_Fedora_17#Using_Eucalyptus_tools

Which results in:

$ keystone ec2-credentials-list ++--+--+ ++--+--+ ++--+--+
 * tenant |             access              |              secret              |
 * admin | 4daeb1fe741844c5ad51705a3ceff938 | bc66d563349d491ca8345e3de4419698 |

We will have to add similar code to that in the nova API found in:

nova/nova/api/ec2/ init .py:259

which queries keystone and retrieves the normal credential information for the user before passing it on to the engine.

Note that this relates to this issue: https://github.com/heat-api/heat/issues/91

Recommendations
- We should do as is mentioned in git issue #91 and have code similar to that found in nova api. This involves using boto to access the API and then getting the heat API to contact keystone for authentication as per nova/nova/api/ec2/ init .py:259. The information returned from that call must then be fed into the security context sent to the engine via RPC. In the engine we will have to deal with the context information in a different manner for ec2 style authenticated connections. The other option is to do all authentication in the heat API so that the same context information can be sent to the engine for all auth types.

- I think we should authenticate against the 'heat' service type in keystone immediately upon entering the implementation of the call in the engine. This should be done such that the username is easily available. Authentication/security is important and I think it should be clearly defined and not as it is now where the authentication calls are made deep within another class.

- Once this is done we can pull out the username from the authentication call (we could pull it from the parser class now but as noted above that has issues).

- A migration should be added to add a username column to the 'stack' table and every newly created stack should be associated with the given username.

- All selection queries in the database api (heat/db/sqlalchemy/api.py) need to include a username argument which gets verified against the stack to which the object is associated. Only those objects which relate to the specified users' stack will be returned.

Testing with multiple users
First, set up Heat using the instructions provided in the [Getting Started Guide](https://github.com/heat-api/heat/blob/master/docs/GettingStarted.rst#readme). Source the credentials saved in the file `~/.openstack/keystonerc` by the `tools/openstack install` script to allow admin access to Keystone for adding more users.

$ source ~/.openstack/keystonerc

You can optionally create a separate Tenant to associate the new users with. A tenant in OpenStack is an organisation with its own separate set resources. Each user may be granted particular roles within each tenant.

$ keystone tenant-create --name test-org +-+--+ +-+--+ +-+--+
 * Property |              Value               |
 * description | None                            |
 * enabled    | True                             |
 * id         | 318feba7a8434c0d862f1b1ea6af682f |
 * name       | test-org                         |

If you prefer to add users to an existing tenant, you can get a list from Keystone:

$ keystone tenant-list +--++-+ +--++-+ +--++-+
 * id               |        name        | enabled |
 * 2c19538c046f4173b89db60806858bf6 | admin             | True    |
 * 91a3f9e8ed0147ada0828a27b14b8deb | demo              | True    |
 * 93118730840143258dd75db65f4cf956 | service           | True    |
 * bf9e6a341d4c4bcc98f10a0d59771b0c | invisible_to_admin | True   |

You can now proceed to create the user(s) in Keystone:

$ keystone user-create --name test-user-1 --tenant_id 318feba7a8434c0d862f1b1ea6af682f --pass user1pass --email test-user-1@example.com +--+-+ +--+-+ +--+-+
 * Property |                                                         Value                                                          |
 * email   | test-user-1@example.com                                                                                                 |
 * enabled | True                                                                                                                    |
 * id      | 9e75c27389e14d02ada0d4d0d2695f0a                                                                                        |
 * name    | test-user-1                                                                                                             |
 * password | $6$rounds=40000$W1AmcplPA/ADHikL$PAt4DtMnRsQYscXBEcHsSxEfz6AXb//nxOBImphRup39Vr8xIXjhW43CfZIEpcWebzy2WAh/U6WHdpOUD.O/T/ |
 * tenantId | 318feba7a8434c0d862f1b1ea6af682f                                                                                       |

Finally, to allow the user to create Stacks in Heat they must be assigned the admin role for the tenant.

$ keystone role-list +--+--+ +--+--+ +--+--+ $ $ keystone user-role-add --user 9e75c27389e14d02ada0d4d0d2695f0a --role 132dfe5d20ae4386bfedef0b68f00677 --tenant_id 318feba7a8434c0d862f1b1ea6af682f
 * id               |         name         |
 * 132dfe5d20ae4386bfedef0b68f00677 | admin               |
 * 3d1c4c9e8798485da5f1d8ecec6f2e9c | KeystoneAdmin       |
 * 5715c0fecb6145b1a342562a6cfcd52c | Member              |
 * 5cbb8dda66ed41f89f0357f9b22fbeac | KeystoneServiceAdmin |
 * d472337644a041d78bf60e6db9003223 | netadmin            |
 * e782b293227843c5820b2bd106d44c39 | sysadmin            |

An easy way to perform operations as a particular OpenStack user is to save the credentials in a `keystonerc` file similar to `~/.openstack/keystonerc`. The file should look a bit like this:

export OS_USERNAME=test-user-1 export OS_PASSWORD=user1pass export OS_TENANT_NAME=example-org export OS_AUTH_URL=http://127.0.0.2:5000/v2.0/ export OS_AUTH_STRATEGY=keystone

For templates that require a public SSH key to be registered (which includes most of the example templates), you need to register the keypair with Nova. The list of keypairs in Nova is per-user, so you must add the key separately for each OpenStack user.

$ source test-user-1.keystonerc $ nova keypair-add --pub_key ~/.ssh/id_rsa.pub ${USER}_key