This started with a simple requirement: “I’d like to know when users or projects are added or removed and who did the action”
As it turns out there’s no great way to do this. Sure you can log it when a user is deleted:
"DELETE /v2.0/users/702b12ec7f0e4f7d93945eebb95705e1 HTTP/1.1" 204 - "-" "python-keystoneclient"
The only problem is that ‘702b12ec7f0e4f7d93945eebb95705e1’ is meaningless without the DB entry which is now conveniently gone.
But if you had an async way to get events from Keystone, you could solve this yourself. That was my idea with my Keystone CADF Event Logger tool. Before we dive into the tool, some quick background on CADF events. You can read the DMTF mumbo-jumbo at the link in the previous sentence, but just know, Keystone CADF events log anything interesting that happens in Keystone. They also tell you who did it, from where they did it, and when they did it. All important things for auditing. (This article from Steve Martinelli has some more great background)
So how does this solve my problem? CADF events still just log ids, not names. My solution was a simple rabbit consuming async daemon that cached a user and project names locally and used it to do lookups. Here’s an example of what it does:
Logs user auth events
Note that V2 doesn’t log much info on these, although that is fixed in Liberty I believe.
INFO 2015-09-24 15:09:27.172 USER AUTH: success: nova INFO 2015-09-24 15:09:27.524 USER AUTH: success: icinga INFO 2015-09-24 15:09:27.800 USER AUTH: success: neutron INFO 2015-09-24 15:09:27.800 USER AUTH: failure: neutron
Log user/project crud events
Note again V2 issues here with Kilo leave us with less than full info.
USER CREATED: success: user ffflll at 2015-09-18 16:00:10.426372 by unknown (unknown) (project: unknown (unknown)). USER DELETED: success: user ffflll at 2015-09-18 16:02:13.196172 by unknown (unknown) (project: unknown (unknown)).
Figures it out when rabbit goes away
INFO 2015-11-11 20:46:59.325 Connecting to 188.8.131.52:5672 ERROR 2015-11-11 22:16:59.514 Socket Error: 104 WARNING 2015-11-11 22:16:59.515 Socket closed when connection was open WARNING 2015-11-11 22:16:59.515 Disconnected from RabbitMQ at top-secret-internal-company-url.com:5672 (0): Not specified WARNING 2015-11-11 22:16:59.516 Connection closed, reopening in 5 seconds: (0) Not specified
This requires that Keystone is configured to talk to rabbit and emit CADF events. The previously referenced blog from Steve Martinelli has good info on this. Here’s what I set:
This code also assumes that /var/log/keystone_cadf is there and writable. I setup this with puppet in my environment.
You should ensure Keystone is talking to Rabbit and has made the queues and exchanges before trying the program.
I designed this to run in a docker container, which explains the overly full requirements.txt, you can probably get away with the requirements.txt.ORIG. After you build it (python ./setup.py build && python ./setup.py install, just run it by passing in creds for Keystone and for RabbitMQ. You can also use environment variables which is I how I ran in it my docker container.
source openrc keystone-cadf-logger --rabbit_user rabbit --rabbit-pass pass1 --rabbit-host dev-lb.twc.net
So what issues exist with this? First some small ones. The code that parses the events is horrible and I hate it, but it worked. You can probably improve it. Second, the big issue. In our environment this code introduced a circular dependency between our control nodes, where rabbit runs, and our keystone nodes which now need to talk to rabbit. For this reason, we ended up not deploying this code, even thought I had all the puppet and docker portions working. If you don’t have this issue, then this code will work well for you. I also don’t have much operating experience with this, it might set all your disks on fire and blow up in spectacular fashion. I planned to deploy it to our dev environment and tweak things as needed. So if you operate it, do it cautiously.
If you are interested in more event types, just change the on_message code. You might also want to change the action that happens. Right now it just logs, but how about emailing the team anytime a user is removed or noting it in your team chat.
This code consists of a few parts and I hope at least some of it is useful to someone. It was fun to write and I was a bit disappointed that we couldn’t fully use it, but I hope that something in here, even if it’s just the async rabbit code might be useful to you. But what about our requirement? Well, we’ll probably still log CADF events locally on the Keystone node and consume them, or we might write a pipeline filter that does something similar, whatever we decide I will update on this site. So please pull the code and play with it!