Keystone, il cuore di OpenStack

10 novembre 2014

Fabrizio Soppelsa

- RHCE, Autore di Linux&C, Autore di Linux Journal

OpenStack

Keystone è la parte di OpenStack che coordina il funzionamento di tutta la famiglia di servizi. Keystone, noto come Identity Service, si occupa di progetti, utenti, gruppi, ruoli, autorizzazioni, autenticazione e molto altro. Si capisce quindi come sia d’obbligo, in un ambiente OpenStack di produzione, il funzionamento perfetto di Keystone: ogni volta che si effettua una qualsiasi operazione su un qualsiasi servizio (come gestire le VM, lo storage, la rete, gli oggetti), Keystone genera e gestisce i token delle API, verifica i permessi (chi può fare cosa e su quali progetti) e permette o impedisce che le operazioni vadano a buon fine.

184540-OpenStack_services

Se pensiamo a OpenStack come a un albergo, allora ci possono essere ospiti (utenti) più o meno raggruppati (gruppi) che possono (permessi) accedere alle stanze (progetti) e c’è un gestore (admin) che può tutto. Come una stanza d’albergo è configurabile – numero di letti, TV, Internet – così sono i progetti di OpenStack, che si chiamano tenant. Al posto dei servizi come i letti, nei tenant di OpenStack si parla invece di quote di calcolo, capacità di storage, numero di interfacce di rete virtuali autorizzate.

Gestire tenant, utenti e permessi fa parte dei compiti quotidiani di un amministratore di sistema OpenStack. Vediamo quindi qualche esempio concreto.

Il comando da linea che permette di usare Keystone si chiama keystone, e con keystone help si può ottenere l’help in linea. La configurazione si trova in /etc/keystone e i log sono di solito posizionati in /var/log/keystone.

Prima di tutto, osserviamo quali servizi sono abilitati in questo ambiente OpenStack:

root@node-1:~# keystone service-list
+----------------------------------+----------+----------------+----------------------------------+
|                id                |   name   |      type      |           description            |
+----------------------------------+----------+----------------+----------------------------------+
| 04c5b1a7813f45e0a09fee9462841b5d |  cinder  |     volume     |          Cinder Service          |
| 6787f6d754ed4696aa39c3d3276e8fd2 | cinderv2 |    volumev2    |        Cinder Service v2         |
| 5a0ae04ea5b4451c95603f2393adc098 |  glance  |     image      |     Openstack Image Service      |
| 7371748974d147f5b9aacac8cd2c004a |   heat   | orchestration  | Openstack Orchestration Service  |
| 012c7e36815f4f7b97efc2f6858582dc | heat-cfn | cloudformation | Openstack Cloudformation Service |
| af06ee444cfa445ca016a1d0b8e2fa2e | keystone |    identity    |    OpenStack Identity Service    |
| 181703412f2949829a40596f615d25c2 | neutron  |    network     |    Neutron Networking Service    |
| f5339c43a2584d7db189af4d0e49e5e4 |   nova   |    compute     |    Openstack Compute Service     |
| 3432144627ad4e94b002dce627660964 | nova_ec2 |      ec2       |           EC2 Service            |
| e4ab30b65890413991ceca2efffff43b |  swift   |  object-store  |  Openstack Object-Store Service  |
| 088c8979c9994cd49cc1f95716d2cf31 | swift_s3 |       s3       |       Openstack S3 Service       |
+----------------------------------+----------+----------------+----------------------------------+

Senza addentrarci troppo in dettagli implementativi delle installazioni, bisogna parlare del concetto di endpoint. In ogni deployment OpenStack, un servizio deve registrarsi per farsi riconoscere all’interno dell’ambiente. Parte della registrazione è registrare gli endpoint: gli endpoint definiscono gli URL delle API del servizio. Ecco per esempio nella mia installazione l’endpoint per il servizio compute (gestione VM):

root@node-1:~# keystone endpoint-get --service compute
+-------------------+------------------------------------------------------------+
|      Property     |                           Value                            |
+-------------------+------------------------------------------------------------+
| compute.publicURL | http://172.16.0.2:8774/v2/a63e920ac1dc45e6acd7f4f624752cc7 |
+-------------------+------------------------------------------------------------+

Iniziamo adesso a lavorare sull’ambiente. Incominciamo con la gestione dei tenant. Innanzitutto come visualizzarne la lista:

root@node-1:~# keystone tenant-list
+----------------------------------+----------+---------+
|                id                |   name   | enabled |
+----------------------------------+----------+---------+
| a63e920ac1dc45e6acd7f4f624752cc7 |  admin   |   True  |
| 0b3e324fe7fe42c0844c229b91641b10 | services |   True  |
+----------------------------------+----------+---------+

In questa installazione abbiamo due tenant abilitati: uno admin, generale dell’amministratore, e uno denominato services, che salva le configurazioni dei servizi. A questa lista possiamo aggiungere un nostro nuovo tenant:

root@node-1:~# keystone tenant-create --name extraordy
+-------------+----------------------------------+
|   Property  |              Value               |
+-------------+----------------------------------+
| description |                                  |
|   enabled   |               True               |
|      id     | 3f4825739692434082be71db711d3ca9 |
|     name    |            extraordy             |
+-------------+----------------------------------+

Familiarizziamo con questo output tabulare dei comandi, perché standard per tutti, e con il fatto che a ogni oggetto OpenStack viene creato con un ID univoco. Questo è molto importante, perché spesso l’amministratore OpenStack si trova a dover intersecare oggetti (come VM e rete) e deve farlo tramite i rispettivi ID.

Senza sorprese, per ottenere l’elenco degli utenti, si usa il seguente comando:

root@node-1:~# keystone user-list
+----------------------------------+-------------------+---------+--------------------+
|                id                |        name       | enabled |       email        |
+----------------------------------+-------------------+---------+--------------------+
| a4482efcf8b84ef6be9393dfd150f387 |       admin       |   True  | admin@example.org  |
| f543721e7a98461c9dd4c59a1cdf4502 |       cinder      |   True  |  cinder@localhost  |
| d85fce571d4245a985475ee35b193106 |       glance      |   True  |  glance@localhost  |
| 301725a46aed4f79a4a285f7ecfb19ea |        heat       |   True  |   heat@localhost   |
| 41fce7428f6142449e7c8e41940375a7 |      heat-cfn     |   True  | heat-cfn@localhost |
| a05364bd75ea4a15a3cef578c44e4bb0 |      neutron      |   True  | neutron@localhost  |
| 010843a37f9c425a813711aa60217614 |        nova       |   True  |   nova@localhost   |
| 2a7245a5c1b044328bbe4e5b7aa1d7b1 |       swift       |   True  |                    |
| 13c29954440b4a49964a2ad6fc8e3e1b | tmp_neutron_admin |   True  |                    |
+----------------------------------+-------------------+---------+--------------------+

Ho stampato l’elenco completo perché è interessante notare come vengano di solito configurati, in un ambiente OpenStack di produzione, utenti relativi ai servizi (cinder, glance, heat e così via). Questo perché Keystone è la vera colla che tiene insieme tutto: i servizi devono essere autorizzati per funzionare (ricordate il tenant services?), perciò sono presenti come utenti.

Creiamo ora un utente e assegniamogli come tenant di riferimento quello che abbiamo appena creato, passandogli l’ID:

root@node-1:~# keystone user-create --name fabrizio --pass s3cr3t --email fsoppelsa@extraordy.com --tenant-id 3f4825739692434082be71db711d3ca9
+----------+----------------------------------+
| Property |              Value               |
+----------+----------------------------------+
|  email   |     fsoppelsa@extraordy.com      |
| enabled  |               True               |
|    id    | 69f8a02ea9b54d2eaac7aab0d1a6fe08 |
|   name   |             fabrizio             |
| tenantId | 3f4825739692434082be71db711d3ca9 |
| username |             fabrizio             |
+----------+----------------------------------+

La domanda ora è: questo utente è associato a un tenant okay, ma cosa può fare? Vediamo i ruoli in questo ambiente:

root@node-1:~# keystone role-list
+----------------------------------+-----------------+
|                id                |       name      |
+----------------------------------+-----------------+
| 84549a4f9e404d76bfd33a674577e153 |      Member     |
| 64432a224eb14d3586c4ab2e0c2f46f0 |  SwiftOperator  |
| 9fe2ff9ee4384b1894a90878d3e92bab |     _member_    |
| 7247693c0caf41b48b5d86d2a1c938a2 |      admin      |
| 31596d0dd8a44d0585c2faacffa7aebd | heat_stack_user |
+----------------------------------+-----------------+

Possiamo associare Member al nostro nuovo utente per il nostro tenant:

root@node-1:~# keystone user-role-add \
> --user-id 69f8a02ea9b54d2eaac7aab0d1a6fe08 \
> --role-id 84549a4f9e404d76bfd33a674577e153 \
> --tenant-id 3f4825739692434082be71db711d3ca9
root@node-1:~#

Purtroppo la granularità delle autorizzazioni (per esempio cosa può fare Member nel dettaglio sui servizi) non si può gestire con keystone da riga di comando, ma bisogna modificare un file di configurazione denominato policy.json che si trova in ogni /etc/SERVIZIO, per esempio in /etc/nova/policy.json.

In genere, le installazioni di default fatte con i tool o con i moduli puppet configurano già ottimalmente i servizi. Se osserviamo per esempio /etc/nova/policy.json della mia installazione, possiamo notare che:

"compute:start": "rule:admin_or_owner"
...
"network:associate": "",
...

Da questo estratto si capisce come vanno gestiti i ruoli. Per ogni operazione (in questo caso su Nova), vengono definite delle regole. Chi può fare cosa? Lanciare un’istanza (compute:start) è prerogativa dell’admin o dell’owner della VM. Associare una rete a una VM (network:associate) è permesso a chiunque abbia accesso alle operazioni sul tenant.

Infine, per tornare ai ruoli della lista di prima (ricordate heat_stack_user?) nella mia installazione, viene impedito a chiunque non abbia associato il ruolo heat_stack_user di usare heat:

root@node-1:~# grep deny_stack_user /etc/heat/policy.json 
    "deny_stack_user": "not role:heat_stack_user",

Complicato? Fortunatamente, questa di modificare i policy.json è un’operazione di solito molto infrequente che occorre effettuare solo in caso di tuning molto spinto sui permessi.

Dopo questa panoramica su Keystone, nel prossimo articolo inizieremo ad addentrarci nel cavallo di battaglia di OpenStack: il servizio di virtualizzazione, o Nova. A presto!