Skip to content

refactor(auth): replace pyOpenSSL with standard ssl and cryptography#16976

Open
nbayati wants to merge 11 commits into
googleapis:mainfrom
nbayati:remove-pyopenssl-dependency
Open

refactor(auth): replace pyOpenSSL with standard ssl and cryptography#16976
nbayati wants to merge 11 commits into
googleapis:mainfrom
nbayati:remove-pyopenssl-dependency

Conversation

@nbayati

@nbayati nbayati commented May 7, 2026

Copy link
Copy Markdown
Contributor

Replace pyOpenSSL with standard library ssl for mTLS transport and update key decryption to use cryptography library.

This change also enhances security for handling private keys by:

  • Using Linux memfd_create for RAM-backed in-memory files to avoid writing secrets to physical storage.
  • Encrypting plaintext keys on-the-fly before writing to fallback temporary files on disk.
  • Securely wiping temporary files with null bytes before deletion.

Fixes #16920
Closes #16921
fixes #17412

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the library to remove the pyOpenSSL dependency, migrating certificate and key management to the cryptography library and Python's standard ssl module. It introduces a secure three-tier fallback strategy for handling mTLS credentials via the new secure_cert_key_paths utility, which utilizes RAM-backed virtual files on Linux to minimize disk exposure. Review feedback suggests simplifying the manual context manager orchestration within this utility and using os.fdopen instead of os.write for more robust and idiomatic file descriptor operations.

Comment thread packages/google-auth/google/auth/transport/_mtls_helper.py
Comment thread packages/google-auth/google/auth/transport/_mtls_helper.py Outdated
Comment thread packages/google-auth/google/auth/transport/_mtls_helper.py Outdated
@parthea

parthea commented May 26, 2026

Copy link
Copy Markdown
Contributor

@nbayati , Please could you address the failing presubmits?

@nbayati nbayati marked this pull request as ready for review June 10, 2026 05:59
@nbayati nbayati requested review from a team as code owners June 10, 2026 05:59
@nbayati nbayati added kokoro:run Add this label to force Kokoro to re-run the tests. kokoro:force-run Add this label to force Kokoro to re-run the tests. and removed kokoro:run Add this label to force Kokoro to re-run the tests. labels Jun 10, 2026
nbayati and others added 4 commits June 10, 2026 06:07
Replace pyOpenSSL with standard library ssl for mTLS transport and update key decryption to use cryptography library.

This change also enhances security for handling private keys by:
- Using Linux memfd_create for RAM-backed in-memory files to avoid writing secrets to physical storage.
- Encrypting plaintext keys on-the-fly before writing to fallback temporary files on disk.
- Securely wiping temporary files with null bytes before deletion.
@nbayati nbayati force-pushed the remove-pyopenssl-dependency branch from f7e5146 to 1704dd8 Compare June 10, 2026 06:11
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jun 10, 2026
@nbayati

nbayati commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request removes the dependency on pyOpenSSL across the library, replacing it with Python's standard ssl library and the cryptography package. It also introduces a secure three-tier fallback strategy for handling client certificates and private keys. The review feedback highlights a potential NameError in urllib3.py due to an unimported module reference, suggests simplifying context manager exit logic in _mtls_helper.py, recommends verifying write permissions for /dev/shm to prevent failures in restricted environments, and advises defensively encoding leaf certificate callback values to bytes.

Comment thread packages/google-auth/google/auth/transport/urllib3.py
Comment thread packages/google-auth/google/auth/transport/_mtls_helper.py Outdated
Comment thread packages/google-auth/google/auth/transport/_mtls_helper.py Outdated
Comment thread packages/google-auth/google/auth/identity_pool.py Outdated
Comment thread packages/google-auth/google/auth/transport/_mtls_helper.py Outdated
return

cert_bytes = cert if isinstance(cert, bytes) else None
key_bytes = key if isinstance(key, bytes) else None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if a user passed one as a string path, and one as bytes? It seems like that isn't supported? Should we mention that in the docstrings?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be good to leave an in-line comment about how Nones are treated here too. After looking at some of the helpers, it seems like it will result in an empty output?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mixed inputs (e.g. one string path and one bytes) are currently supported. If one parameter is a string path or None, its bytes representation will be None, so only the bytes parameter is written to a temp file, and the string/None parameter is yielded back as-is. I've updated the docstrings to clarify this behavior, and added inline comments on how None values are handled.

Comment thread packages/google-auth/google/auth/transport/_mtls_helper.py Outdated
cleanup_fds.append(fd_key)
with os.fdopen(fd_key, "wb", closefd=False) as f:
f.write(key_bytes)
key_path = f"/proc/self/fd/{fd_key}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I wonder if making a helper or loop for these could be cleaner?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! Added a loop.

Yields:
Tuple[Optional[str], Optional[str]]: In-memory file paths pointing to
the active descriptors (e.g., '/proc/self/fd/3').
"""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like secure_cert_key_paths expects this one to raise OSErrors? If so, we should probably mention them in a Raises: section in the docstring

(although I had a separate suggestion about those OSErrors too)

Comment thread packages/google-auth/google/auth/transport/_mtls_helper.py
cert: Union[str, bytes],
key: Union[str, bytes],
passphrase: Optional[bytes] = None,
) -> Generator[Tuple[str, str, Optional[bytes]], None, None]:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be useful to mention in the docstring that this was implemented as a context manager/generator to clean up after itself between yield and exit.

A lot of the clean-up code is hidden in delegated calls, so it may not be obvious to future readers

with os.fdopen(fd, "wb") as f:
f.write(encrypted_key_bytes)
f.flush()
os.fsync(f.fileno())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be worth replacing with a loop or helper

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

4 participants