Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bitnami/airflow] Airflow container errors out if any AIRFLOW_LDAP_ environment variable contains a single quote #65217

Closed
zanecodes opened this issue Apr 16, 2024 · 3 comments · May be fixed by #66534
Assignees
Labels
airflow solved stale 15 days without activity tech-issues The user has a technical issue about an application

Comments

@zanecodes
Copy link
Contributor

Name and Version

bitnami/airflow:2 (currently 2.9.0-debian-12-r1)

What architecture are you using?

amd64

What steps will reproduce the bug?

Run the following commands:

wget https://raw.githubusercontent.com/bitnami/containers/9dfe0b6070e38a57829b879e0b870cf7c94095ed/bitnami/airflow/docker-compose-ldap.yml
echo -e '{"services":{"airflow":{"environment":["AIRFLOW_LDAP_BIND_PASSWORD=adminpassword'\''"]}}}' > docker-compose-override.yml
docker compose --file docker-compose-ldap.yml --file docker-compose-override.yml up

This will fetch the current example docker-compose-ldap.yml from the bitnami/containers GitHub repository and override the AIRFLOW_LDAP_BIND_PASSWORD environment variable in the airflow service to adminpassword' (note the trailing single quote).

What is the expected behavior?

The airflow-1 container should come up successfully.

What do you see instead?

The airflow-1 container errors out immediately after the Starting Airflow stage, with the following Python traceback:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/opt/bitnami/airflow/venv/lib/python3.11/site-packages/gunicorn/__main__.py", line 7, in <module>
    run()
  File "/opt/bitnami/airflow/venv/lib/python3.11/site-packages/gunicorn/app/wsgiapp.py", line 67, in run
    WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]").run()
  File "/opt/bitnami/airflow/venv/lib/python3.11/site-packages/gunicorn/app/base.py", line 236, in run
    super().run()
  File "/opt/bitnami/airflow/venv/lib/python3.11/site-packages/gunicorn/app/base.py", line 72, in run
    Arbiter(self).run()
    ^^^^^^^^^^^^^
  File "/opt/bitnami/airflow/venv/lib/python3.11/site-packages/gunicorn/arbiter.py", line 58, in __init__
    self.setup(app)
  File "/opt/bitnami/airflow/venv/lib/python3.11/site-packages/gunicorn/arbiter.py", line 118, in setup
    self.app.wsgi()
  File "/opt/bitnami/airflow/venv/lib/python3.11/site-packages/gunicorn/app/base.py", line 67, in wsgi
    self.callable = self.load()
                    ^^^^^^^^^^^
  File "/opt/bitnami/airflow/venv/lib/python3.11/site-packages/gunicorn/app/wsgiapp.py", line 58, in load
    return self.load_wsgiapp()
           ^^^^^^^^^^^^^^^^^^^
  File "/opt/bitnami/airflow/venv/lib/python3.11/site-packages/gunicorn/app/wsgiapp.py", line 48, in load_wsgiapp
    return util.import_app(self.app_uri)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/bitnami/airflow/venv/lib/python3.11/site-packages/gunicorn/util.py", line 424, in import_app
    app = app(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^
  File "/opt/bitnami/airflow/venv/lib/python3.11/site-packages/airflow/www/app.py", line 187, in cached_app
    app = create_app(config=config, testing=testing)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/bitnami/airflow/venv/lib/python3.11/site-packages/airflow/www/app.py", line 83, in create_app
    flask_app.config.from_pyfile(webserver_config, silent=True)
  File "/opt/bitnami/airflow/venv/lib/python3.11/site-packages/flask/config.py", line 185, in from_pyfile
    exec(compile(config_file.read(), filename, "exec"), d.__dict__)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/bitnami/airflow/webserver_config.py", line 137
    AUTH_LDAP_BIND_PASSWORD = 'adminpassword''
                                             ^
SyntaxError: unterminated string literal (detected at line 137)

Additional information

This happens because the airflow_webserver_conf_set function in /opt/bitnami/scripts/libairflow.sh simply wraps the configuration values in single quotes without escaping any single quotes in the value when adding new values to webserver_config.py, as well as when updating existing values.

The issue can be partially mitigated by changing line 332 to is_boolean_yes "$is_literal" && new_value="'${value//"'"/\'}'" and line 326 to is_boolean_yes "$is_literal" && entry="${key} = '${value//"'"/\'}'" || entry="${key} = ${value}". This will escape all single quotes in "literal" values with a backslash. However, additional logic will also be needed to escape backslashes in values, and to handle non-"literal" configuration values, such as True & False (which would be considerably more complex).

A more bulletproof approach might instead be to append a minimal Python script to /opt/bitnami/airflow/webserver_config.py, which would deserialize configuration values as JSON from an environment variable or file, something like the following:

import json
globals().update(json.load(open('webserver_config.json')))

Then airflow_webserver_conf_set could be updated to serialize the provided configuration values to JSON using a tool such as jq, for example:

########################
# Set properties in webserver_config.json
# Globals:
#   AIRFLOW_*
# Arguments:
#   None
# Returns:
#   None
#########################
airflow_webserver_conf_set() {
    local -r key="${1:?missing key}"
    local -r value="${2:?missing key}"
    local -r is_literal="${3:-no}"
    shift 2

    local -r arg_type='--argjson'
    is_boolean_yes "$is_literal" && arg_type='--arg'

    jq --arg key "$key" "$arg_type" value "$value" '.[$key] |= $value' webserver_config.json | tee webserver_config.json
}

This would eliminate all issues with escaping configuration values, while still allowing users to pass literals such as True & False, as well as more complex values such as the dictionaries used by AIRFLOW_LDAP_ROLES_MAPPING. This would be a potentially-breaking change for users using AIRFLOW_LDAP_ROLES_MAPPING, since it would now be JSON instead of a Python dictionary literal. Additionally, care would have to be taken to ensure the existing configuration override behavior is maintained. if an existing webserver_config.py is provided.

This affects all configuration values that are written using airflow_webserver_conf_set, namely:

Values containing single quotes are not valid for many of these anyway, but are valid for AIRFLOW_LDAP_BIND_PASSWORD and AIRFLOW_LDAP_TLS_CA_CERTIFICATE, and possibly also AIRFLOW_LDAP_SEARCH and AIRFLOW_LDAP_ROLES_MAPPING.

Since webserver_config.py is executed by Airflow on startup, this also enables arbitrary Python code injection in the Airflow process via any of the AIRFLOW_LDAP_ environment variables. This could be considered a vulnerability, although an attacker with sufficient access to alter those environment variables is already in a position to do much worse things anyway.

@zanecodes zanecodes added the tech-issues The user has a technical issue about an application label Apr 16, 2024
@github-actions github-actions bot added the triage Triage is needed label Apr 16, 2024
@javsalgar javsalgar changed the title Airflow container errors out if any AIRFLOW_LDAP_ environment variable contains a single quote [bitnami/airflow] Airflow container errors out if any AIRFLOW_LDAP_ environment variable contains a single quote Apr 18, 2024
@github-actions github-actions bot removed the triage Triage is needed label Apr 18, 2024
@github-actions github-actions bot assigned jotamartos and unassigned carrodher Apr 18, 2024
@jotamartos
Copy link
Contributor

Hi @zanecodes,

Thank you for taking the time to create this ticket. As I mentioned in your other ticket, would you like to contribute? You can follow our contributing guidelines and the whole community will benefit from your change.

Thanks

Copy link

github-actions bot commented May 9, 2024

This Issue has been automatically marked as "stale" because it has not had recent activity (for 15 days). It will be closed if no further activity occurs. Thanks for the feedback.

Copy link

Due to the lack of activity in the last 5 days since it was marked as "stale", we proceed to close this Issue. Do not hesitate to reopen it later if necessary.

@bitnami-bot bitnami-bot closed this as not planned Won't fix, can't repro, duplicate, stale May 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
airflow solved stale 15 days without activity tech-issues The user has a technical issue about an application
Projects
None yet
5 participants