control delete permission separately from write permission
rptaylor opened this issue · 4 comments
What is the idea ?
ARA_WRITE_LOGIN_REQUIRED
"Determines if authentication is required before being authorized to post data"
ARA_READ_LOGIN_REQUIRED
"Determines if authentication is required before being authorized to query all API endpoints"
If interpreted literally, it sounds like ARA_WRITE_LOGIN_REQUIRED might only allow POST operations.
But the CLI and API support delete operations for plays, tasks, etc. which I am guessing would be considered a write operation and would be controlled by ARA_WRITE_LOGIN_REQUIRED.
Considering that this is based on Django REST framework permissions, it would be useful to have documented exactly which operations are controlled by these settings.
What I really want is to allow unauthenticated append (POST) operations, but require authentication for all read (GET, HEAD) and delete (DELETE) operations. The use case is having a large fleet of servers and not wanting to configure write credentials on them, and in particular to allow the plays to run quickly without being slowed down by Django authentication when writing output to the server.
Our consideration on security is that it's not terribly bad if a malicious actor can write fake data to ARA without authentication, but we can not allow reading, deletion, or modification of existing data without authentication.
It's hard to tell from the docs but hopefully Ara is already append-only, i.e. records written to the database should be immutable, so PUT and PATCH are never allowed by the API?
Anyway if I interpreted the docs correctly, would it be possible to have something like ARA_DELETE_LOGIN_REQUIRED, so that unauthenticated (append-only) writes are possible, but deletion requires authentication?
I found more documentation here that clarifies "you can enable authentication against the API for read (ex: GET) and write (ex: DELETE, POST, PATCH) requests."
Those three types of operations (delete a record, write a new record, change an existing record) could have very different security considerations from each other. Would it make sense to make new settings, something like ARA_DELETE_LOGIN_REQUIRED, ARA_CREATE_LOGIN_REQUIRED, ARA_CHANGE_LOGIN_REQUIRED to control each of them separately? To preserve compatibility, the value of each one could default to ARA_WRITE_LOGIN_REQUIRED.
It looks like the code for that is here but I'm not sure how the Django REST framework works.
Hey @rptaylor and thanks for the issue.
I understand the use case and it's a valid one.
The ARA_WRITE_LOGIN_REQUIRED
and ARA_READ_LOGIN_REQUIRED
settings control the behavior of the built-in Django authentication backend. You've found the entirety of that code already: it is designed to be simple with just a few lines of code.
Generally speaking, there are no plans to implement more granular permissions or full-blown RBAC in ara at this time.
However, what users with similar use cases have done in the past -- and it is actually what the documentation recommends -- is to use a reverse proxy (such as nginx or apache) in front of the server to handle authentication and authorization instead of django.
Handling authentication in django has non-negligible performance drawbacks and using a reverse proxy gives you the flexibility of handling different endpoints (or HTTP methods) according to your needs.
I don't have an example on hand right now but in both apache and nginx, you can have different authentication methods (or none at all) depending on whether it is a GET, a POST/PUT/PATCH or a DELETE.
In fact there is an issue about supporting a "read-only" configuration via nginx in ara-collection which would leverage this mechanism: ansible-community/ara-collection#50
It's hard to tell from the docs but hopefully Ara is already append-only, i.e. records written to the database should be immutable, so PUT and PATCH are never allowed by the API?
The database is not immutable. The web reporting interface does not implement edition or deletion (on purpose) but the CLI provides commands to delete and prune playbooks. There are no implementations that allow users to edit things that I know of but the API allows for editing.
For example, the callback first does a POST to create a playbook in the database when the playbook begins and then once the playbook ends it sends a PATCH to update the end date and duration of the playbook.
There's nothing that uses PUT right now, at least to my knowledge.
I hope this helps!
If you end up writing an apache or nginx configuration that works for you, we can consider adding it to the docs.
Handling authentication in django has non-negligible performance drawbacks and using a reverse proxy gives you the flexibility of handling different endpoints (or HTTP methods) according to your needs.
Yes, but I was hoping to improve performance by allowing writes to be performed without requiring a login, bypassing the slowness of Django authn.
I am running Ara on k8s , it would be nice if it had built in functionality for this. If I have to make my own authenticating proxy I would maybe try implementing it with Traefik ingress rules and middlewares but that could be complex.
Anyway a question @dmsimard , I noticed the admin interface allows you to add user permissions such as "API | file | Can add file" etc, but these don't seem to do anything? Do all users have all capabilities regardless of what permissions are set there?
Thanks!
Sorry for not replying until now, @rptaylor.
I am not all that familiar with the django admin interface. It is possible that these knobs might work but that would be news to me. In other words: there is no intentional support for granular RBAC but it could be possible that it comes "for free" like many things in django.
If it works we can add it to the docs.
Otherwise, to my knowledge, either you have access to everything or you don't. The loose concept of "read only" comes from leaving ARA_READ_LOGIN_REQUIRED
to false while ARA_WRITE_LOGIN_REQUIRED
is true.
You can also implement a similar concept with a reverse proxy in front by filtering on POST/PATCH/DELETE like I mentioned in my previous comment.