Skip to content

Commit 18a21fe

Browse files
committed
bundle: use userland resolver
- Fix SNI handling - Add keylog fd to seccomp policy - Add userland resolver to seccomp policy
1 parent af04005 commit 18a21fe

File tree

5 files changed

+273
-107
lines changed

5 files changed

+273
-107
lines changed

src/disco/bundle/fd_bundle_client.c

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#include <unistd.h> /* close */
1818
#include <poll.h> /* poll */
1919
#include <sys/socket.h> /* socket */
20-
#include <netinet/in.h> /* IPPROTO_TCP */
20+
#include <netinet/in.h>
2121

2222
static void
2323
fd_bundle_client_reset( fd_bundle_tile_t * ctx ) {
@@ -58,10 +58,11 @@ fd_bundle_client_reset( fd_bundle_tile_t * ctx ) {
5858
}
5959

6060
static int
61-
fd_bundle_client_do_connect( fd_bundle_tile_t const * ctx ) {
61+
fd_bundle_client_do_connect( fd_bundle_tile_t const * ctx,
62+
uint ip4_addr ) {
6263
struct sockaddr_in addr = {
6364
.sin_family = AF_INET,
64-
.sin_addr.s_addr = ctx->server_ip4_addr,
65+
.sin_addr.s_addr = ip4_addr,
6566
.sin_port = fd_ushort_bswap( ctx->server_tcp_port )
6667
};
6768
errno = 0;
@@ -76,9 +77,24 @@ static void
7677
fd_bundle_client_create_conn( fd_bundle_tile_t * ctx ) {
7778
fd_bundle_client_reset( ctx );
7879

79-
int tcp_sock = socket( AF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_TCP );
80+
/* FIXME IPv6 support */
81+
fd_addrinfo_t hints = {0};
82+
hints.ai_family = AF_INET;
83+
fd_addrinfo_t * res = NULL;
84+
uchar scratch[ 4096 ];
85+
void * pscratch = scratch;
86+
int err = fd_getaddrinfo( ctx->server_fqdn, &hints, &res, &pscratch, sizeof(scratch) );
87+
if( FD_UNLIKELY( err ) ) {
88+
FD_LOG_WARNING(( "fd_getaddrinfo `%s` failed (%d-%s)", ctx->server_fqdn, err, fd_gai_strerror( err ) ));
89+
fd_bundle_client_reset( ctx );
90+
ctx->metrics.transport_fail_cnt++;
91+
return;
92+
}
93+
uint const ip4_addr = ((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr;
94+
95+
int tcp_sock = socket( AF_INET, SOCK_STREAM|SOCK_CLOEXEC, 0 );
8096
if( FD_UNLIKELY( tcp_sock<0 ) ) {
81-
FD_LOG_ERR(( "socket(AF_INET,SOCK_STREAM|SOCK_CLOEXEC,IPPROTO_TCP) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
97+
FD_LOG_ERR(( "socket(AF_INET,SOCK_STREAM|SOCK_CLOEXEC,0) failed (%i-%s)", errno, fd_io_strerror( errno ) ));
8298
}
8399
ctx->tcp_sock = tcp_sock;
84100

@@ -97,14 +113,14 @@ fd_bundle_client_create_conn( fd_bundle_tile_t * ctx ) {
97113

98114
FD_LOG_INFO(( "Connecting to %s://" FD_IP4_ADDR_FMT ":%hu (%.*s)",
99115
scheme,
100-
FD_IP4_ADDR_FMT_ARGS( ctx->server_ip4_addr ), ctx->server_tcp_port,
101-
(int)ctx->server_fqdn_len, ctx->server_fqdn ));
116+
FD_IP4_ADDR_FMT_ARGS( ip4_addr ), ctx->server_tcp_port,
117+
(int)ctx->server_sni_len, ctx->server_sni ));
102118

103-
int connect_err = fd_bundle_client_do_connect( ctx );
119+
int connect_err = fd_bundle_client_do_connect( ctx, ip4_addr );
104120
if( FD_UNLIKELY( connect_err ) ) {
105121
if( FD_UNLIKELY( connect_err!=EINPROGRESS ) ) {
106122
FD_LOG_WARNING(( "connect(tcp_sock," FD_IP4_ADDR_FMT ":%u) failed (%i-%s)",
107-
FD_IP4_ADDR_FMT_ARGS( ctx->server_ip4_addr ), ctx->server_tcp_port,
123+
FD_IP4_ADDR_FMT_ARGS( ip4_addr ), ctx->server_tcp_port,
108124
connect_err, fd_io_strerror( connect_err ) ));
109125
fd_bundle_client_reset( ctx );
110126
ctx->metrics.transport_fail_cnt++;
@@ -127,7 +143,7 @@ fd_bundle_client_create_conn( fd_bundle_tile_t * ctx ) {
127143
SSL_set_bio( ssl, bio, bio ); /* moves ownership of bio */
128144
SSL_set_connect_state( ssl );
129145

130-
if( FD_UNLIKELY( !SSL_set_tlsext_host_name( ssl, ctx->server_fqdn ) ) ) {
146+
if( FD_UNLIKELY( !SSL_set_tlsext_host_name( ssl, ctx->server_sni ) ) ) {
131147
FD_LOG_ERR(( "SSL_set_tlsext_host_name failed" ));
132148
}
133149

@@ -162,7 +178,7 @@ fd_bundle_client_request_builder_info( fd_bundle_tile_t * ctx ) {
162178
static char const path[] = "/block_engine.BlockEngineValidator/GetBlockBuilderFeeInfo";
163179
int req_ok = fd_grpc_client_request_start(
164180
ctx->grpc_client,
165-
ctx->server_fqdn, ctx->server_fqdn_len, ctx->server_tcp_port,
181+
ctx->server_sni, ctx->server_sni_len, ctx->server_tcp_port,
166182
path, sizeof(path)-1,
167183
FD_BUNDLE_CLIENT_REQ_Bundle_GetBlockBuilderFeeInfo,
168184
&block_engine_BlockBuilderFeeInfoRequest_msg, &req,
@@ -181,7 +197,7 @@ fd_bundle_client_subscribe_packets( fd_bundle_tile_t * ctx ) {
181197
static char const path[] = "/block_engine.BlockEngineValidator/SubscribePackets";
182198
int req_ok = fd_grpc_client_request_start(
183199
ctx->grpc_client,
184-
ctx->server_fqdn, ctx->server_fqdn_len, ctx->server_tcp_port,
200+
ctx->server_sni, ctx->server_sni_len, ctx->server_tcp_port,
185201
path, sizeof(path)-1,
186202
FD_BUNDLE_CLIENT_REQ_Bundle_SubscribePackets,
187203
&block_engine_SubscribePacketsRequest_msg, &req,
@@ -200,7 +216,7 @@ fd_bundle_client_subscribe_bundles( fd_bundle_tile_t * ctx ) {
200216
static char const path[] = "/block_engine.BlockEngineValidator/SubscribeBundles";
201217
int req_ok = fd_grpc_client_request_start(
202218
ctx->grpc_client,
203-
ctx->server_fqdn, ctx->server_fqdn_len, ctx->server_tcp_port,
219+
ctx->server_sni, ctx->server_sni_len, ctx->server_tcp_port,
204220
path, sizeof(path)-1,
205221
FD_BUNDLE_CLIENT_REQ_Bundle_SubscribeBundles,
206222
&block_engine_SubscribeBundlesRequest_msg, &req,
@@ -253,7 +269,7 @@ fd_bundle_client_step( fd_bundle_tile_t * ctx,
253269
if( poll_res==0 ) return;
254270

255271
if( pfds[0].revents & (POLLERR|POLLHUP) ) {
256-
int connect_err = fd_bundle_client_do_connect( ctx );
272+
int connect_err = fd_bundle_client_do_connect( ctx, 0 );
257273
FD_LOG_INFO(( "Bundle gRPC connect attempt failed (%i-%s)", connect_err, fd_io_strerror( connect_err ) ));
258274
fd_bundle_client_reset( ctx );
259275
ctx->metrics.transport_fail_cnt++;
@@ -296,7 +312,7 @@ fd_bundle_client_step( fd_bundle_tile_t * ctx,
296312

297313
/* Drive auth */
298314
if( FD_UNLIKELY( ctx->auther.needs_poll ) ) {
299-
fd_bundle_auther_poll( &ctx->auther, ctx->grpc_client, ctx->keyguard_client, ctx->server_fqdn, ctx->server_fqdn_len, ctx->server_tcp_port );
315+
fd_bundle_auther_poll( &ctx->auther, ctx->grpc_client, ctx->keyguard_client, ctx->server_sni, ctx->server_sni_len, ctx->server_tcp_port );
300316
*charge_busy = 1;
301317
return;
302318
}

src/disco/bundle/fd_bundle_tile.c

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
#include <sys/mman.h> /* PROT_READ (seccomp) */
1212
#include <sys/uio.h> /* writev */
1313
#include <netinet/in.h> /* AF_INET */
14-
#include <netdb.h> /* getaddrinfo */
14+
#include <netinet/tcp.h> /* TCP_FASTOPEN_CONNECT (seccomp) */
15+
#include "../../waltz/resolv/fd_netdb.h"
1516

1617
#include "generated/fd_bundle_tile_seccomp.h"
1718

@@ -123,12 +124,11 @@ after_credit( fd_bundle_tile_t * ctx,
123124
}
124125

125126
static void
126-
resolve_url( fd_url_t * url_,
127-
char const * url_str,
128-
ulong url_str_len,
129-
uint * ip4_addr,
130-
ushort * tcp_port,
131-
_Bool * is_ssl ) {
127+
parse_url( fd_url_t * url_,
128+
char const * url_str,
129+
ulong url_str_len,
130+
ushort * tcp_port,
131+
_Bool * is_ssl ) {
132132

133133
/* Parse URL */
134134

@@ -182,29 +182,16 @@ resolve_url( fd_url_t * url_,
182182
}
183183
char host_cstr[ 256 ];
184184
fd_cstr_fini( fd_cstr_append_text( fd_cstr_init( host_cstr ), url->host, url->host_len ) );
185-
186-
struct addrinfo hints, *res;
187-
memset( &hints, 0, sizeof(hints) );
188-
hints.ai_family = AF_INET;
189-
190-
int err = getaddrinfo( host_cstr, NULL, &hints, &res );
191-
if( FD_UNLIKELY( err ) ) {
192-
FD_LOG_ERR(( "getaddrinfo `%s` failed (%d-%s)", host_cstr, err, gai_strerror( err ) ));
193-
}
194-
195-
*ip4_addr = ((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr;
196-
freeaddrinfo( res );
197185
}
198186

199187
static void
200-
fd_bundle_tile_resolve_endpoint( fd_bundle_tile_t * ctx,
201-
fd_topo_tile_t const * tile ) {
188+
fd_bundle_tile_parse_endpoint( fd_bundle_tile_t * ctx,
189+
fd_topo_tile_t const * tile ) {
202190
fd_url_t url[1];
203191
_Bool is_ssl = 0;
204-
resolve_url(
192+
parse_url(
205193
url,
206194
tile->bundle.url, tile->bundle.url_len,
207-
&ctx->server_ip4_addr,
208195
&ctx->server_tcp_port,
209196
&is_ssl
210197
);
@@ -214,6 +201,14 @@ fd_bundle_tile_resolve_endpoint( fd_bundle_tile_t * ctx,
214201
fd_cstr_fini( fd_cstr_append_text( fd_cstr_init( ctx->server_fqdn ), url->host, url->host_len ) );
215202
ctx->server_fqdn_len = url->host_len;
216203

204+
if( FD_UNLIKELY( tile->bundle.sni_len ) ) {
205+
fd_cstr_fini( fd_cstr_append_text( fd_cstr_init( ctx->server_sni ), tile->bundle.sni, tile->bundle.sni_len ) );
206+
ctx->server_sni_len = tile->bundle.sni_len;
207+
} else {
208+
fd_cstr_fini( fd_cstr_append_text( fd_cstr_init( ctx->server_sni ), url->host, url->host_len ) );
209+
ctx->server_sni_len = url->host_len;
210+
}
211+
217212
ctx->is_ssl = !!is_ssl;
218213
#if !FD_HAS_OPENSSL
219214
if( FD_UNLIKELY( is_ssl ) ) {
@@ -379,19 +374,10 @@ privileged_init( fd_topo_t * topo,
379374
uchar const * public_key = fd_keyload_load( tile->bundle.identity_key_path, 1 /* public key only */ );
380375
fd_memcpy( ctx->auther.pubkey, public_key, 32UL );
381376

382-
/* DNS resolution does arbitrary syscalls and system ops, therefore
383-
has to be run outside the sandbox. */
384-
fd_bundle_tile_resolve_endpoint( ctx, tile );
385-
386-
/* Override server name indication */
387-
if( FD_UNLIKELY( tile->bundle.sni_len ) ) {
388-
fd_cstr_fini( fd_cstr_append_text( fd_cstr_init( ctx->server_fqdn ), tile->bundle.sni, tile->bundle.sni_len ) );
389-
ctx->server_fqdn_len = tile->bundle.sni_len;
390-
}
377+
ctx->keylog_fd = -1;
391378

392379
# if FD_HAS_OPENSSL
393380

394-
ctx->keylog_fd = -1;
395381
if( FD_UNLIKELY( tile->bundle.key_log_path[0] ) ) {
396382
ctx->keylog_fd = open( tile->bundle.key_log_path, O_WRONLY|O_APPEND|O_CREAT, 0644 );
397383
if( FD_UNLIKELY( ctx->keylog_fd < 0 ) ) {
@@ -406,6 +392,11 @@ privileged_init( fd_topo_t * topo,
406392

407393
# endif /* FD_HAS_OPENSSL */
408394

395+
/* Init resolver */
396+
if( FD_UNLIKELY( !fd_netdb_open_fds( ctx->netdb_fds ) ) ) {
397+
FD_LOG_ERR(( "fd_netdb_open_fds failed" ));
398+
}
399+
409400
/* Random seed for header hashmap */
410401
if( FD_UNLIKELY( !fd_rng_secure( &ctx->map_seed, sizeof(ulong) ) ) ) {
411402
FD_LOG_CRIT(( "fd_rng_secure failed" ));
@@ -490,16 +481,23 @@ unprivileged_init( fd_topo_t * topo,
490481
/* Force tile to output a plugin message on startup */
491482
ctx->bundle_status_plugin = 127;
492483
ctx->bundle_status_recent = FD_PLUGIN_MSG_BLOCK_ENGINE_UPDATE_STATUS_DISCONNECTED;
484+
485+
fd_bundle_tile_parse_endpoint( ctx, tile );
493486
}
494487

495488
static ulong
496489
populate_allowed_seccomp( fd_topo_t const * topo,
497490
fd_topo_tile_t const * tile,
498491
ulong out_cnt,
499492
struct sock_filter * out ) {
500-
(void)topo; (void)tile;
493+
fd_bundle_tile_t * ctx = fd_topo_obj_laddr( topo, tile->tile_obj_id );
501494
populate_sock_filter_policy_fd_bundle_tile(
502-
out_cnt, out, (uint)fd_log_private_logfile_fd() );
495+
out_cnt, out,
496+
(uint)fd_log_private_logfile_fd(),
497+
(uint)ctx->keylog_fd,
498+
(uint)ctx->netdb_fds->etc_hosts,
499+
(uint)ctx->netdb_fds->etc_resolv_conf
500+
);
503501
return sock_filter_policy_fd_bundle_tile_instr_cnt;
504502
}
505503

@@ -508,14 +506,19 @@ populate_allowed_fds( fd_topo_t const * topo,
508506
fd_topo_tile_t const * tile,
509507
ulong out_fds_cnt,
510508
int * out_fds ) {
511-
(void)topo; (void)tile;
509+
fd_bundle_tile_t * ctx = fd_topo_obj_laddr( topo, tile->tile_obj_id );
512510

513-
if( FD_UNLIKELY( out_fds_cnt<2UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
511+
if( FD_UNLIKELY( out_fds_cnt<5UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
514512

515513
ulong out_cnt = 0UL;
516514
out_fds[ out_cnt++ ] = 2; /* stderr */
517515
if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
518516
out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
517+
if( FD_LIKELY( ctx->netdb_fds->etc_hosts >= 0 ) )
518+
out_fds[ out_cnt++ ] = ctx->netdb_fds->etc_hosts;
519+
out_fds[ out_cnt++ ] = ctx->netdb_fds->etc_resolv_conf;
520+
if( FD_UNLIKELY( ctx->keylog_fd>=0 ) )
521+
out_fds[ out_cnt++ ] = ctx->keylog_fd;
519522
return out_cnt;
520523
}
521524

src/disco/bundle/fd_bundle_tile.seccomppolicy

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
# logfile_fd: It can be disabled by configuration, but typically tiles
22
# will open a log file on boot and write all messages there.
3-
unsigned int logfile_fd
3+
#
4+
# keylog_fd: Log HTTPS session keys to file (development option)
5+
#
6+
# etc_hosts_fd, etc_resolv_conf: Used to resolve DNS records.
7+
uint logfile_fd, uint keylog_fd, uint etc_hosts_fd, uint etc_resolv_conf
48

59
# bundle: Read from TCP connection (HTTPS)
10+
#
11+
# resolv: Read DNS config
612
read
713

814
# bundle: Read from TCP connection (HTTP)
9-
recvmsg: (eq (arg 2) "MSG_NOSIGNAL|MSG_DONTWAIT")
15+
#
16+
# resolv: Receive DNS responses
17+
recvmsg: (or (eq (arg 2) "MSG_NOSIGNAL|MSG_DONTWAIT")
18+
(eq (arg 2) 0))
1019

1120
# bundle: Write to TCP connection (HTTPS)
1221
#
@@ -19,36 +28,73 @@ recvmsg: (eq (arg 2) "MSG_NOSIGNAL|MSG_DONTWAIT")
1928
# that descriptor 2 is always STDERR.
2029
write
2130

31+
# OpenSSL: SSLKEYLOGFILE
32+
writev: (and (eq (arg 0) keylog_fd)
33+
(eq (arg 2) 2))
34+
2235
# bundle: Write to TCP connection (HTTP)
23-
sendmsg: (eq (arg 2) "MSG_NOSIGNAL|MSG_DONTWAIT")
36+
#
37+
# resolv: Send DNS queries via UDP or TCP
38+
sendmsg: (or (eq (arg 2) "MSG_NOSIGNAL|MSG_DONTWAIT")
39+
(eq (arg 2) "MSG_FASTOPEN|MSG_NOSIGNAL")
40+
(eq (arg 2) "MSG_NOSIGNAL"))
41+
42+
# resolv: Send DNS queries via UDP or TCP
43+
sendto: (eq (arg 3) "MSG_NOSIGNAL")
2444

2545
# logging: 'WARNING' and above fsync the logfile to disk immediately
2646
#
2747
# arg 0 is the file descriptor to fsync.
2848
fsync: (eq (arg 0) logfile_fd)
2949

3050
# bundle: TCP connection to the bundle server needs to be established.
31-
socket: (and (eq (arg 0) "AF_INET")
32-
(eq (arg 1) "SOCK_STREAM|SOCK_CLOEXEC")
33-
(eq (arg 2) "IPPROTO_TCP"))
51+
#
52+
# resolv: Send DNS queries via UDP or TCP
53+
socket: (and (or (eq (arg 0) "AF_INET")
54+
(eq (arg 0) "AF_INET6"))
55+
(or (and (or (eq (arg 1) "SOCK_STREAM|SOCK_CLOEXEC")
56+
(eq (arg 1) "SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK"))
57+
(eq (arg 2) 0))
58+
(and (eq (arg 1) "SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK")
59+
(eq (arg 2) 0))
60+
(and (eq (arg 1) "SOCK_DGRAM|SOCK_CLOEXEC")
61+
(eq (arg 2) "IPPROTO_UDP"))))
3462
connect
3563

3664
# bundle: TCP connection is closed if there is an error, and a new one
3765
# is reopened
3866
shutdown: (eq (arg 1) "SHUT_WR")
67+
68+
# bundle: TCP connection is closed if there is an error, and a new one
69+
# is reopened
70+
# resolv: Close UDP/TCP sockets once done querying DNS
3971
close
4072

4173
# bundle: Make TCP connection non-blocking
4274
fcntl: (and (eq (arg 1) "F_SETFL")
4375
(eq (arg 2) "O_NONBLOCK"))
4476

77+
# resolv: Bind DNS request socket
78+
bind: (or (eq (arg 2) "sizeof(struct sockaddr_in)")
79+
(eq (arg 2) "sizeof(struct sockaddr_in6)"))
80+
4581
# bundle: Wait for TCP connection to be established
46-
poll: (and (eq (arg 1) 1)
47-
(eq (arg 2) 0))
82+
#
83+
# resolv: Wait for DNS responses
84+
poll
85+
86+
# bundle: configure TCP socket (for gRPC connection)
87+
#
88+
# resolv: configure UDP or TCP socket (for DNS queries)
89+
setsockopt: (or (and (eq (arg 1) SOL_SOCKET)
90+
(eq (arg 2) SO_RCVBUF))
91+
(and (eq (arg 1) IPPROTO_TCP)
92+
(eq (arg 2) TCP_FASTOPEN_CONNECT))
93+
(and (eq (arg 1) IPPROTO_IPV6)
94+
(eq (arg 2) IPV6_V6ONLY)))
4895

49-
# bundle: configure TCP socket
50-
setsockopt: (and (eq (arg 1) SOL_SOCKET)
51-
(eq (arg 2) SO_RCVBUF))
96+
# resolv: check if DNS queries use IPv6
97+
getsockname
5298

5399
# openssl: RAND_bytes requires getpid
54100
#
@@ -65,3 +111,9 @@ getpid
65111
# picking connection IDs. The OpenSSL implementation calls getrandom
66112
# internally for periodically reseeding the RNG.
67113
getrandom
114+
115+
# resolv: Read DNS config
116+
lseek: (and (or (eq (arg 0) etc_resolv_conf)
117+
(eq (arg 0) etc_hosts_fd))
118+
(eq (arg 1) 0)
119+
(eq (arg 2) "SEEK_SET"))

0 commit comments

Comments
 (0)