From 80466bdca24ff9de4e90299b0b9635fee8a9ab01 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Mon, 28 Oct 2024 18:26:48 +0000 Subject: [PATCH] remove postgres auth backend from proxy tests --- test_runner/fixtures/neon_fixtures.py | 57 ++++++------ test_runner/regress/test_proxy_allowed_ips.py | 90 +++++++++++++++---- 2 files changed, 103 insertions(+), 44 deletions(-) diff --git a/test_runner/fixtures/neon_fixtures.py b/test_runner/fixtures/neon_fixtures.py index 6491069f20..2c9d41568b 100644 --- a/test_runner/fixtures/neon_fixtures.py +++ b/test_runner/fixtures/neon_fixtures.py @@ -3098,10 +3098,6 @@ class NeonProxy(PgProtocol): class AuthBackend(abc.ABC): """All auth backends must inherit from this class""" - @property - def default_conn_url(self) -> Optional[str]: - return None - @abc.abstractmethod def extra_args(self) -> list[str]: pass @@ -3115,7 +3111,7 @@ class NeonProxy(PgProtocol): *["--allow-self-signed-compute", "true"], ] - class Console(AuthBackend): + class ControlPlane(AuthBackend): def __init__(self, endpoint: str, fixed_rate_limit: Optional[int] = None): self.endpoint = endpoint self.fixed_rate_limit = fixed_rate_limit @@ -3139,21 +3135,6 @@ class NeonProxy(PgProtocol): ] return args - @dataclass(frozen=True) - class Postgres(AuthBackend): - pg_conn_url: str - - @property - def default_conn_url(self) -> Optional[str]: - return self.pg_conn_url - - def extra_args(self) -> list[str]: - return [ - # Postgres auth backend params - *["--auth-backend", "postgres"], - *["--auth-endpoint", self.pg_conn_url], - ] - def __init__( self, neon_binpath: Path, @@ -3168,7 +3149,7 @@ class NeonProxy(PgProtocol): ): host = "127.0.0.1" domain = "proxy.localtest.me" # resolves to 127.0.0.1 - super().__init__(dsn=auth_backend.default_conn_url, host=domain, port=proxy_port) + super().__init__(host=domain, port=proxy_port) self.domain = domain self.host = host @@ -3422,20 +3403,38 @@ def static_proxy( port_distributor: PortDistributor, neon_binpath: Path, test_output_dir: Path, + httpserver: HTTPServer, ) -> Iterator[NeonProxy]: - """Neon proxy that routes directly to vanilla postgres.""" + """Neon proxy that routes directly to vanilla postgres and a mocked cplane HTTP API.""" port = vanilla_pg.default_options["port"] host = vanilla_pg.default_options["host"] - dbname = vanilla_pg.default_options["dbname"] - auth_endpoint = f"postgres://proxy:password@{host}:{port}/{dbname}" - # For simplicity, we use the same user for both `--auth-endpoint` and `safe_psql` vanilla_pg.start() vanilla_pg.safe_psql("create user proxy with login superuser password 'password'") - vanilla_pg.safe_psql("CREATE SCHEMA IF NOT EXISTS neon_control_plane") - vanilla_pg.safe_psql( - "CREATE TABLE neon_control_plane.endpoints (endpoint_id VARCHAR(255) PRIMARY KEY, allowed_ips VARCHAR(255))" + [(rolpassword,)] = vanilla_pg.safe_psql( + "select rolpassword from pg_catalog.pg_authid where rolname = 'proxy'" + ) + + # return local postgres addr on ProxyWakeCompute. + httpserver.expect_request("/cplane/proxy_wake_compute").respond_with_json( + { + "address": f"{host}:{port}", + "aux": { + "endpoint_id": "ep-foo-bar-1234", + "branch_id": "br-foo-bar", + "project_id": "foo-bar", + }, + } + ) + + # return local postgres addr on ProxyWakeCompute. + httpserver.expect_request("/cplane/proxy_get_role_secret").respond_with_json( + { + "role_secret": rolpassword, + "allowed_ips": None, + "project_id": "foo-bar", + } ) proxy_port = port_distributor.get_port() @@ -3450,7 +3449,7 @@ def static_proxy( http_port=http_port, mgmt_port=mgmt_port, external_http_port=external_http_port, - auth_backend=NeonProxy.Postgres(auth_endpoint), + auth_backend=NeonProxy.ControlPlane(httpserver.url_for("/cplane")), ) as proxy: proxy.start() yield proxy diff --git a/test_runner/regress/test_proxy_allowed_ips.py b/test_runner/regress/test_proxy_allowed_ips.py index 902da1942e..f0dfcc2b24 100644 --- a/test_runner/regress/test_proxy_allowed_ips.py +++ b/test_runner/regress/test_proxy_allowed_ips.py @@ -6,22 +6,51 @@ from fixtures.neon_fixtures import ( NeonProxy, VanillaPostgres, ) +from pytest_httpserver import HTTPServer TABLE_NAME = "neon_control_plane.endpoints" # Proxy uses the same logic for psql and websockets. @pytest.mark.asyncio -async def test_proxy_psql_allowed_ips(static_proxy: NeonProxy, vanilla_pg: VanillaPostgres): +async def test_proxy_psql_allowed_ips( + static_proxy: NeonProxy, + vanilla_pg: VanillaPostgres, + httpserver: HTTPServer, +): + [(rolpassword,)] = vanilla_pg.safe_psql( + "select rolpassword from pg_catalog.pg_authid where rolname = 'proxy'" + ) + # Shouldn't be able to connect to this project - vanilla_pg.safe_psql( - f"INSERT INTO {TABLE_NAME} (endpoint_id, allowed_ips) VALUES ('private-project', '8.8.8.8')" + httpserver.expect_request( + "/cplane/proxy_get_role_secret", query_string="project=private-project" + ).respond_with_json( + { + "role_secret": rolpassword, + "allowed_ips": ["8.8.8.8"], + "project_id": "foo-bar", + } ) + # Should be able to connect to this project - vanilla_pg.safe_psql( - f"INSERT INTO {TABLE_NAME} (endpoint_id, allowed_ips) VALUES ('generic-project', '::1,127.0.0.1')" + httpserver.expect_request( + "/cplane/proxy_get_role_secret", query_string="project=generic-project" + ).respond_with_json( + { + "role_secret": rolpassword, + "allowed_ips": ["::1", "127.0.0.1"], + "project_id": "foo-bar", + } ) + # vanilla_pg.safe_psql( + # f"INSERT INTO {TABLE_NAME} (endpoint_id, allowed_ips) VALUES ('private-project', '8.8.8.8')" + # ) + # vanilla_pg.safe_psql( + # f"INSERT INTO {TABLE_NAME} (endpoint_id, allowed_ips) VALUES ('generic-project', '::1,127.0.0.1')" + # ) + def check_cannot_connect(**kwargs): with pytest.raises(psycopg2.Error) as exprinfo: static_proxy.safe_psql(**kwargs) @@ -51,12 +80,20 @@ async def test_proxy_psql_allowed_ips(static_proxy: NeonProxy, vanilla_pg: Vanil @pytest.mark.asyncio -async def test_proxy_http_allowed_ips(static_proxy: NeonProxy, vanilla_pg: VanillaPostgres): - static_proxy.safe_psql("create user http_auth with password 'http' superuser") +async def test_proxy_http_allowed_ips( + static_proxy: NeonProxy, + vanilla_pg: VanillaPostgres, + httpserver: HTTPServer, +): + vanilla_pg.safe_psql("create user http_auth with password 'http' superuser") - # Shouldn't be able to connect to this project - vanilla_pg.safe_psql( - f"INSERT INTO {TABLE_NAME} (endpoint_id, allowed_ips) VALUES ('proxy', '8.8.8.8')" + # # Shouldn't be able to connect to this project + # vanilla_pg.safe_psql( + # f"INSERT INTO {TABLE_NAME} (endpoint_id, allowed_ips) VALUES ('proxy', '8.8.8.8')" + # ) + + [(rolpassword,)] = vanilla_pg.safe_psql( + "select rolpassword from pg_catalog.pg_authid where rolname = 'proxy'" ) def query(status: int, query: str, *args): @@ -68,9 +105,32 @@ async def test_proxy_http_allowed_ips(static_proxy: NeonProxy, vanilla_pg: Vanil expected_code=status, ) - query(400, "select 1;") # ip address is not allowed - # Should be able to connect to this project - vanilla_pg.safe_psql( - f"UPDATE {TABLE_NAME} SET allowed_ips = '8.8.8.8,127.0.0.1' WHERE endpoint_id = 'proxy'" + # Shouldn't be able to connect to this project + httpserver.expect_oneshot_request( + "/cplane/proxy_get_role_secret", query_string="project=proxy" + ).respond_with_json( + { + "role_secret": rolpassword, + "allowed_ips": ["8.8.8.8"], + "project_id": "foo-bar", + } ) - query(200, "select 1;") # should work now + + with httpserver.wait() as waiting: + query(400, "select 1;") # ip address is not allowed + assert waiting.result + + # Should be able to connect to this project + httpserver.expect_oneshot_request( + "/cplane/proxy_get_role_secret", query_string="project=proxy" + ).respond_with_json( + { + "role_secret": rolpassword, + "allowed_ips": ["8.8.8.8", "127.0.0.1"], + "project_id": "foo-bar", + } + ) + + with httpserver.wait() as waiting: + query(400, "select 1;") # ip address is not allowed + assert waiting.result