Authored by George Ke with Maxime Beauchemin on 2016-07-26

Overview

We want to add the ability to impersonate a unix user for a subprocess run.

Motivation

We need feature parity with Chronos tasks for the upcoming Chronos migration at Airbnb.

Currently, all tasks are run as the same `airflow` user on a given worker. This makes it impossible to log who ran which task. With impersonation, commands run by operators can be ran as and logged as the user who wrote the task.

Security is also a concern, as right now all subprocesses have the same privileges as the parent process. Although we are not specifically tackling security, we want to make sure we don't regress or leave blatant security holes.

Additionally, we want to keep in mind a long-term goal of having a system that manages secrets that Airflow and its tasks need. We want to have the config & secrets that Airflow needs to be completely separate from the config & secrets operators need. Ideally, we would have a vault that manages secrets and provides what's needed to a user with a lease attached to it.  
 

Solutions

There have been many solutions proposed, listed below in chronological order. 
Solution 1: Simple
Sharing `airflow.cfg` and logging files with the user, (Link to commit)
 
Solution 2: Pickles
Passing in a pickled subset of `airflow.cfg` to `airflow run --raw` (Link to commit)
Solution 3: Forking
Forking the parent process and calling Python's `os.setuid` to change the user, (Link to commit)
Solution 4: Operators
Operator and Hook level impersonation
Solution 5: Custom File
Parent process creates a temporary custom config file for the child to read.

Other Considerations

  1. Right now all airflow boxes are run as `airflow` user. To do impersonation, we need to run as a `sudo`er. This means that all existing jobs will be run as `sudo`er instead of `airflow`. This avoid this, we can allow the scheduler to be run as a `sudo`er but lower down to `airflow` before we run any tasks. This may either require a default "lower user" in `airflow.cfg` or having all operators set `unix_impersonate` so that it they don't run as `sudo`.

Conclusion

We are strongly leaning towards Solution 5 as it solves our following needs: 

We want clear guarantees for unix impersonation, implemented at the BaseOperator level and baked into the framework.  The guarantee we want is simple: when using the `BaseOperator.unix_impersonate` (var name may change) the operator's execute method and hooks of a task instance will be executed as that user. Solution 5 is a blanket solution that fulfills this guarantee.

On top of that, individual hooks and operators that require impersonation beyond the Unix user itself should expose a `run_as` parameter with custom logic.
For instance someone maybe want the HiveOperator to run as the unix user, in which case `BaseOperator.unix_impersonate` would be applied. In another environment, someone may want to use the `HiveOperator.run_as` which essentially prepends the HQL command with a `SET proxy.user = {run_as}`. Both scenarios are legitimate, people may want either, and both will be possible with Solution 5.