Skip to content

Migrate to canonical PostgreSQL client parameter sslmode#202

Open
amotl wants to merge 8 commits intomainfrom
sslmode
Open

Migrate to canonical PostgreSQL client parameter sslmode#202
amotl wants to merge 8 commits intomainfrom
sslmode

Conversation

@amotl
Copy link
Member

@amotl amotl commented Feb 16, 2025

Introduction

We need the capability to connect to CrateDB using SSL without verifying the host name.
We also would like to standardize connection parameters to be more like PostgreSQL.

About

This implements sslmode=require to connect to SSL-enabled CrateDB instances without verifying the host name.

Preview

uv pip install 'sqlalchemy-cratedb @ https://github.com/crate/sqlalchemy-cratedb/archive/refs/heads/sslmode.zip'
export SQLALCHEMY_CRATEDB_URL=crate://localhost:4200/?sslmode=require

NB: The environment where you are installing this into needs to have Git installed first, see jwodder/versioningit#106.

References

This miniature rig can be used to validate it manually.

NB: Please also make yourself heard on this patch and/or acknowledge even when not assigned as reviewers. I am just not doing it because it is not appropriate to slot in more than two of us. /cc @surister, @kneth

@amotl amotl changed the base branch from main to versioningit-git-archive February 16, 2025 19:48
@amotl amotl requested review from matriv and seut February 16, 2025 20:25
@amotl amotl marked this pull request as ready for review February 16, 2025 20:25
Base automatically changed from versioningit-git-archive to main February 17, 2025 08:09
if use_ssl:
# TODO: Switch to the canonical default `sslmode=prefer` later.
sslmode = kwargs.pop("sslmode", "disable")
if use_ssl or sslmode in ["allow", "prefer", "require", "verify-ca", "verify-full"]:
Copy link
Member

Choose a reason for hiding this comment

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

If ssl is explicit disabled, sslmode shouldn't have any effect. At least for bwc imho.

Suggested change
if use_ssl or sslmode in ["allow", "prefer", "require", "verify-ca", "verify-full"]:
if use_ssl and sslmode in ["allow", "prefer", "require", "verify-ca", "verify-full"]:

Copy link
Member Author

@amotl amotl Feb 17, 2025

Choose a reason for hiding this comment

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

The patch is intended to set the stage to transition to PostgreSQL conventions, so sslmode is intended to be handled in parallel / as an alternative to the ssl option parameter, so we can phase it out in the future.

Copy link
Contributor

Choose a reason for hiding this comment

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

If that is the intention, then we should deprecate ssl in the docs/changes, and clearly state that sslmode has higher precedence and overrides ssl.

Copy link
Member Author

@amotl amotl Feb 17, 2025

Choose a reason for hiding this comment

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

Yes, documentation needs to be added. I will reflect that correspondingly, but did not intend to deprecate the ssl option just yet, maybe just give an outlook about it.

If you think it is the right choice to officially deprecate it, but still keep it just for bwc reasons, sure I can do it timely. I am trying to find out about your opinion on this. Thanks!

Copy link
Member

Choose a reason for hiding this comment

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

At least this should be documented as it may not behave as expected.

Copy link
Contributor

Choose a reason for hiding this comment

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

imho, it wouldn't make sense to keep 2 different mechanisms in the future, or do you see some value in that?

Copy link
Member Author

@amotl amotl Feb 17, 2025

Choose a reason for hiding this comment

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

I agree. Let's just keep it for bwc purposes until we may remove the previous one, possibly paired with a 1.0.0 release?

Copy link
Member Author

@amotl amotl Feb 18, 2026

Choose a reason for hiding this comment

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

Hi again. @94492ef improves the change log message, adf9eaa the documentation, b1eb6bf starts emitting a deprecation notice, and ab1b600 starts validating that ensemble.

if use_ssl or sslmode in ["allow", "prefer", "require", "verify-ca", "verify-full"]:
servers = ["https://" + server for server in servers]
if sslmode == "require":
kwargs["verify_ssl_cert"] = False
Copy link
Member

Choose a reason for hiding this comment

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

This looks odd as it disables cert verification even that the user did not intend to do that.
If one need to do that, one can set the arg explicitly, or what do I miss?

Copy link
Member Author

@amotl amotl Feb 17, 2025

Choose a reason for hiding this comment

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

Currently, verify_ssl_cert can not be defined by the user on behalf of the SQLAlchemy connection string. It's the intention of the patch to unlock that, and make connectivity options adhere to PostgreSQL conventions.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, but this will disable cert verification for everyone who uses require. This sounds not like it is what we really want.

Copy link
Member Author

@amotl amotl Feb 17, 2025

Choose a reason for hiding this comment

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

We think sslmode=require is the right option to strictly use an SSL connection, but turn off host name validation on it.

The PostgreSQL docs possibly uses uncommon jargon there, that's why it is sometimes difficult to understand, but of course we may also be wrong on this detail. 1

Please validate and clarify.

Footnotes

  1. We will try to use a better jargon on our docs, but still adhere to the option values PostgreSQL clients are using here.

Copy link
Member

Choose a reason for hiding this comment

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

Ah ok, I understood it wrongly, thanks for the clarification.

Copy link
Member

Choose a reason for hiding this comment

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

Why would we want to disable hostname validation with sslmode=require ?

Copy link
Member Author

@amotl amotl Feb 17, 2025

Choose a reason for hiding this comment

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

Hi. It's for the same reasons PostgreSQL and other network clients talking SSL offer the same option in one way or another, to enable connectivity (e.g. for debugging purposes) even when the hostname does not match the certificate name.

The specific need is originating from cloud operations when connecting to CrateDB/SSL from internal spots within a K8s cluster using port-forwarding mechanisms, which is business as usual for @WalBeh and @goat-ssh.

That this mode, to disable hostname validation, is called sslmode=require in PostgreSQL jargon is unfortunate, but not my fault. The stronger variants are called verify-ca and verify-full.

Copy link
Member Author

@amotl amotl Feb 19, 2026

Choose a reason for hiding this comment

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

Hi again. I've changed the implementation with d93e3f2: It is now using the SSLMode class from asyncpg to wrap away the topic domain of sslmode-parameter parsing, so what's left is much cleaner and easier to follow. As an additional benefit, parsing the parameter becomes more robust, because the code now also fails on invalid values like other PostgreSQL clients are doing it.

# Process new SSL option `sslmode`.
# Please consult https://www.postgresql.org/docs/18/libpq-connect.html.
if "sslmode" in kwargs:
try:
sslmode = SSLMode.parse(kwargs.pop("sslmode"))
except AttributeError as exc:
modes = ", ".join(SSLMode.modes)
raise SQLAlchemyError(
"`sslmode` parameter must be one of: {}".format(modes)
) from exc
if sslmode < SSLMode.allow:
use_ssl = False
else:
use_ssl = True
if sslmode >= SSLMode.verify_ca:
kwargs["verify_ssl_cert"] = True
else:
kwargs["verify_ssl_cert"] = False

@kneth kneth linked an issue Mar 27, 2025 that may be closed by this pull request
Comment on lines 237 to 238
return self.dbapi.connect(servers=servers, **kwargs)
return self.dbapi.connect(**kwargs)
Copy link
Member Author

Choose a reason for hiding this comment

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

When evaluating the patch downstream using sqlalchemy-cratedb==0.42.0.dev2, we found that this connection string syntax, omitting the host name, is not supported yet: crate://?sslmode=require.

Currently, it bails out like this:

TypeError: Connection.__init__() got an unexpected keyword argument 'sslmode'

The reason is, as you can observe above, that two different code paths are used here.

Copy link
Member Author

@amotl amotl Feb 18, 2026

Choose a reason for hiding this comment

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

This spot has been improved with 9c54f7c, so that crate://?sslmode=require also works as intended.

This implements `sslmode=prefer` to connect to SSL-enabled CrateDB
instances without verifying the host name.
@coderabbitai
Copy link

coderabbitai bot commented Feb 18, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch sslmode

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@amotl amotl requested review from mfussenegger and seut February 18, 2026 20:25
@amotl amotl changed the title Add canonical PostgreSQL client parameter sslmode Migrate to canonical PostgreSQL client parameter sslmode Feb 18, 2026
@amotl amotl force-pushed the sslmode branch 2 times, most recently from c8d5fd8 to d15d115 Compare February 18, 2026 23:18
Comment on lines 241 to 245
sslmode = kwargs.pop("sslmode", "disable")
if sslmode in ["allow", "prefer", "require", "verify-ca", "verify-full"]:
use_ssl = True
if sslmode in ["allow", "prefer", "require"]:
kwargs["verify_ssl_cert"] = False
Copy link
Member Author

@amotl amotl Feb 19, 2026

Choose a reason for hiding this comment

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

This evaluation of the new sslmode parameter does not check for invalid values yet, like other PostgreSQL clients are doing it. For improving compatibility and interoperability of this patch, the evaluation should do the same.

$ psql "postgresql://postgres@localhost:5432?sslmode=foo"
psql: error: invalid sslmode value: "foo"
>>> import psycopg
>>> psycopg.connect("postgresql://postgres@localhost:5432?sslmode=foo")
Traceback (most recent call last):
  File "<python-input-1>", line 1, in <module>
    psycopg.connect("postgresql://postgres@localhost:5432?sslmode=foo")
    ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/path/to/lib/python3.13/site-packages/psycopg/connection.py", line 130, in connect
    raise new_ex.with_traceback(None)
psycopg.OperationalError: connection is bad: invalid sslmode value: "foo"
Multiple connection attempts failed. All failures were:
- host: 'localhost', port: '5432', hostaddr: '::1': connection is bad: invalid sslmode value: "foo"
- host: 'localhost', port: '5432', hostaddr: '127.0.0.1': connection is bad: invalid sslmode value: "foo"
>>> import asyncpg
>>> import asyncio
>>> asyncio.run(asyncpg.connect("postgresql://postgres@localhost:5432?sslmode=foo"))
  File "/path/to/lib/python3.13/site-packages/asyncpg/connect_utils.py", line 685, in _parse_connect_dsn_and_args
    raise exceptions.ClientConfigurationError(
        '`sslmode` parameter must be one of: {}'.format(modes)
    ) from None
asyncpg.exceptions._base.ClientConfigurationError: `sslmode` parameter must be one of: disable, allow, prefer, require, verify-ca, verify-full

Copy link
Member Author

@amotl amotl Feb 19, 2026

Choose a reason for hiding this comment

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

d93e3f2 improves the situation by inheriting fragments of parameter parsing and error handling from asyncpg.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Respect sslmode option

4 participants

Comments