Validate SMTP server certificate on STARTTLS upgrade - #65346

Merged
potiuk merged 2 commits into
apache:mainfrom
potiuk:validate-cert-on-smtp-starttls
Apr 16, 2026
Merged

Validate SMTP server certificate on STARTTLS upgrade#65346
potiuk merged 2 commits into
apache:mainfrom
potiuk:validate-cert-on-smtp-starttls

Conversation

@potiuk

Copy link
Copy Markdown
Member

Summary

smtplib.SMTP.starttls() does not validate the server certificate unless an SSL context is passed. airflow.utils.email.send_mime_email and the SMTP provider's SmtpHook (both sync get_conn and async aget_conn) were calling starttls() without a context, so the STARTTLS upgrade skipped certificate validation.

This PR passes the existing SSL-context machinery to starttls() at all three call sites:

  • airflow-core/src/airflow/utils/email.py — factored the SSL-context lookup out of _get_smtp_connection into a helper _get_ssl_context(), driven by the existing email.ssl_context config. The helper is now called by both the SMTP_SSL path and the new STARTTLS path.
  • providers/smtp/src/airflow/providers/smtp/hooks/smtp.py — factored the SSL-context lookup into _build_ssl_context(), driven by the existing ssl_context connection extra. The helper is called for SMTP_SSL (sync), for aiosmtplib.SMTP construction (async), and for the explicit starttls() calls in both get_conn and aget_conn.

Default behaviour change

The default SSL context is now ssl.create_default_context(), which validates against the system's trusted CAs. Environments that intentionally use self-signed or otherwise non-validating SMTP servers can opt out:

  • Core: set email.ssl_context = "none" in airflow.cfg
  • Provider: set ssl_context: "none" in the SMTP connection extras

The opt-out already existed for the SMTP_SSL path; it now applies to the STARTTLS path too.

Test plan

  • airflow-core/tests/unit/utils/test_email.py — 25/25 pass
  • providers/smtp/tests/unit/smtp/hooks/test_smtp.py — 34/34 pass (including sync test_ehlo_called_after_starttls and async test_async_connection updated to verify starttls is invoked with an ssl.SSLContext)
Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.6 (1M context)

Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment threadairflow-core/newsfragments/65346.significant.rst
@potiuk
potiuk merged commit 06981d4 into apache:mainApr 16, 2026
95 checks passed
@potiuk
potiuk deleted the validate-cert-on-smtp-starttls branch April 16, 2026 10:01
@github-actionsgithub-actionsBot added this to the Airflow 3.2.2 milestone Apr 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi maintainer, this PR was merged without a milestone set.
We've automatically set the milestone to Airflow 3.2.2 based on: backport label targeting v3-2-test
If this milestone is not correct, please update it to the appropriate milestone.

This comment was generated by Milestone Tag Assistant.

github-actionsBot pushed a commit that referenced this pull request Apr 16, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

github-actionsBot pushed a commit to aws-mwaa/upstream-to-airflow that referenced this pull request Apr 16, 2026
…che#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
karenbraganz pushed a commit to karenbraganz/airflow that referenced this pull request Apr 16, 2026
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 23, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
potiuk added a commit that referenced this pull request Apr 26, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
potiuk added a commit that referenced this pull request Apr 26, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 27, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
sehaj-sandhu added a commit to acceldata-io/airflow that referenced this pull request Jun 30, 2026
Backport of apache#65346 (06981d4),
upstream fixed in 3.2.2. With smtp_starttls=True / smtp_ssl=False, starttls()
was called with no SSL context, so the upgraded connection skipped certificate
validation (CWE-295) and was open to MITM credential capture. Resolve the SSL
context once and pass it to starttls() so STARTTLS validates the cert exactly
like implicit-SSL does.
- providers/smtp/hooks/smtp.py: extract _get_ssl_context(); use for starttls()
- utils/email.py: extract _get_email_ssl_context(); use for starttls()
Context "none" still disables validation by explicit opt-in only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@potiuk@eladkal
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Validate SMTP server certificate on STARTTLS upgrade - #65346

Merged
potiuk merged 2 commits into
apache:mainfrom
potiuk:validate-cert-on-smtp-starttls
Apr 16, 2026
Merged

Validate SMTP server certificate on STARTTLS upgrade#65346
potiuk merged 2 commits into
apache:mainfrom
potiuk:validate-cert-on-smtp-starttls

Conversation

@potiuk

Copy link
Copy Markdown
Member

Summary

smtplib.SMTP.starttls() does not validate the server certificate unless an SSL context is passed. airflow.utils.email.send_mime_email and the SMTP provider's SmtpHook (both sync get_conn and async aget_conn) were calling starttls() without a context, so the STARTTLS upgrade skipped certificate validation.

This PR passes the existing SSL-context machinery to starttls() at all three call sites:

  • airflow-core/src/airflow/utils/email.py — factored the SSL-context lookup out of _get_smtp_connection into a helper _get_ssl_context(), driven by the existing email.ssl_context config. The helper is now called by both the SMTP_SSL path and the new STARTTLS path.
  • providers/smtp/src/airflow/providers/smtp/hooks/smtp.py — factored the SSL-context lookup into _build_ssl_context(), driven by the existing ssl_context connection extra. The helper is called for SMTP_SSL (sync), for aiosmtplib.SMTP construction (async), and for the explicit starttls() calls in both get_conn and aget_conn.

Default behaviour change

The default SSL context is now ssl.create_default_context(), which validates against the system's trusted CAs. Environments that intentionally use self-signed or otherwise non-validating SMTP servers can opt out:

  • Core: set email.ssl_context = "none" in airflow.cfg
  • Provider: set ssl_context: "none" in the SMTP connection extras

The opt-out already existed for the SMTP_SSL path; it now applies to the STARTTLS path too.

Test plan

  • airflow-core/tests/unit/utils/test_email.py — 25/25 pass
  • providers/smtp/tests/unit/smtp/hooks/test_smtp.py — 34/34 pass (including sync test_ehlo_called_after_starttls and async test_async_connection updated to verify starttls is invoked with an ssl.SSLContext)
Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.6 (1M context)

Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment threadairflow-core/newsfragments/65346.significant.rst
@potiuk
potiuk merged commit 06981d4 into apache:mainApr 16, 2026
95 checks passed
@potiuk
potiuk deleted the validate-cert-on-smtp-starttls branch April 16, 2026 10:01
@github-actionsgithub-actionsBot added this to the Airflow 3.2.2 milestone Apr 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi maintainer, this PR was merged without a milestone set.
We've automatically set the milestone to Airflow 3.2.2 based on: backport label targeting v3-2-test
If this milestone is not correct, please update it to the appropriate milestone.

This comment was generated by Milestone Tag Assistant.

github-actionsBot pushed a commit that referenced this pull request Apr 16, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

github-actionsBot pushed a commit to aws-mwaa/upstream-to-airflow that referenced this pull request Apr 16, 2026
…che#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
karenbraganz pushed a commit to karenbraganz/airflow that referenced this pull request Apr 16, 2026
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 23, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
potiuk added a commit that referenced this pull request Apr 26, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
potiuk added a commit that referenced this pull request Apr 26, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 27, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
sehaj-sandhu added a commit to acceldata-io/airflow that referenced this pull request Jun 30, 2026
Backport of apache#65346 (06981d4),
upstream fixed in 3.2.2. With smtp_starttls=True / smtp_ssl=False, starttls()
was called with no SSL context, so the upgraded connection skipped certificate
validation (CWE-295) and was open to MITM credential capture. Resolve the SSL
context once and pass it to starttls() so STARTTLS validates the cert exactly
like implicit-SSL does.
- providers/smtp/hooks/smtp.py: extract _get_ssl_context(); use for starttls()
- utils/email.py: extract _get_email_ssl_context(); use for starttls()
Context "none" still disables validation by explicit opt-in only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@potiuk@eladkal
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Validate SMTP server certificate on STARTTLS upgrade - #65346

Merged
potiuk merged 2 commits into
apache:mainfrom
potiuk:validate-cert-on-smtp-starttls
Apr 16, 2026
Merged

Validate SMTP server certificate on STARTTLS upgrade#65346
potiuk merged 2 commits into
apache:mainfrom
potiuk:validate-cert-on-smtp-starttls

Conversation

@potiuk

Copy link
Copy Markdown
Member

Summary

smtplib.SMTP.starttls() does not validate the server certificate unless an SSL context is passed. airflow.utils.email.send_mime_email and the SMTP provider's SmtpHook (both sync get_conn and async aget_conn) were calling starttls() without a context, so the STARTTLS upgrade skipped certificate validation.

This PR passes the existing SSL-context machinery to starttls() at all three call sites:

  • airflow-core/src/airflow/utils/email.py — factored the SSL-context lookup out of _get_smtp_connection into a helper _get_ssl_context(), driven by the existing email.ssl_context config. The helper is now called by both the SMTP_SSL path and the new STARTTLS path.
  • providers/smtp/src/airflow/providers/smtp/hooks/smtp.py — factored the SSL-context lookup into _build_ssl_context(), driven by the existing ssl_context connection extra. The helper is called for SMTP_SSL (sync), for aiosmtplib.SMTP construction (async), and for the explicit starttls() calls in both get_conn and aget_conn.

Default behaviour change

The default SSL context is now ssl.create_default_context(), which validates against the system's trusted CAs. Environments that intentionally use self-signed or otherwise non-validating SMTP servers can opt out:

  • Core: set email.ssl_context = "none" in airflow.cfg
  • Provider: set ssl_context: "none" in the SMTP connection extras

The opt-out already existed for the SMTP_SSL path; it now applies to the STARTTLS path too.

Test plan

  • airflow-core/tests/unit/utils/test_email.py — 25/25 pass
  • providers/smtp/tests/unit/smtp/hooks/test_smtp.py — 34/34 pass (including sync test_ehlo_called_after_starttls and async test_async_connection updated to verify starttls is invoked with an ssl.SSLContext)
Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.6 (1M context)

Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment threadairflow-core/newsfragments/65346.significant.rst
@potiuk
potiuk merged commit 06981d4 into apache:mainApr 16, 2026
95 checks passed
@potiuk
potiuk deleted the validate-cert-on-smtp-starttls branch April 16, 2026 10:01
@github-actionsgithub-actionsBot added this to the Airflow 3.2.2 milestone Apr 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi maintainer, this PR was merged without a milestone set.
We've automatically set the milestone to Airflow 3.2.2 based on: backport label targeting v3-2-test
If this milestone is not correct, please update it to the appropriate milestone.

This comment was generated by Milestone Tag Assistant.

github-actionsBot pushed a commit that referenced this pull request Apr 16, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

github-actionsBot pushed a commit to aws-mwaa/upstream-to-airflow that referenced this pull request Apr 16, 2026
…che#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
karenbraganz pushed a commit to karenbraganz/airflow that referenced this pull request Apr 16, 2026
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 23, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
potiuk added a commit that referenced this pull request Apr 26, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
potiuk added a commit that referenced this pull request Apr 26, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 27, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
sehaj-sandhu added a commit to acceldata-io/airflow that referenced this pull request Jun 30, 2026
Backport of apache#65346 (06981d4),
upstream fixed in 3.2.2. With smtp_starttls=True / smtp_ssl=False, starttls()
was called with no SSL context, so the upgraded connection skipped certificate
validation (CWE-295) and was open to MITM credential capture. Resolve the SSL
context once and pass it to starttls() so STARTTLS validates the cert exactly
like implicit-SSL does.
- providers/smtp/hooks/smtp.py: extract _get_ssl_context(); use for starttls()
- utils/email.py: extract _get_email_ssl_context(); use for starttls()
Context "none" still disables validation by explicit opt-in only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@potiuk@eladkal
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Validate SMTP server certificate on STARTTLS upgrade - #65346

Merged
potiuk merged 2 commits into
apache:mainfrom
potiuk:validate-cert-on-smtp-starttls
Apr 16, 2026
Merged

Validate SMTP server certificate on STARTTLS upgrade#65346
potiuk merged 2 commits into
apache:mainfrom
potiuk:validate-cert-on-smtp-starttls

Conversation

@potiuk

Copy link
Copy Markdown
Member

Summary

smtplib.SMTP.starttls() does not validate the server certificate unless an SSL context is passed. airflow.utils.email.send_mime_email and the SMTP provider's SmtpHook (both sync get_conn and async aget_conn) were calling starttls() without a context, so the STARTTLS upgrade skipped certificate validation.

This PR passes the existing SSL-context machinery to starttls() at all three call sites:

  • airflow-core/src/airflow/utils/email.py — factored the SSL-context lookup out of _get_smtp_connection into a helper _get_ssl_context(), driven by the existing email.ssl_context config. The helper is now called by both the SMTP_SSL path and the new STARTTLS path.
  • providers/smtp/src/airflow/providers/smtp/hooks/smtp.py — factored the SSL-context lookup into _build_ssl_context(), driven by the existing ssl_context connection extra. The helper is called for SMTP_SSL (sync), for aiosmtplib.SMTP construction (async), and for the explicit starttls() calls in both get_conn and aget_conn.

Default behaviour change

The default SSL context is now ssl.create_default_context(), which validates against the system's trusted CAs. Environments that intentionally use self-signed or otherwise non-validating SMTP servers can opt out:

  • Core: set email.ssl_context = "none" in airflow.cfg
  • Provider: set ssl_context: "none" in the SMTP connection extras

The opt-out already existed for the SMTP_SSL path; it now applies to the STARTTLS path too.

Test plan

  • airflow-core/tests/unit/utils/test_email.py — 25/25 pass
  • providers/smtp/tests/unit/smtp/hooks/test_smtp.py — 34/34 pass (including sync test_ehlo_called_after_starttls and async test_async_connection updated to verify starttls is invoked with an ssl.SSLContext)
Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.6 (1M context)

Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment threadairflow-core/newsfragments/65346.significant.rst
@potiuk
potiuk merged commit 06981d4 into apache:mainApr 16, 2026
95 checks passed
@potiuk
potiuk deleted the validate-cert-on-smtp-starttls branch April 16, 2026 10:01
@github-actionsgithub-actionsBot added this to the Airflow 3.2.2 milestone Apr 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi maintainer, this PR was merged without a milestone set.
We've automatically set the milestone to Airflow 3.2.2 based on: backport label targeting v3-2-test
If this milestone is not correct, please update it to the appropriate milestone.

This comment was generated by Milestone Tag Assistant.

github-actionsBot pushed a commit that referenced this pull request Apr 16, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

github-actionsBot pushed a commit to aws-mwaa/upstream-to-airflow that referenced this pull request Apr 16, 2026
…che#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
karenbraganz pushed a commit to karenbraganz/airflow that referenced this pull request Apr 16, 2026
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 23, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
potiuk added a commit that referenced this pull request Apr 26, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
potiuk added a commit that referenced this pull request Apr 26, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 27, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
sehaj-sandhu added a commit to acceldata-io/airflow that referenced this pull request Jun 30, 2026
Backport of apache#65346 (06981d4),
upstream fixed in 3.2.2. With smtp_starttls=True / smtp_ssl=False, starttls()
was called with no SSL context, so the upgraded connection skipped certificate
validation (CWE-295) and was open to MITM credential capture. Resolve the SSL
context once and pass it to starttls() so STARTTLS validates the cert exactly
like implicit-SSL does.
- providers/smtp/hooks/smtp.py: extract _get_ssl_context(); use for starttls()
- utils/email.py: extract _get_email_ssl_context(); use for starttls()
Context "none" still disables validation by explicit opt-in only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@potiuk@eladkal
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Validate SMTP server certificate on STARTTLS upgrade - #65346

Merged
potiuk merged 2 commits into
apache:mainfrom
potiuk:validate-cert-on-smtp-starttls
Apr 16, 2026
Merged

Validate SMTP server certificate on STARTTLS upgrade#65346
potiuk merged 2 commits into
apache:mainfrom
potiuk:validate-cert-on-smtp-starttls

Conversation

@potiuk

Copy link
Copy Markdown
Member

Summary

smtplib.SMTP.starttls() does not validate the server certificate unless an SSL context is passed. airflow.utils.email.send_mime_email and the SMTP provider's SmtpHook (both sync get_conn and async aget_conn) were calling starttls() without a context, so the STARTTLS upgrade skipped certificate validation.

This PR passes the existing SSL-context machinery to starttls() at all three call sites:

  • airflow-core/src/airflow/utils/email.py — factored the SSL-context lookup out of _get_smtp_connection into a helper _get_ssl_context(), driven by the existing email.ssl_context config. The helper is now called by both the SMTP_SSL path and the new STARTTLS path.
  • providers/smtp/src/airflow/providers/smtp/hooks/smtp.py — factored the SSL-context lookup into _build_ssl_context(), driven by the existing ssl_context connection extra. The helper is called for SMTP_SSL (sync), for aiosmtplib.SMTP construction (async), and for the explicit starttls() calls in both get_conn and aget_conn.

Default behaviour change

The default SSL context is now ssl.create_default_context(), which validates against the system's trusted CAs. Environments that intentionally use self-signed or otherwise non-validating SMTP servers can opt out:

  • Core: set email.ssl_context = "none" in airflow.cfg
  • Provider: set ssl_context: "none" in the SMTP connection extras

The opt-out already existed for the SMTP_SSL path; it now applies to the STARTTLS path too.

Test plan

  • airflow-core/tests/unit/utils/test_email.py — 25/25 pass
  • providers/smtp/tests/unit/smtp/hooks/test_smtp.py — 34/34 pass (including sync test_ehlo_called_after_starttls and async test_async_connection updated to verify starttls is invoked with an ssl.SSLContext)
Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.6 (1M context)

Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment threadairflow-core/newsfragments/65346.significant.rst
@potiuk
potiuk merged commit 06981d4 into apache:mainApr 16, 2026
95 checks passed
@potiuk
potiuk deleted the validate-cert-on-smtp-starttls branch April 16, 2026 10:01
@github-actionsgithub-actionsBot added this to the Airflow 3.2.2 milestone Apr 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi maintainer, this PR was merged without a milestone set.
We've automatically set the milestone to Airflow 3.2.2 based on: backport label targeting v3-2-test
If this milestone is not correct, please update it to the appropriate milestone.

This comment was generated by Milestone Tag Assistant.

github-actionsBot pushed a commit that referenced this pull request Apr 16, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

github-actionsBot pushed a commit to aws-mwaa/upstream-to-airflow that referenced this pull request Apr 16, 2026
…che#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
karenbraganz pushed a commit to karenbraganz/airflow that referenced this pull request Apr 16, 2026
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 23, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
potiuk added a commit that referenced this pull request Apr 26, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
potiuk added a commit that referenced this pull request Apr 26, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 27, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
sehaj-sandhu added a commit to acceldata-io/airflow that referenced this pull request Jun 30, 2026
Backport of apache#65346 (06981d4),
upstream fixed in 3.2.2. With smtp_starttls=True / smtp_ssl=False, starttls()
was called with no SSL context, so the upgraded connection skipped certificate
validation (CWE-295) and was open to MITM credential capture. Resolve the SSL
context once and pass it to starttls() so STARTTLS validates the cert exactly
like implicit-SSL does.
- providers/smtp/hooks/smtp.py: extract _get_ssl_context(); use for starttls()
- utils/email.py: extract _get_email_ssl_context(); use for starttls()
Context "none" still disables validation by explicit opt-in only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@potiuk@eladkal
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Validate SMTP server certificate on STARTTLS upgrade - #65346

Merged
potiuk merged 2 commits into
apache:mainfrom
potiuk:validate-cert-on-smtp-starttls
Apr 16, 2026
Merged

Validate SMTP server certificate on STARTTLS upgrade#65346
potiuk merged 2 commits into
apache:mainfrom
potiuk:validate-cert-on-smtp-starttls

Conversation

@potiuk

Copy link
Copy Markdown
Member

Summary

smtplib.SMTP.starttls() does not validate the server certificate unless an SSL context is passed. airflow.utils.email.send_mime_email and the SMTP provider's SmtpHook (both sync get_conn and async aget_conn) were calling starttls() without a context, so the STARTTLS upgrade skipped certificate validation.

This PR passes the existing SSL-context machinery to starttls() at all three call sites:

  • airflow-core/src/airflow/utils/email.py — factored the SSL-context lookup out of _get_smtp_connection into a helper _get_ssl_context(), driven by the existing email.ssl_context config. The helper is now called by both the SMTP_SSL path and the new STARTTLS path.
  • providers/smtp/src/airflow/providers/smtp/hooks/smtp.py — factored the SSL-context lookup into _build_ssl_context(), driven by the existing ssl_context connection extra. The helper is called for SMTP_SSL (sync), for aiosmtplib.SMTP construction (async), and for the explicit starttls() calls in both get_conn and aget_conn.

Default behaviour change

The default SSL context is now ssl.create_default_context(), which validates against the system's trusted CAs. Environments that intentionally use self-signed or otherwise non-validating SMTP servers can opt out:

  • Core: set email.ssl_context = "none" in airflow.cfg
  • Provider: set ssl_context: "none" in the SMTP connection extras

The opt-out already existed for the SMTP_SSL path; it now applies to the STARTTLS path too.

Test plan

  • airflow-core/tests/unit/utils/test_email.py — 25/25 pass
  • providers/smtp/tests/unit/smtp/hooks/test_smtp.py — 34/34 pass (including sync test_ehlo_called_after_starttls and async test_async_connection updated to verify starttls is invoked with an ssl.SSLContext)
Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.6 (1M context)

Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment threadairflow-core/newsfragments/65346.significant.rst
@potiuk
potiuk merged commit 06981d4 into apache:mainApr 16, 2026
95 checks passed
@potiuk
potiuk deleted the validate-cert-on-smtp-starttls branch April 16, 2026 10:01
@github-actionsgithub-actionsBot added this to the Airflow 3.2.2 milestone Apr 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi maintainer, this PR was merged without a milestone set.
We've automatically set the milestone to Airflow 3.2.2 based on: backport label targeting v3-2-test
If this milestone is not correct, please update it to the appropriate milestone.

This comment was generated by Milestone Tag Assistant.

github-actionsBot pushed a commit that referenced this pull request Apr 16, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

github-actionsBot pushed a commit to aws-mwaa/upstream-to-airflow that referenced this pull request Apr 16, 2026
…che#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
karenbraganz pushed a commit to karenbraganz/airflow that referenced this pull request Apr 16, 2026
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 23, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
potiuk added a commit that referenced this pull request Apr 26, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
potiuk added a commit that referenced this pull request Apr 26, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 27, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
sehaj-sandhu added a commit to acceldata-io/airflow that referenced this pull request Jun 30, 2026
Backport of apache#65346 (06981d4),
upstream fixed in 3.2.2. With smtp_starttls=True / smtp_ssl=False, starttls()
was called with no SSL context, so the upgraded connection skipped certificate
validation (CWE-295) and was open to MITM credential capture. Resolve the SSL
context once and pass it to starttls() so STARTTLS validates the cert exactly
like implicit-SSL does.
- providers/smtp/hooks/smtp.py: extract _get_ssl_context(); use for starttls()
- utils/email.py: extract _get_email_ssl_context(); use for starttls()
Context "none" still disables validation by explicit opt-in only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@potiuk@eladkal
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Validate SMTP server certificate on STARTTLS upgrade - #65346

Merged
potiuk merged 2 commits into
apache:mainfrom
potiuk:validate-cert-on-smtp-starttls
Apr 16, 2026
Merged

Validate SMTP server certificate on STARTTLS upgrade#65346
potiuk merged 2 commits into
apache:mainfrom
potiuk:validate-cert-on-smtp-starttls

Conversation

@potiuk

Copy link
Copy Markdown
Member

Summary

smtplib.SMTP.starttls() does not validate the server certificate unless an SSL context is passed. airflow.utils.email.send_mime_email and the SMTP provider's SmtpHook (both sync get_conn and async aget_conn) were calling starttls() without a context, so the STARTTLS upgrade skipped certificate validation.

This PR passes the existing SSL-context machinery to starttls() at all three call sites:

  • airflow-core/src/airflow/utils/email.py — factored the SSL-context lookup out of _get_smtp_connection into a helper _get_ssl_context(), driven by the existing email.ssl_context config. The helper is now called by both the SMTP_SSL path and the new STARTTLS path.
  • providers/smtp/src/airflow/providers/smtp/hooks/smtp.py — factored the SSL-context lookup into _build_ssl_context(), driven by the existing ssl_context connection extra. The helper is called for SMTP_SSL (sync), for aiosmtplib.SMTP construction (async), and for the explicit starttls() calls in both get_conn and aget_conn.

Default behaviour change

The default SSL context is now ssl.create_default_context(), which validates against the system's trusted CAs. Environments that intentionally use self-signed or otherwise non-validating SMTP servers can opt out:

  • Core: set email.ssl_context = "none" in airflow.cfg
  • Provider: set ssl_context: "none" in the SMTP connection extras

The opt-out already existed for the SMTP_SSL path; it now applies to the STARTTLS path too.

Test plan

  • airflow-core/tests/unit/utils/test_email.py — 25/25 pass
  • providers/smtp/tests/unit/smtp/hooks/test_smtp.py — 34/34 pass (including sync test_ehlo_called_after_starttls and async test_async_connection updated to verify starttls is invoked with an ssl.SSLContext)
Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.6 (1M context)

Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment threadairflow-core/newsfragments/65346.significant.rst
@potiuk
potiuk merged commit 06981d4 into apache:mainApr 16, 2026
95 checks passed
@potiuk
potiuk deleted the validate-cert-on-smtp-starttls branch April 16, 2026 10:01
@github-actionsgithub-actionsBot added this to the Airflow 3.2.2 milestone Apr 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi maintainer, this PR was merged without a milestone set.
We've automatically set the milestone to Airflow 3.2.2 based on: backport label targeting v3-2-test
If this milestone is not correct, please update it to the appropriate milestone.

This comment was generated by Milestone Tag Assistant.

github-actionsBot pushed a commit that referenced this pull request Apr 16, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

github-actionsBot pushed a commit to aws-mwaa/upstream-to-airflow that referenced this pull request Apr 16, 2026
…che#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
karenbraganz pushed a commit to karenbraganz/airflow that referenced this pull request Apr 16, 2026
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 23, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
potiuk added a commit that referenced this pull request Apr 26, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
potiuk added a commit that referenced this pull request Apr 26, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 27, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
sehaj-sandhu added a commit to acceldata-io/airflow that referenced this pull request Jun 30, 2026
Backport of apache#65346 (06981d4),
upstream fixed in 3.2.2. With smtp_starttls=True / smtp_ssl=False, starttls()
was called with no SSL context, so the upgraded connection skipped certificate
validation (CWE-295) and was open to MITM credential capture. Resolve the SSL
context once and pass it to starttls() so STARTTLS validates the cert exactly
like implicit-SSL does.
- providers/smtp/hooks/smtp.py: extract _get_ssl_context(); use for starttls()
- utils/email.py: extract _get_email_ssl_context(); use for starttls()
Context "none" still disables validation by explicit opt-in only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@potiuk@eladkal
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Validate SMTP server certificate on STARTTLS upgrade - #65346

Merged
potiuk merged 2 commits into
apache:mainfrom
potiuk:validate-cert-on-smtp-starttls
Apr 16, 2026
Merged

Validate SMTP server certificate on STARTTLS upgrade#65346
potiuk merged 2 commits into
apache:mainfrom
potiuk:validate-cert-on-smtp-starttls

Conversation

@potiuk

Copy link
Copy Markdown
Member

Summary

smtplib.SMTP.starttls() does not validate the server certificate unless an SSL context is passed. airflow.utils.email.send_mime_email and the SMTP provider's SmtpHook (both sync get_conn and async aget_conn) were calling starttls() without a context, so the STARTTLS upgrade skipped certificate validation.

This PR passes the existing SSL-context machinery to starttls() at all three call sites:

  • airflow-core/src/airflow/utils/email.py — factored the SSL-context lookup out of _get_smtp_connection into a helper _get_ssl_context(), driven by the existing email.ssl_context config. The helper is now called by both the SMTP_SSL path and the new STARTTLS path.
  • providers/smtp/src/airflow/providers/smtp/hooks/smtp.py — factored the SSL-context lookup into _build_ssl_context(), driven by the existing ssl_context connection extra. The helper is called for SMTP_SSL (sync), for aiosmtplib.SMTP construction (async), and for the explicit starttls() calls in both get_conn and aget_conn.

Default behaviour change

The default SSL context is now ssl.create_default_context(), which validates against the system's trusted CAs. Environments that intentionally use self-signed or otherwise non-validating SMTP servers can opt out:

  • Core: set email.ssl_context = "none" in airflow.cfg
  • Provider: set ssl_context: "none" in the SMTP connection extras

The opt-out already existed for the SMTP_SSL path; it now applies to the STARTTLS path too.

Test plan

  • airflow-core/tests/unit/utils/test_email.py — 25/25 pass
  • providers/smtp/tests/unit/smtp/hooks/test_smtp.py — 34/34 pass (including sync test_ehlo_called_after_starttls and async test_async_connection updated to verify starttls is invoked with an ssl.SSLContext)
Was generative AI tooling used to co-author this PR?
  • Yes — Claude Opus 4.6 (1M context)

Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions

smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment threadairflow-core/newsfragments/65346.significant.rst
@potiuk
potiuk merged commit 06981d4 into apache:mainApr 16, 2026
95 checks passed
@potiuk
potiuk deleted the validate-cert-on-smtp-starttls branch April 16, 2026 10:01
@github-actionsgithub-actionsBot added this to the Airflow 3.2.2 milestone Apr 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi maintainer, this PR was merged without a milestone set.
We've automatically set the milestone to Airflow 3.2.2 based on: backport label targeting v3-2-test
If this milestone is not correct, please update it to the appropriate milestone.

This comment was generated by Milestone Tag Assistant.

github-actionsBot pushed a commit that referenced this pull request Apr 16, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Backport successfully created: v3-2-test

Note: As of Merging PRs targeted for Airflow 3.X
the committer who merges the PR is responsible for backporting the PRs that are bug fixes (generally speaking) to the maintenance branches.

In matter of doubt please ask in #release-management Slack channel.

StatusBranchResult
v3-2-testPR Link

github-actionsBot pushed a commit to aws-mwaa/upstream-to-airflow that referenced this pull request Apr 16, 2026
…che#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
karenbraganz pushed a commit to karenbraganz/airflow that referenced this pull request Apr 16, 2026
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 23, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
potiuk added a commit that referenced this pull request Apr 26, 2026
)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
potiuk added a commit that referenced this pull request Apr 26, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request Apr 27, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
vatsrahul1001 pushed a commit that referenced this pull request May 20, 2026
) (#65364)
* [v3-2-test] Bump actions/github-script in the github-actions-updates group (#65150) (#65160)
Bumps the github-actions-updates group with 1 update: [actions/github-script](https://github.com/actions/github-script).
Updates `actions/github-script` from 8.0.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](actions/github-script@ed59741...3a2844b)
(cherry picked from commit e5a047c)
---
updated-dependencies:
- dependency-name: actions/github-script
dependency-version: 9.0.0
dependency-type: direct:production
update-type: version-update:semver-major
dependency-group: github-actions-updates
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* [v3-2-test] Added breeze generate issue content for airflow-ctl (#65042) (#65241)
* Add breeze generate issue content for airflow-ctl
* add new command to doc
(cherry picked from commit b24538b)
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
* [v3-2-test] Run release calendar verification on its own schedule (#65118) (#65242)
* Move release calendar verification to its own scheduled workflow
Run dev/verify_release_calendar.py from a dedicated daily scheduled
workflow instead of as a canary job in the main CI pipeline, and
notify the #release-management Slack channel when the check fails so
the issue is surfaced to release managers directly.
* Include wiki and calendar links in release calendar Slack alert
(cherry picked from commit 048e9a1)
* [v3-2-test] Validate SMTP server certificate on STARTTLS upgrade (#65346)
* Validate SMTP server certificate on STARTTLS upgrade
smtplib.SMTP.starttls() does not validate the server certificate
unless an SSL context is passed. airflow.utils.email.send_mime_email
and the SMTP provider's SmtpHook (both sync get_conn and async
aget_conn) were calling starttls() without a context, so the STARTTLS
upgrade accepted any certificate and the subsequent login() call
could send credentials over a connection terminated by a MITM.
Pass the existing SSL-context machinery (the email.ssl_context
config in core and the ssl_context connection extra in the provider)
to starttls() at all three call sites. The default becomes
ssl.create_default_context(), which validates against the system's
trusted CAs. Users who intentionally use self-signed certificates
can still opt out by setting the value to "none".
Generated-by: Claude Opus 4.6 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
* Add newsfragment and SMTP provider changelog for STARTTLS cert default
Document the default behaviour change introduced by passing an SSL
context to the STARTTLS upgrade: system-default CA validation now
applies to both airflow.utils.email.send_email (via
email.ssl_context) and the SMTP provider's SmtpHook (via the
ssl_context connection extra). Users who intentionally run against
self-signed SMTP servers can preserve the old behaviour by setting
the value to "none".
(cherry picked from commit 06981d4)
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Generated-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jarek Potiuk <jarek@potiuk.com>
Co-authored-by: Justin Pakzad <114518232+justinpakzad@users.noreply.github.com>
sehaj-sandhu added a commit to acceldata-io/airflow that referenced this pull request Jun 30, 2026
Backport of apache#65346 (06981d4),
upstream fixed in 3.2.2. With smtp_starttls=True / smtp_ssl=False, starttls()
was called with no SSL context, so the upgraded connection skipped certificate
validation (CWE-295) and was open to MITM credential capture. Resolve the SSL
context once and pass it to starttls() so STARTTLS validates the cert exactly
like implicit-SSL does.
- providers/smtp/hooks/smtp.py: extract _get_ssl_context(); use for starttls()
- utils/email.py: extract _get_email_ssl_context(); use for starttls()
Context "none" still disables validation by explicit opt-in only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@potiuk@eladkal