Skip to content

Commit 9ab5f83

Browse files
authored
Merge pull request #19691 from guswynn/hidden-host-test
storage: test that we don't resolve remote hosts when using ssh tunnels
2 parents 5db9c2d + e2d8b69 commit 9ab5f83

6 files changed

Lines changed: 114 additions & 10 deletions

File tree

Cargo.lock

Lines changed: 13 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,7 @@ proptest-derive = { git = "https://github.com/MaterializeInc/proptest.git" }
172172
# Waiting on https://github.com/edenhill/librdkafka/pull/4051.
173173
rdkafka = { git = "https://github.com/MaterializeInc/rust-rdkafka.git" }
174174
rdkafka-sys = { git = "https://github.com/MaterializeInc/rust-rdkafka.git" }
175+
176+
# Waiting on https://github.com/openssh-rust/openssh/pull/120 to make it into
177+
# a release.
178+
openssh = { git = "https://github.com/MaterializeInc/openssh.git" }

src/mz/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ rust-version.workspace = true
1010
anyhow = "1.0.66"
1111
axum = { version = "0.6.7" }
1212
clap = { version = "3.2.24", features = [ "derive" ] }
13-
dirs = "4.0.0"
13+
dirs = "5.0.0"
1414
indicatif = "0.17.2"
1515
mz-build-info = { path = "../build-info" }
1616
mz-ore = { path = "../ore", features = ["async", "cli"] }

src/ssh-util/src/tunnel.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,8 @@ async fn connect(
199199
// Force use of IPv4 loopback. Do not use the hostname `localhost`,
200200
// as that can resolve to IPv6, and the SSH tunnel is only listening
201201
// for IPv4 connections.
202-
let local = openssh::Socket::new(&(Ipv4Addr::LOCALHOST, local_port))?;
203-
let remote = openssh::Socket::new(&(host, port))?;
202+
let local = openssh::Socket::from((Ipv4Addr::LOCALHOST, local_port));
203+
let remote = openssh::Socket::new(host, port);
204204

205205
match session
206206
.request_port_forward(ForwardType::Local, local, remote)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Copyright Materialize, Inc. and contributors. All rights reserved.
2+
#
3+
# Use of this software is governed by the Business Source License
4+
# included in the LICENSE file at the root of this repository.
5+
#
6+
# As of the Change Date specified in that file, in accordance with
7+
# the Business Source License, use of this software will be governed
8+
# by the Apache License, Version 2.0.
9+
10+
# Test creating a source that uses Kafka/CSR via an SSH tunnel where the Kafka
11+
# broker and CSR server have hostnames that can only be resolved from the
12+
# SSH bastion host.
13+
#
14+
# The `openssh` crate previously had a bug where it would attempt to resolve
15+
# the target host on the client.
16+
# See: https://github.com/openssh-rust/openssh/pull/120
17+
18+
$ kafka-create-topic topic=thetopic
19+
20+
$ kafka-ingest topic=thetopic format=bytes
21+
one
22+
23+
> CREATE CONNECTION kafka_conn
24+
TO KAFKA (BROKER 'hidden-kafka:9092' USING SSH TUNNEL thancred);
25+
26+
27+
> CREATE CONNECTION csr_conn TO CONFLUENT SCHEMA REGISTRY (
28+
URL 'http://hidden-schema-registry:8081',
29+
SSH TUNNEL thancred
30+
);
31+
32+
$ set schema={
33+
"type" : "record",
34+
"name" : "test",
35+
"fields" : [
36+
{"name":"f1", "type":"string"},
37+
{"name":"f2", "type":"long"}
38+
]
39+
}
40+
41+
$ kafka-create-topic topic=avroavro
42+
43+
$ kafka-ingest format=avro topic=avroavro schema=${schema}
44+
{"f1": "fish", "f2": 1000}
45+
46+
> CREATE SOURCE csr_source
47+
FROM KAFKA CONNECTION kafka_conn (TOPIC 'testdrive-avroavro-${testdrive.seed}')
48+
FORMAT AVRO USING CONFLUENT SCHEMA REGISTRY CONNECTION csr_conn
49+
ENVELOPE NONE
50+
51+
> SELECT * FROM csr_source
52+
f1 f2
53+
----------
54+
fish 1000

test/ssh-connection/mzcompose.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,45 @@ def workflow_kafka_csr_via_ssh_tunnel(c: Composition, redpanda: bool = False) ->
115115
c.run("testdrive", "--no-reset", "kafka-source-after-ssh-restart.td")
116116

117117

118+
def workflow_hidden_hosts(c: Composition, redpanda: bool = False) -> None:
119+
c.down()
120+
dependencies = ["materialized", "ssh-bastion-host"]
121+
if redpanda:
122+
dependencies += ["redpanda"]
123+
else:
124+
dependencies += ["zookeeper", "kafka", "schema-registry"]
125+
c.up(*dependencies)
126+
127+
c.run("testdrive", "setup.td")
128+
129+
public_key = c.sql_query("select public_key_1 from mz_ssh_tunnel_connections;")[0][
130+
0
131+
]
132+
133+
c.exec(
134+
"ssh-bastion-host",
135+
"bash",
136+
"-c",
137+
f"echo '{public_key}' > /etc/authorized_keys/mz",
138+
)
139+
140+
def add_hidden_host(container: str) -> None:
141+
ip = c.exec(
142+
"ssh-bastion-host", "getent", "hosts", container, capture=True
143+
).stdout.split(" ")[0]
144+
c.exec(
145+
"ssh-bastion-host",
146+
"bash",
147+
"-c",
148+
f"echo '{ip} hidden-{container}' >> /etc/hosts",
149+
)
150+
151+
add_hidden_host("kafka")
152+
add_hidden_host("schema-registry")
153+
154+
c.run("testdrive", "--no-reset", "hidden-hosts.td")
155+
156+
118157
# Test that if we restart the bastion AND change its server keys(s), we can
119158
# still reconnect in the replication stream.
120159
def workflow_pg_restart_bastion(c: Composition) -> None:
@@ -268,3 +307,4 @@ def workflow_default(c: Composition) -> None:
268307
workflow_pg_via_ssh_tunnel(c)
269308
workflow_pg_via_ssh_tunnel_with_ssl(c)
270309
workflow_pg_restart_bastion(c)
310+
workflow_hidden_hosts(c)

0 commit comments

Comments
 (0)