From 385f78c4f1457b533075ef017f0decef28c858af Mon Sep 17 00:00:00 2001
From: Daniel Wolf <95075445+wolfdn@users.noreply.github.com>
Date: Fri, 17 Apr 2026 11:10:31 +0200
Subject: [PATCH 1/2] Embed GPG keys in repo for Docker build

---
 .pre-commit-config.yaml                     |  11 +-
 Dockerfile                                  |  28 +----
 Dockerfile.ci                               |  28 +----
 scripts/ci/prek/inline_scripts_in_docker.py |   2 +
 scripts/ci/prek/update_docker_gpg_keys.py   | 104 +++++++++++++++++++
 scripts/docker/common.sh                    |  25 +----
 scripts/docker/install_os_dependencies.sh   |   2 +-
 scripts/docker/keys/mariadb.asc             |  51 +++++++++
 scripts/docker/keys/microsoft.asc           |  24 +++++
 scripts/docker/keys/postgres.asc            |  64 ++++++++++++
 scripts/docker/keys/python-3.10.asc         | 109 ++++++++++++++++++++
 11 files changed, 378 insertions(+), 70 deletions(-)
 create mode 100755 scripts/ci/prek/update_docker_gpg_keys.py
 create mode 100644 scripts/docker/keys/mariadb.asc
 create mode 100644 scripts/docker/keys/microsoft.asc
 create mode 100644 scripts/docker/keys/postgres.asc
 create mode 100644 scripts/docker/keys/python-3.10.asc

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 9ca611feb5c79..b6b4089fe7957 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -369,7 +369,8 @@ repos:
           ^.*pnpm-lock\.yaml$|
           .*/dist/.*|
           ^airflow-core/src/airflow/ui/public/i18n/locales/(?!en/).+/|
-          ^\.github/skills/airflow-translations/
+          ^\.github/skills/airflow-translations/|
+          ^scripts/docker/keys/.*\.asc$
         args:
           - --ignore-words=docs/spelling_wordlist.txt
           - --skip=providers/.*/src/airflow/providers/*/*.rst,providers/*/docs/changelog.rst,docs/*/commits.rst,providers/*/docs/commits.rst,providers/*/*/docs/commits.rst,docs/apache-airflow/tutorial/pipeline_example.csv,*.min.js,*.lock,INTHEWILD.md,*.svg
@@ -926,6 +927,14 @@ repos:
         pass_filenames: false
         files: ^Dockerfile$|^Dockerfile\.ci$|^scripts/docker/.*$
         require_serial: true
+      - id: update-docker-gpg-keys
+        name: Update Docker build GPG keys (manual)
+        entry: ./scripts/ci/prek/update_docker_gpg_keys.py
+        stages: ['manual']
+        language: python
+        pass_filenames: false
+        files: ^scripts/docker/keys/.*$|^scripts/ci/prek/update_docker_gpg_keys\.py$
+        require_serial: true
       - id: check-changelog-has-no-duplicates
         name: Check changelogs for duplicate entries
         language: python
diff --git a/Dockerfile b/Dockerfile
index 699f7355b027d..4bf05e0468fd0 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -438,7 +438,7 @@ function install_python() {
         GNUPGHOME="$(mktemp -d)"; export GNUPGHOME
         local gpg_key="${keys[${major_minor_version}]}"
         echo "Using GPG key ${gpg_key}"
-        gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "${gpg_key}"
+        gpg --batch --import "/scripts/docker/keys/python-${major_minor_version}.asc"
         gpg --batch --verify python.tar.xz.asc python.tar.xz
         gpgconf --kill all
         rm -rf "${GNUPGHOME}" python.tar.xz.asc
@@ -958,29 +958,10 @@ function common::import_trusted_gpg() {
 
     local key=${1:?${COLOR_RED}First argument expects OpenPGP Key ID${COLOR_RESET}}
     local name=${2:?${COLOR_RED}Second argument expected trust storage name${COLOR_RESET}}
-    # Please note that not all servers could be used for retrieve keys
-    #  sks-keyservers.net: Unmaintained and DNS taken down due to GDPR requests.
-    #  keys.openpgp.org: User ID Mandatory, not suitable for APT repositories
-    #  keyring.debian.org: Only accept keys in Debian keyring.
-    #  pgp.mit.edu: High response time.
-    local keyservers=(
-        "hkps://keyserver.ubuntu.com"
-        "hkps://pgp.surf.nl"
-    )
+    local key_file="/scripts/docker/keys/${name}.asc"
 
-    GNUPGHOME="$(mktemp -d)"
-    export GNUPGHOME
-    set +e
-    for keyserver in $(shuf -e "${keyservers[@]}"); do
-        echo "${COLOR_BLUE}Try to receive GPG public key ${key} from ${keyserver}${COLOR_RESET}"
-        gpg --keyserver "${keyserver}" --recv-keys "${key}" 2>&1 && break
-        echo "${COLOR_YELLOW}Unable to receive GPG public key ${key} from ${keyserver}${COLOR_RESET}"
-    done
-    set -e
-    gpg --export "${key}" > "/etc/apt/trusted.gpg.d/${name}.gpg"
-    gpgconf --kill all
-    rm -rf "${GNUPGHOME}"
-    unset GNUPGHOME
+    echo "${COLOR_BLUE}Installing GPG public key ${key} from ${key_file}${COLOR_RESET}"
+    gpg --dearmor < "${key_file}" > "/etc/apt/trusted.gpg.d/${name}.gpg"
 }
 EOF
 
@@ -1878,6 +1859,7 @@ ENV CARGO_HOME="/home/airflow/.cargo"
 ENV PATH="${CARGO_HOME}/bin:${PATH}"
 
 COPY --from=scripts install_os_dependencies.sh /scripts/docker/
+COPY scripts/docker/keys/ /scripts/docker/keys/
 RUN PYTHON_LTO=${PYTHON_LTO} bash /scripts/docker/install_os_dependencies.sh dev
 
 # In case system python is installed, setting LD_LIBRARY_PATH prevents any case the system python
diff --git a/Dockerfile.ci b/Dockerfile.ci
index f1934e0905b33..582ddaf7f340a 100644
--- a/Dockerfile.ci
+++ b/Dockerfile.ci
@@ -378,7 +378,7 @@ function install_python() {
         GNUPGHOME="$(mktemp -d)"; export GNUPGHOME
         local gpg_key="${keys[${major_minor_version}]}"
         echo "Using GPG key ${gpg_key}"
-        gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "${gpg_key}"
+        gpg --batch --import "/scripts/docker/keys/python-${major_minor_version}.asc"
         gpg --batch --verify python.tar.xz.asc python.tar.xz
         gpgconf --kill all
         rm -rf "${GNUPGHOME}" python.tar.xz.asc
@@ -898,29 +898,10 @@ function common::import_trusted_gpg() {
 
     local key=${1:?${COLOR_RED}First argument expects OpenPGP Key ID${COLOR_RESET}}
     local name=${2:?${COLOR_RED}Second argument expected trust storage name${COLOR_RESET}}
-    # Please note that not all servers could be used for retrieve keys
-    #  sks-keyservers.net: Unmaintained and DNS taken down due to GDPR requests.
-    #  keys.openpgp.org: User ID Mandatory, not suitable for APT repositories
-    #  keyring.debian.org: Only accept keys in Debian keyring.
-    #  pgp.mit.edu: High response time.
-    local keyservers=(
-        "hkps://keyserver.ubuntu.com"
-        "hkps://pgp.surf.nl"
-    )
+    local key_file="/scripts/docker/keys/${name}.asc"
 
-    GNUPGHOME="$(mktemp -d)"
-    export GNUPGHOME
-    set +e
-    for keyserver in $(shuf -e "${keyservers[@]}"); do
-        echo "${COLOR_BLUE}Try to receive GPG public key ${key} from ${keyserver}${COLOR_RESET}"
-        gpg --keyserver "${keyserver}" --recv-keys "${key}" 2>&1 && break
-        echo "${COLOR_YELLOW}Unable to receive GPG public key ${key} from ${keyserver}${COLOR_RESET}"
-    done
-    set -e
-    gpg --export "${key}" > "/etc/apt/trusted.gpg.d/${name}.gpg"
-    gpgconf --kill all
-    rm -rf "${GNUPGHOME}"
-    unset GNUPGHOME
+    echo "${COLOR_BLUE}Installing GPG public key ${key} from ${key_file}${COLOR_RESET}"
+    gpg --dearmor < "${key_file}" > "/etc/apt/trusted.gpg.d/${name}.gpg"
 }
 EOF
 
@@ -1683,6 +1664,7 @@ ENV PATH="${CARGO_HOME}/bin:${PATH}"
 ARG PYTHON_LTO
 
 COPY --from=scripts install_os_dependencies.sh /scripts/docker/
+COPY scripts/docker/keys/ /scripts/docker/keys/
 
 RUN PYTHON_LTO=${PYTHON_LTO} bash /scripts/docker/install_os_dependencies.sh ci
 
diff --git a/scripts/ci/prek/inline_scripts_in_docker.py b/scripts/ci/prek/inline_scripts_in_docker.py
index 0b277b6ddaa18..d11ae37dffcb6 100755
--- a/scripts/ci/prek/inline_scripts_in_docker.py
+++ b/scripts/ci/prek/inline_scripts_in_docker.py
@@ -45,6 +45,8 @@ def insert_content(file_path: Path, content: list[str], header: str, footer: str
 
     for file in [DOCKERFILE_FILE, DOCKERFILE_CI_FILE]:
         for script in SCRIPTS_DOCKER_DIR.iterdir():
+            if not script.is_file():
+                continue
             script_content = script.read_text().splitlines(keepends=True)
             no_comments_script_content = [
                 line for line in script_content if not line.startswith("#") or line.startswith("#!")
diff --git a/scripts/ci/prek/update_docker_gpg_keys.py b/scripts/ci/prek/update_docker_gpg_keys.py
new file mode 100755
index 0000000000000..26916d81d64e5
--- /dev/null
+++ b/scripts/ci/prek/update_docker_gpg_keys.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Fetch GPG public keys used during Docker image builds and store them in scripts/docker/keys/."""
+
+from __future__ import annotations
+
+import os
+import subprocess
+import sys
+import tempfile
+
+from common_prek_utils import AIRFLOW_ROOT_PATH
+
+KEYS_DIR = AIRFLOW_ROOT_PATH / "scripts" / "docker" / "keys"
+
+KEYSERVERS = [
+    "hkps://keyserver.ubuntu.com",
+    "hkps://pgp.surf.nl",
+    "hkps://keys.openpgp.org",
+]
+
+KEYS: dict[str, str] = {
+    # MariaDB APT repository signing key
+    "mariadb": "0xF1656F24C74CD1D8",
+    # PostgreSQL PGDG APT repository signing key
+    "postgres": "7FCC7D46ACCC4CF8",
+    # Microsoft APT repository signing key (MSSQL ODBC)
+    "microsoft": "EB3E94ADBE1229CF",
+    # Python 3.10 release manager (Pablo Galindo Salgado)
+    "python-3.10": "A035C8C19219BA821ECEA86B64E628F8D684696D",
+}
+
+
+def _recv_key(key_id: str, gpg_env: dict[str, str]) -> bool:
+    """Try to receive a GPG key from keyservers, trying each in order."""
+    for keyserver in KEYSERVERS:
+        result = subprocess.run(
+            ["gpg", "--batch", "--keyserver", keyserver, "--recv-keys", key_id],
+            env=gpg_env,
+            capture_output=True,
+            text=True,
+            check=False,
+        )
+        if result.returncode == 0:
+            return True
+        print(f"  Warning: {keyserver} failed for {key_id}: {result.stderr.strip()}", file=sys.stderr)
+    return False
+
+
+def fetch_and_export_keys() -> bool:
+    """Fetch all keys from keyservers and export as ASCII-armored files. Returns True if any changed."""
+    changed = False
+    KEYS_DIR.mkdir(parents=True, exist_ok=True)
+
+    with tempfile.TemporaryDirectory() as gnupghome:
+        gpg_env = {**os.environ, "GNUPGHOME": gnupghome}
+
+        for name, key_id in KEYS.items():
+            if not _recv_key(key_id, gpg_env):
+                print(f"ERROR: All keyservers failed for {key_id} ({name})", file=sys.stderr)
+                return False
+
+            result = subprocess.run(
+                ["gpg", "--export", "--armor", key_id],
+                env=gpg_env,
+                capture_output=True,
+                check=False,
+            )
+            if result.returncode != 0 or not result.stdout:
+                print(f"ERROR: Failed to export key {key_id} ({name})", file=sys.stderr)
+                return False
+
+            key_file = KEYS_DIR / f"{name}.asc"
+            new_content = result.stdout
+            if key_file.exists() and key_file.read_bytes() == new_content:
+                continue
+
+            key_file.write_bytes(new_content)
+            print(f"Updated {key_file.relative_to(AIRFLOW_ROOT_PATH)}")
+            changed = True
+
+    return changed
+
+
+if __name__ == "__main__":
+    changed = fetch_and_export_keys()
+    if changed:
+        print("\nGPG key files updated. Please review and commit the changes.")
+        sys.exit(1)
diff --git a/scripts/docker/common.sh b/scripts/docker/common.sh
index 6a941bdc34354..d8ebb2e261f6d 100644
--- a/scripts/docker/common.sh
+++ b/scripts/docker/common.sh
@@ -222,27 +222,8 @@ function common::import_trusted_gpg() {
 
     local key=${1:?${COLOR_RED}First argument expects OpenPGP Key ID${COLOR_RESET}}
     local name=${2:?${COLOR_RED}Second argument expected trust storage name${COLOR_RESET}}
-    # Please note that not all servers could be used for retrieve keys
-    #  sks-keyservers.net: Unmaintained and DNS taken down due to GDPR requests.
-    #  keys.openpgp.org: User ID Mandatory, not suitable for APT repositories
-    #  keyring.debian.org: Only accept keys in Debian keyring.
-    #  pgp.mit.edu: High response time.
-    local keyservers=(
-        "hkps://keyserver.ubuntu.com"
-        "hkps://pgp.surf.nl"
-    )
+    local key_file="/scripts/docker/keys/${name}.asc"
 
-    GNUPGHOME="$(mktemp -d)"
-    export GNUPGHOME
-    set +e
-    for keyserver in $(shuf -e "${keyservers[@]}"); do
-        echo "${COLOR_BLUE}Try to receive GPG public key ${key} from ${keyserver}${COLOR_RESET}"
-        gpg --keyserver "${keyserver}" --recv-keys "${key}" 2>&1 && break
-        echo "${COLOR_YELLOW}Unable to receive GPG public key ${key} from ${keyserver}${COLOR_RESET}"
-    done
-    set -e
-    gpg --export "${key}" > "/etc/apt/trusted.gpg.d/${name}.gpg"
-    gpgconf --kill all
-    rm -rf "${GNUPGHOME}"
-    unset GNUPGHOME
+    echo "${COLOR_BLUE}Installing GPG public key ${key} from ${key_file}${COLOR_RESET}"
+    gpg --dearmor < "${key_file}" > "/etc/apt/trusted.gpg.d/${name}.gpg"
 }
diff --git a/scripts/docker/install_os_dependencies.sh b/scripts/docker/install_os_dependencies.sh
index f351d4fbe0c54..5ccc8c74bc278 100644
--- a/scripts/docker/install_os_dependencies.sh
+++ b/scripts/docker/install_os_dependencies.sh
@@ -344,7 +344,7 @@ function install_python() {
         GNUPGHOME="$(mktemp -d)"; export GNUPGHOME
         local gpg_key="${keys[${major_minor_version}]}"
         echo "Using GPG key ${gpg_key}"
-        gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "${gpg_key}"
+        gpg --batch --import "/scripts/docker/keys/python-${major_minor_version}.asc"
         gpg --batch --verify python.tar.xz.asc python.tar.xz
         gpgconf --kill all
         rm -rf "${GNUPGHOME}" python.tar.xz.asc
diff --git a/scripts/docker/keys/mariadb.asc b/scripts/docker/keys/mariadb.asc
new file mode 100644
index 0000000000000..708d2063ad3e1
--- /dev/null
+++ b/scripts/docker/keys/mariadb.asc
@@ -0,0 +1,51 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+ukhXRwJXH8RVVEUS
+=ihRo
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/scripts/docker/keys/microsoft.asc b/scripts/docker/keys/microsoft.asc
new file mode 100644
index 0000000000000..da1fc5ccdd2b0
--- /dev/null
+++ b/scripts/docker/keys/microsoft.asc
@@ -0,0 +1,24 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQENBFYxWIwBCADAKoZhZlJxGNGWzqV+1OG1xiQeoowKhssGAKvd+buXCGISZJwT
+LXZqIcIiLP7pqdcZWtE9bSc7yBY2MalDp9Liu0KekywQ6VVX1T72NPf5Ev6x6DLV
+7aVWsCzUAF+eb7DC9fPuFLEdxmOEYoPjzrQ7cCnSV4JQxAqhU4T6OjbvRazGl3ag
+OeizPXmRljMtUUttHQZnRhtlzkmwIrUivbfFPD+fEoHJ1+uIdfOzZX8/oKHKLe2j
+H632kvsNzJFlROVvGLYAk2WRcLu+RjjggixhwiB+Mu/A8Tf4V6b+YppS44q8EvVr
+M+QvY7LNSOffSO6Slsy9oisGTdfE39nC7pVRABEBAAG0N01pY3Jvc29mdCAoUmVs
+ZWFzZSBzaWduaW5nKSA8Z3Bnc2VjdXJpdHlAbWljcm9zb2Z0LmNvbT6JATUEEwEC
+AB8FAlYxWIwCGwMGCwkIBwMCBBUCCAMDFgIBAh4BAheAAAoJEOs+lK2+EinPGpsH
+/32vKy29Hg51H9dfFJMx0/a/F+5vKeCeVqimvyTM04C+XENNuSbYZ3eRPHGHFLqe
+MNGxsfb7C7ZxEeW7J/vSzRgHxm7ZvESisUYRFq2sgkJ+HFERNrqfci45bdhmrUsy
+7SWw9ybxdFOkuQoyKD3tBmiGfONQMlBaOMWdAsic965rvJsd5zYaZZFI1UwTkFXV
+KJt3bp3Ngn1vEYXwijGTa+FXz6GLHueJwF0I7ug34DgUkAFvAs8Hacr2DRYxL5RJ
+XdNgj4Jd2/g6T9InmWT0hASljur+dJnzNiNCkbn9KbX7J/qK1IbR8y560yRmFsU+
+NdCFTW7wY0Fb1fWJ+/KTsC6JATQEEwEIAB4FAlYxWIwCGwMGCwkIBwMCAxUIAwMW
+AgECHgECF4AACgkQ6z6Urb4SKc+P9gf/diY2900wvWEgV7iMgrtGzx79W/PbwWiO
+kKoD9sdzhARXWiP8Q5teL/t5TUH6TZ3BENboDjwr705jLLPwuEDtPI9jz4kvdT86
+JwwG6N8gnWM8Ldi56SdJEtXrzwtlB/Fe6tyfMT1E/PrJfgALUG9MWTIJkc0GhRJo
+yPpGZ6YWSLGXnk4c0HltYKDFR7q4wtI84cBu4mjZHZbxIO6r8Cci+xxuJkpOTIpr
+4pdpQKpECM6x5SaT2gVnscbN0PE19KK9nPsBxyK4wW0AvAhed2qldBPTipgzPhqB
+2gu0jSryil95bKrSmlYJd1Y1XfNHno5Dxfn5JwgySBIdWWvtOI05gw==
+=dfGw
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/scripts/docker/keys/postgres.asc b/scripts/docker/keys/postgres.asc
new file mode 100644
index 0000000000000..443bf58487430
--- /dev/null
+++ b/scripts/docker/keys/postgres.asc
@@ -0,0 +1,64 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+=XSRU
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/scripts/docker/keys/python-3.10.asc b/scripts/docker/keys/python-3.10.asc
new file mode 100644
index 0000000000000..0cd2e1412fd92
--- /dev/null
+++ b/scripts/docker/keys/python-3.10.asc
@@ -0,0 +1,109 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBFq+ToQBEADRYvIVtbK6owynD3j3nxwpW2KEk/p+aDvtXmc2SR2dBcZ8sFW2
+R5vEsG8d3/D3wgv5pcL3KfNNXQYUnXVbobrFUUWQYc79qIsE3MgiPf5NVOtwKPUR
+i5g9YJgKvpBxkQfqp3LYGm9ZBtwo3DVLA3yn7KsazCmAgTNFJYw7ku1XxgmIzY6K
+5J30DfbJiqDqj4f9GslCCCCH3qiPnuLG/HUyVLHMpbWlaiy9NI0GcaLxjJewHj9w
+W2D2lydkxe5JGo7egUkV3ILcuLVSVKA35SKY27dYqfuyqp9tAzaRbjDYjsYdHA6G
+BqrNrKBn/GwlFDPrVdcvN3ZSY2wMLTxWE3Axc/FweuHxFnou/80FwX7F3JD+oEQ6
+rofmcxOBCC7J98I7HZAhP9jBn88XIS2hztbLq8d6rZJZRtcz0k61VR0ddO+TrFmf
+9rMYCPgCckRtVxeFIVIabrN1IzKynLFeo040h8hSGswd6YKDOVwjJY6Oa6EmVefZ
+a8QSt4+M65RSzH6SEPY008F3nJUAK6MEkzTak+tFltZNrVWu8p2xd1j9nmxAwEhZ
+/lgbxLqzYgaUWmfyHeZ8yVA0MhHzdiAL8nVUEdG3KecIq0RWCJLGLWWIjd6KAJl1
+yAmhRYKK/sjPDsL3elHsFACfZbyx3o5GGQNlas1FYoPLWbaNGaJtgFTF2QARAQAB
+tCtQYWJsbyBHYWxpbmRvIFNhbGdhZG8gPHBhYmxvZ3NhbEBnbWFpbC5jb20+iQJO
+BBMBCgA4FiEEoDXIwZIZuoIezqhrZOYo+NaEaW0FAlq+ToQCGwMFCwkIBwMFFQoJ
+CAsFFgIDAQACHgECF4AACgkQZOYo+NaEaW2bmA/+PXIap2udLoUVOHxnsIBdqYwp
+sv1Aj5lfIJmNhmxPbHShwp1Jg+w4urxe+2Dj5ofKVlIo1i83bQkvnKJMDXDVuc/K
+P6zqhBJ3rT4Q3qx2mzX8bIfQoJ2JHuH4lkP+I7doDcHHRyeNASyk72VdQmU4twNw
+Ibn8nSNV6ThKHdoPYzVnO2rZUFcGIqH5HNsvR+B7cc1MBCHsgURYwSVhSePIFGlZ
+iasdBD6QQkDSe4QWi7AcJFWFElw4kbOKJWxAWsrEk+tMXJVGRjnmL289EmPCx/vx
+BqKy7Mse0yWCSRR3vB+O6TB1S5SgEyEgqlYsfGNv1qf/rfRD4KkyCbNU3LhY1Aim
+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
+=XrQs
+-----END PGP PUBLIC KEY BLOCK-----

From eadc5bda148a354847fd662fb55dfdcf11ad348b Mon Sep 17 00:00:00 2001
From: Daniel Wolf <95075445+wolfdn@users.noreply.github.com>
Date: Fri, 17 Apr 2026 14:30:16 +0200
Subject: [PATCH 2/2] Fix build of prod image

---
 Dockerfile | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Dockerfile b/Dockerfile
index 4bf05e0468fd0..4655d398c46ca 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2131,6 +2131,8 @@ ENV PATH="${AIRFLOW_USER_HOME_DIR}/.local/bin:/usr/python/bin:${PATH}" \
 
 COPY --from=scripts common.sh /scripts/docker/
 
+COPY scripts/docker/keys/ /scripts/docker/keys/
+
 # Only copy mysql/mssql installation scripts for now - so that changing the other
 # scripts which are needed much later will not invalidate the docker layer here.
 COPY --from=scripts install_mysql.sh install_mssql.sh install_postgres.sh /scripts/docker/
