From ef85969b61471d0a7ade059235f5eb4165519d81 Mon Sep 17 00:00:00 2001 From: Richard Patel Date: Mon, 1 Jun 2026 04:43:09 +0000 Subject: [PATCH 1/5] events: fix lifecycle bugs --- src/disco/events/fd_event_client.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/disco/events/fd_event_client.c b/src/disco/events/fd_event_client.c index ae69ab62f58..6dce7616236 100644 --- a/src/disco/events/fd_event_client.c +++ b/src/disco/events/fd_event_client.c @@ -145,6 +145,8 @@ fd_event_client_new( void * shmem, fd_event_client_t * client = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_event_client_t), sizeof(fd_event_client_t) ); void * grpc_client_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_grpc_client_align(), fd_grpc_client_footprint( buf_max ) ); + memset( client, 0, sizeof(fd_event_client_t) ); + fd_url_t url[1]; _Bool _is_ssl = 0; if( FD_UNLIKELY( fd_url_parse_endpoint( url, @@ -275,6 +277,7 @@ disconnect( fd_event_client_t * client, int reason, int err, int _backoff ) { + client->event_stream = NULL; #if FD_HAS_OPENSSL if( FD_UNLIKELY( client->ssl ) ) { SSL_free( client->ssl ); @@ -284,10 +287,10 @@ disconnect( fd_event_client_t * client, if( FD_LIKELY( -1!=client->sockfd ) ) { if( FD_UNLIKELY( -1==close( client->sockfd ) ) ) FD_LOG_ERR(( "close() failed (%d-%s)", errno, fd_io_strerror( errno ) )); client->sockfd = -1; - client->state = FD_EVENT_CLIENT_STATE_DISCONNECTED; - fd_circq_reset_cursor( client->circq ); } client->auth_deadline = LONG_MAX; + client->state = FD_EVENT_CLIENT_STATE_DISCONNECTED; + fd_circq_reset_cursor( client->circq ); switch( reason ) { case DISCONNECT_REASON_IDENTITY_CHANGED: @@ -719,7 +722,7 @@ fd_event_client_grpc_rx_timeout( void * app_ctx, static void fd_event_client_grpc_ping_ack( void * app_ctx ) { (void)app_ctx; - FD_LOG_WARNING(( "Event gRPC ping ack" )); + FD_LOG_DEBUG(( "Event gRPC ping ack" )); } static void From 0d47a69e52669441747b1b189fc3c83797201e19 Mon Sep 17 00:00:00 2001 From: Richard Patel Date: Mon, 1 Jun 2026 00:46:58 +0000 Subject: [PATCH 2/5] events: remove custom signing scheme Remove custom challenge-response authentication scheme from event telemetry API. To be replaced with mutual TLS in a future commit. Rename 'authenticate' method to 'hello' to avoid confusion. --- book/api/metrics-generated.md | 3 +- contrib/event-test-server/Cargo.toml | 9 +- contrib/event-test-server/build.rs | 5 +- contrib/event-test-server/src/main.rs | 43 ++--- src/app/shared/commands/watch/watch.c | 11 +- src/disco/events/Local.mk | 10 +- src/disco/events/fd_event_client.c | 155 ++++-------------- src/disco/events/fd_event_client.h | 10 +- src/disco/events/fd_event_tile.c | 19 +-- src/disco/events/schema/event.proto | 22 +-- src/disco/keyguard/fd_keyguard.h | 17 +- src/disco/keyguard/fd_keyguard_authorize.c | 8 - src/disco/keyguard/fd_keyguard_match.c | 13 -- src/disco/keyguard/fd_sign_tile.c | 9 - .../metrics/generated/fd_metrics_event.c | 1 - .../metrics/generated/fd_metrics_event.h | 10 +- src/disco/metrics/metrics.xml | 3 +- 17 files changed, 95 insertions(+), 253 deletions(-) diff --git a/book/api/metrics-generated.md b/book/api/metrics-generated.md index 373a001e289..19c48e2142e 100644 --- a/book/api/metrics-generated.md +++ b/book/api/metrics-generated.md @@ -1219,10 +1219,9 @@ | event_​events_​acked | counter | The total number of events acknowledged by the event service | | event_​bytes_​written | counter | The total number of bytes written to the event service | | event_​bytes_​read | counter | The total number of bytes read from the event service | -| event_​auth_​fail | counter | The total number of authentication failures with the event service | | event_​invalid_​msg | counter | The total number of malformed messages received from the event service | | event_​connect_​attempts | counter | The total number of connection attempts to the event service | -| event_​handshake_​timeouts | counter | The total number of authentication handshake timeouts with the event service | +| event_​handshake_​timeouts | counter | The total number of handshake timeouts with the event service | diff --git a/contrib/event-test-server/Cargo.toml b/contrib/event-test-server/Cargo.toml index 1f8882c4a62..e3cc9cb81e6 100644 --- a/contrib/event-test-server/Cargo.toml +++ b/contrib/event-test-server/Cargo.toml @@ -7,13 +7,10 @@ edition = "2021" tonic = "0.14" prost = "0.14" tonic-prost = "0.14" -tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } +tokio = { version = "1.0", features = ["macros"] } tokio-stream = "0.1" hex = "0.4" -tower-http = { version = "0.6", features = ["trace"] } -tracing = "0.1" -tracing-subscriber = "0.3" [build-dependencies] -tonic-build = "0.14.2" -tonic-prost-build = "0.14.2" +tonic-build = "0.14" +tonic-prost-build = "0.14" diff --git a/contrib/event-test-server/build.rs b/contrib/event-test-server/build.rs index 49bd22f9287..fdf42d9d026 100644 --- a/contrib/event-test-server/build.rs +++ b/contrib/event-test-server/build.rs @@ -1,6 +1,9 @@ fn main() -> Result<(), Box> { tonic_prost_build::configure() .build_server(true) - .compile_protos(&["../../src/disco/events/schema/event.proto"], &["../../src/disco/events/schema"])?; + .compile_protos( + &["../../src/disco/events/schema/event.proto"], + &["../../src/disco/events/schema"], + )?; Ok(()) } diff --git a/contrib/event-test-server/src/main.rs b/contrib/event-test-server/src/main.rs index a905872cfc7..680864fd8d5 100644 --- a/contrib/event-test-server/src/main.rs +++ b/contrib/event-test-server/src/main.rs @@ -6,13 +6,9 @@ pub mod events { tonic::include_proto!("events.v1"); } -use events::event_service_server::{EventService, EventServiceServer}; -use events::{ - StreamEventsRequest, StreamEventsResponse, - AuthenticateRequest, AuthenticateResponse, - ConfirmAuthChallengeRequest, ConfirmAuthChallengeResponse, -}; use events::event::Event; +use events::event_service_server::{EventService, EventServiceServer}; +use events::{HelloRequest, HelloResponse, StreamEventsRequest, StreamEventsResponse}; fn event_kind_name(event: &Option) -> &'static str { match event.as_ref().and_then(|e| e.event.as_ref()) { @@ -29,23 +25,12 @@ pub struct MyEventService; impl EventService for MyEventService { type StreamEventsStream = ReceiverStream>; - async fn authenticate( - &self, - request: Request, - ) -> Result, Status> { - println!("Received authenticate request from identity: {:?}", - hex::encode(&request.get_ref().identity_pubkey)); - let challenge = vec![0u8; 32]; - Ok(Response::new(AuthenticateResponse { challenge })) - } - - async fn confirm_auth_challenge( + async fn hello( &self, - request: Request, - ) -> Result, Status> { - println!("Received confirm_auth_challenge with signed challenge: {:?}", - hex::encode(&request.get_ref().signed_challenge)); - Ok(Response::new(ConfirmAuthChallengeResponse {})) + _request: Request, + ) -> Result, Status> { + println!("Received hello request"); + Ok(Response::new(HelloResponse {})) } async fn stream_events( @@ -61,9 +46,15 @@ impl EventService for MyEventService { loop { match stream.message().await { Ok(Some(event_tx)) => { - println!("Received event: nonce={}, event_id={}, kind={}", - event_tx.nonce, event_tx.event_id, event_kind_name(&event_tx.event)); - let ack = StreamEventsResponse { nonce: event_tx.nonce }; + println!( + "Received event: nonce={}, event_id={}, kind={}", + event_tx.nonce, + event_tx.event_id, + event_kind_name(&event_tx.event) + ); + let ack = StreamEventsResponse { + nonce: event_tx.nonce, + }; if tx.send(Ok(ack)).await.is_err() { eprintln!("Failed to send ack, client disconnected"); break; @@ -85,7 +76,7 @@ impl EventService for MyEventService { } } -#[tokio::main] +#[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), Box> { let addr = "127.0.0.1:7878".parse()?; println!("Listening on {}", addr); diff --git a/src/app/shared/commands/watch/watch.c b/src/app/shared/commands/watch/watch.c index 1d1d2b0e884..04af105a461 100644 --- a/src/app/shared/commands/watch/watch.c +++ b/src/app/shared/commands/watch/watch.c @@ -608,12 +608,11 @@ write_event( config_t const * config, ulong connection_state = cur_tile[ event_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, EVENT, CONNECTION_STATE ) ]; char const * connection_state_str; switch( connection_state ) { - case 0UL: connection_state_str = "disconnected"; break; - case 1UL: connection_state_str = "connecting"; break; - case 2UL: connection_state_str = "authenticating"; break; - case 3UL: connection_state_str = "confirming_auth"; break; - case 4UL: connection_state_str = "connected"; break; - default: connection_state_str = "unknown"; break; + case 0UL: connection_state_str = "disconnected"; break; + case 1UL: connection_state_str = "connecting"; break; + case 2UL: connection_state_str = "registering"; break; + case 3UL: connection_state_str = "connected"; break; + default: connection_state_str = "unknown"; break; } ulong event_queue_count = cur_tile[ event_tile_idx*FD_METRICS_TOTAL_SZ+MIDX( GAUGE, EVENT, EVENT_QUEUE_COUNT ) ]; diff --git a/src/disco/events/Local.mk b/src/disco/events/Local.mk index 9fa1b7775c3..e09545fb715 100644 --- a/src/disco/events/Local.mk +++ b/src/disco/events/Local.mk @@ -1,10 +1,10 @@ -ifdef FD_HAS_HOSTED $(call add-hdrs,fd_circq.h) -$(call add-objs,fd_circq fd_event_client,fd_disco) +$(call add-objs,fd_circq,fd_disco) +$(call make-unit-test,test_circq,test_circq,fd_disco fd_flamenco fd_tango fd_util) +$(call run-unit-test,test_circq) +ifdef FD_HAS_HOSTED +$(call add-objs,fd_event_client,fd_disco) ifdef FD_HAS_ALLOCA $(call add-objs,fd_event_tile,fd_disco) endif - -$(call make-unit-test,test_circq,test_circq,fd_disco fd_flamenco fd_tango fd_util) -$(call run-unit-test,test_circq) endif diff --git a/src/disco/events/fd_event_client.c b/src/disco/events/fd_event_client.c index 6dce7616236..77a274be5e3 100644 --- a/src/disco/events/fd_event_client.c +++ b/src/disco/events/fd_event_client.c @@ -30,12 +30,10 @@ #define DISCONNECT_REASON_TRANSPORT_FAILED (4) #define DISCONNECT_REASON_PEER_CLOSED (5) #define DISCONNECT_REASON_INVALID_CURSOR (6) -#define DISCONNECT_REASON_AUTH_FAILED (7) -#define DISCONNECT_REASON_INVALID_PROTOBUF (8) +#define DISCONNECT_REASON_INVALID_PROTOBUF (7) -#define FD_EVENT_CLIENT_REQ_CTX_AUTHENTICATE (1UL) -#define FD_EVENT_CLIENT_REQ_CTX_CONFIRM_AUTH (2UL) -#define FD_EVENT_CLIENT_REQ_CTX_STREAM_EVENTS (3UL) +#define FD_EVENT_CLIENT_REQ_CTX_HELLO (1UL) +#define FD_EVENT_CLIENT_REQ_CTX_STREAM_EVENTS (2UL) struct fd_event_client { fd_grpc_client_t * grpc_client; @@ -85,9 +83,9 @@ struct fd_event_client { SSL * ssl; #endif - /* wallclock deadline for auth handshake, LONG_MAX if not + /* wallclock deadline for hello handshake, LONG_MAX if not authenticating. */ - long auth_deadline; + long hello_deadline; char server_fqdn[ 256 ]; /* cstr */ ulong server_fqdn_len; @@ -190,7 +188,7 @@ fd_event_client_new( void * shmem, "To install OpenSSL, re-run ./deps.sh and do a clean rebuild." )); } #endif - client->auth_deadline = LONG_MAX; + client->hello_deadline = LONG_MAX; client->state = FD_EVENT_CLIENT_STATE_DISCONNECTED; client->disconnected.reconnect_deadline = 0L; @@ -288,7 +286,7 @@ disconnect( fd_event_client_t * client, if( FD_UNLIKELY( -1==close( client->sockfd ) ) ) FD_LOG_ERR(( "close() failed (%d-%s)", errno, fd_io_strerror( errno ) )); client->sockfd = -1; } - client->auth_deadline = LONG_MAX; + client->hello_deadline = LONG_MAX; client->state = FD_EVENT_CLIENT_STATE_DISCONNECTED; fd_circq_reset_cursor( client->circq ); @@ -320,10 +318,6 @@ disconnect( fd_event_client_t * client, FD_LOG_WARNING(( "disconnected: invalid cursor" )); client->metrics.transport_fail_cnt++; break; - case DISCONNECT_REASON_AUTH_FAILED: - FD_LOG_WARNING(( "disconnected: authentication failed" )); - client->metrics.transport_fail_cnt++; - break; case DISCONNECT_REASON_INVALID_PROTOBUF: FD_LOG_WARNING(( "disconnected: invalid protobuf message received" )); client->metrics.transport_fail_cnt++; @@ -443,6 +437,11 @@ static void fd_event_client_grpc_conn_established( void * app_ctx ) { fd_event_client_t * client = app_ctx; + client->event_stream = NULL; + client->metrics.transport_success_cnt++; + client->state = FD_EVENT_CLIENT_STATE_CONNECTED; + client->connected.connected_timestamp = fd_log_wallclock(); + fd_pb_encoder_t auth_req[1]; uchar buffer[ 256UL ]; fd_pb_encoder_init( auth_req, buffer, sizeof(buffer) ); @@ -458,14 +457,14 @@ fd_event_client_grpc_conn_established( void * app_ctx ) { fd_grpc_h2_stream_t * stream = fd_grpc_client_request_start1( client->grpc_client, - "/events.v1.EventService/Authenticate", strlen("/events.v1.EventService/Authenticate"), - FD_EVENT_CLIENT_REQ_CTX_AUTHENTICATE, + "/events.v1.EventService/Hello", strlen("/events.v1.EventService/Hello"), + FD_EVENT_CLIENT_REQ_CTX_HELLO, buffer, fd_pb_encoder_out_sz( auth_req ), NULL, 0UL, 0 /* not streaming */ ); if( FD_UNLIKELY( !stream ) ) { - FD_LOG_WARNING(( "Failed to start Authenticate request" )); + FD_LOG_WARNING(( "Failed to start Hello request" )); return; } @@ -473,109 +472,22 @@ fd_event_client_grpc_conn_established( void * app_ctx ) { fd_grpc_client_deadline_set( stream, FD_GRPC_DEADLINE_HEADER, now+(long)5e9 /* 5s */ ); fd_grpc_client_deadline_set( stream, FD_GRPC_DEADLINE_RX_END, now+(long)5e9 /* 5s */ ); - client->state = FD_EVENT_CLIENT_STATE_AUTHENTICATING; - client->auth_deadline = now + (long)5e9; /* 5s */ + client->state = FD_EVENT_CLIENT_STATE_REGISTERING; + client->hello_deadline = now + (long)5e9; /* 5s */ FD_LOG_INFO(( "Requesting auth challenge from event server " FD_IP4_ADDR_FMT ":%u (%.*s)", FD_IP4_ADDR_FMT_ARGS( client->server_ip4_addr ), client->server_tcp_port, (int)client->server_fqdn_len, client->server_fqdn )); } static void -fd_event_client_handle_auth_challenge_resp( fd_event_client_t * client, - void const * protobuf, - ulong protobuf_sz ) { - fd_pb_inbuf_t inbuf[1]; - fd_pb_inbuf_init( inbuf, protobuf, protobuf_sz ); - - if( FD_UNLIKELY( protobuf_sz==0UL ) ) { - FD_LOG_WARNING(( "Empty auth challenge response" )); - client->defer_disconnect = DISCONNECT_REASON_AUTH_FAILED; - return; - } - - fd_pb_tlv_t challenge_tlv; - if( FD_UNLIKELY( !fd_pb_read_tlv( inbuf, &challenge_tlv ) ) ) { - FD_LOG_WARNING(( "Failed to parse auth challenge response" )); - client->defer_disconnect = DISCONNECT_REASON_AUTH_FAILED; - return; - } - - if( FD_UNLIKELY( challenge_tlv.field_id!=1U || challenge_tlv.wire_type!=FD_PB_WIRE_TYPE_LEN ) ) { - FD_LOG_WARNING(( "Unexpected field in auth challenge response" )); - client->defer_disconnect = DISCONNECT_REASON_AUTH_FAILED; - return; - } - - ulong challenge_len = challenge_tlv.len; - if( FD_UNLIKELY( challenge_len!=32UL ) ) { - FD_LOG_WARNING(( "Invalid challenge size: %lu bytes", challenge_len )); - client->defer_disconnect = DISCONNECT_REASON_AUTH_FAILED; - return; - } - - if( FD_UNLIKELY( fd_pb_inbuf_sz( inbuf )defer_disconnect = DISCONNECT_REASON_AUTH_FAILED; - return; - } - - uchar const * challenge_data = inbuf->cur; - inbuf->cur += challenge_len; - - if( FD_UNLIKELY( fd_pb_inbuf_sz( inbuf ) ) ) { - FD_LOG_WARNING(( "Trailing data in auth challenge response" )); - client->defer_disconnect = DISCONNECT_REASON_AUTH_FAILED; - return; - } - - uchar signed_challenge[ 64UL ]; - fd_keyguard_client_sign( client->keyguard_client, - signed_challenge, - challenge_data, - 32UL, - FD_KEYGUARD_SIGN_TYPE_FD_EVENTS_AUTH_CONCAT_ED25519 ); - - fd_pb_encoder_t confirm_req[1]; - uchar buffer[ 128UL ]; - fd_pb_encoder_init( confirm_req, buffer, sizeof(buffer) ); - fd_pb_push_bytes( confirm_req, 1U, signed_challenge, 64UL ); - - fd_grpc_h2_stream_t * stream = fd_grpc_client_request_start1( - client->grpc_client, - "/events.v1.EventService/ConfirmAuthChallenge", strlen("/events.v1.EventService/ConfirmAuthChallenge"), - FD_EVENT_CLIENT_REQ_CTX_CONFIRM_AUTH, - buffer, fd_pb_encoder_out_sz( confirm_req ), - NULL, 0UL, - 0 /* not streaming */ ); - - if( FD_UNLIKELY( !stream ) ) { - FD_LOG_WARNING(( "Failed to start ConfirmAuthChallenge request" )); - client->defer_disconnect = DISCONNECT_REASON_AUTH_FAILED; - return; - } - - long now = fd_log_wallclock(); - fd_grpc_client_deadline_set( stream, FD_GRPC_DEADLINE_HEADER, now+(long)5e9 /* 5s */ ); - fd_grpc_client_deadline_set( stream, FD_GRPC_DEADLINE_RX_END, now+(long)5e9 /* 5s */ ); - - client->state = FD_EVENT_CLIENT_STATE_CONFIRMING_AUTH; - FD_LOG_DEBUG(( "Sent signed auth challenge" )); -} - -static void -fd_event_client_handle_confirm_auth_resp( fd_event_client_t * client, - void const * protobuf, - ulong protobuf_sz ) { - (void)protobuf; - (void)protobuf_sz; - - client->event_stream = NULL; - client->metrics.transport_success_cnt++; +fd_env_client_handle_hello_resp( fd_event_client_t * client, + void const * protobuf, + ulong protobuf_sz ) { + (void)protobuf; (void)protobuf_sz; client->state = FD_EVENT_CLIENT_STATE_CONNECTED; - client->connected.connected_timestamp = fd_log_wallclock(); FD_LOG_NOTICE(( "connected to event server " FD_IP4_ADDR_FMT ":%u (%.*s)", - FD_IP4_ADDR_FMT_ARGS( client->server_ip4_addr ), client->server_tcp_port, - (int)client->server_fqdn_len, client->server_fqdn )); + FD_IP4_ADDR_FMT_ARGS( client->server_ip4_addr ), client->server_tcp_port, + (int)client->server_fqdn_len, client->server_fqdn )); } static void @@ -645,11 +557,8 @@ fd_event_client_grpc_rx_msg( void * app_ctx, fd_event_client_t * client = app_ctx; switch( request_ctx ) { - case FD_EVENT_CLIENT_REQ_CTX_AUTHENTICATE: - fd_event_client_handle_auth_challenge_resp( client, protobuf, protobuf_sz ); - break; - case FD_EVENT_CLIENT_REQ_CTX_CONFIRM_AUTH: - fd_event_client_handle_confirm_auth_resp( client, protobuf, protobuf_sz ); + case FD_EVENT_CLIENT_REQ_CTX_HELLO: + fd_env_client_handle_hello_resp( client, protobuf, protobuf_sz ); break; case FD_EVENT_CLIENT_REQ_CTX_STREAM_EVENTS: fd_event_client_handle_stream_events_resp( client, protobuf, protobuf_sz ); @@ -681,12 +590,11 @@ fd_event_client_grpc_rx_end( void * app_ctx, if( FD_UNLIKELY( resp->grpc_status!=FD_GRPC_STATUS_OK ) ) { switch( request_ctx ) { - case FD_EVENT_CLIENT_REQ_CTX_AUTHENTICATE: - case FD_EVENT_CLIENT_REQ_CTX_CONFIRM_AUTH: - FD_LOG_WARNING(( "Event authentication failed (gRPC status %u-%s): %.*s", + case FD_EVENT_CLIENT_REQ_CTX_HELLO: + FD_LOG_WARNING(( "Hello request failed (gRPC status %u-%s): %.*s", resp->grpc_status, fd_grpc_status_cstr( resp->grpc_status ), (int)resp->grpc_msg_len, resp->grpc_msg )); - client->defer_disconnect = DISCONNECT_REASON_AUTH_FAILED; + client->defer_disconnect = DISCONNECT_REASON_PEER_CLOSED; return; case FD_EVENT_CLIENT_REQ_CTX_STREAM_EVENTS: FD_LOG_WARNING(( "Event stream failed (gRPC status %u-%s): %.*s", @@ -770,9 +678,9 @@ fd_event_client_poll( fd_event_client_t * client, return; } } - /* Check auth handshake timeout */ - if( FD_UNLIKELY( (client->state==FD_EVENT_CLIENT_STATE_AUTHENTICATING || client->state==FD_EVENT_CLIENT_STATE_CONFIRMING_AUTH) && now>client->auth_deadline ) ) { - FD_LOG_WARNING(( "auth handshake timed out" )); + /* Check hello timeout */ + if( FD_UNLIKELY( client->state==FD_EVENT_CLIENT_STATE_REGISTERING && now>client->hello_deadline ) ) { + FD_LOG_WARNING(( "hello request timed out" )); client->metrics.handshake_timeout_cnt++; disconnect( client, DISCONNECT_REASON_TIMEOUT, 0, 1 ); return; @@ -794,7 +702,6 @@ fd_event_client_poll( fd_event_client_t * client, if( FD_UNLIKELY( client->defer_disconnect!=INT_MAX ) ) { int reason = client->defer_disconnect; client->defer_disconnect = INT_MAX; - if( reason==DISCONNECT_REASON_AUTH_FAILED ) client->metrics.auth_fail_cnt++; if( reason==DISCONNECT_REASON_INVALID_PROTOBUF ) client->metrics.invalid_msg_cnt++; disconnect( client, reason, 0, 1 ); return; diff --git a/src/disco/events/fd_event_client.h b/src/disco/events/fd_event_client.h index 116b08d2c83..2b8b60a60e3 100644 --- a/src/disco/events/fd_event_client.h +++ b/src/disco/events/fd_event_client.h @@ -9,11 +9,10 @@ #include #endif -#define FD_EVENT_CLIENT_STATE_DISCONNECTED (0) -#define FD_EVENT_CLIENT_STATE_CONNECTING (1) -#define FD_EVENT_CLIENT_STATE_AUTHENTICATING (2) -#define FD_EVENT_CLIENT_STATE_CONFIRMING_AUTH (3) -#define FD_EVENT_CLIENT_STATE_CONNECTED (4) +#define FD_EVENT_CLIENT_STATE_DISCONNECTED (0) +#define FD_EVENT_CLIENT_STATE_CONNECTING (1) +#define FD_EVENT_CLIENT_STATE_REGISTERING (2) +#define FD_EVENT_CLIENT_STATE_CONNECTED (3) struct fd_event_client; typedef struct fd_event_client fd_event_client_t; @@ -25,7 +24,6 @@ struct fd_event_client_metrics { ulong events_acked; ulong bytes_written; ulong bytes_read; - ulong auth_fail_cnt; ulong invalid_msg_cnt; ulong connect_attempt_cnt; ulong handshake_timeout_cnt; diff --git a/src/disco/events/fd_event_tile.c b/src/disco/events/fd_event_tile.c index 234e2d56e7e..a8d74db006b 100644 --- a/src/disco/events/fd_event_tile.c +++ b/src/disco/events/fd_event_tile.c @@ -144,16 +144,15 @@ metrics_write( fd_event_tile_t * ctx ) { FD_MGAUGE_SET( EVENT, EVENT_QUEUE_BYTES_CAPACITY, ctx->circq->size ); fd_event_client_metrics_t const * metrics = fd_event_client_metrics( ctx->client ); - FD_MCNT_SET( EVENT, EVENTS_SENT, metrics->events_sent ); - FD_MCNT_SET( EVENT, EVENTS_ACKED, metrics->events_acked ); - FD_MCNT_SET( EVENT, BYTES_WRITTEN, metrics->bytes_written ); - FD_MCNT_SET( EVENT, BYTES_READ, metrics->bytes_read ); - FD_MCNT_SET( EVENT, AUTH_FAIL, metrics->auth_fail_cnt ); - FD_MCNT_SET( EVENT, INVALID_MSG, metrics->invalid_msg_cnt ); - FD_MCNT_SET( EVENT, CONNECT_ATTEMPTS, metrics->connect_attempt_cnt ); - FD_MCNT_SET( EVENT, HANDSHAKE_TIMEOUTS, metrics->handshake_timeout_cnt ); - - FD_MGAUGE_SET( EVENT, CONNECTION_STATE, fd_event_client_state( ctx->client ) ); + FD_MCNT_SET( EVENT, EVENTS_SENT, metrics->events_sent ); + FD_MCNT_SET( EVENT, EVENTS_ACKED, metrics->events_acked ); + FD_MCNT_SET( EVENT, BYTES_WRITTEN, metrics->bytes_written ); + FD_MCNT_SET( EVENT, BYTES_READ, metrics->bytes_read ); + FD_MCNT_SET( EVENT, INVALID_MSG, metrics->invalid_msg_cnt ); + FD_MCNT_SET( EVENT, CONNECT_ATTEMPTS, metrics->connect_attempt_cnt ); + FD_MCNT_SET( EVENT, HANDSHAKE_TIMEOUTS, metrics->handshake_timeout_cnt ); + + FD_MGAUGE_SET( EVENT, CONNECTION_STATE, fd_event_client_state( ctx->client ) ); } static void diff --git a/src/disco/events/schema/event.proto b/src/disco/events/schema/event.proto index e703134a859..183b845ea26 100644 --- a/src/disco/events/schema/event.proto +++ b/src/disco/events/schema/event.proto @@ -22,8 +22,8 @@ message StreamEventsResponse { uint64 nonce = 1; } -message AuthenticateRequest { - bytes identity_pubkey = 1; +message HelloRequest { + reserved 1; string version = 2; bytes genesis_hash = 3; uint64 shred_version = 4; @@ -33,24 +33,14 @@ message AuthenticateRequest { string action = 8; } -message AuthenticateResponse { - bytes challenge = 1; -} - -message ConfirmAuthChallengeRequest { - bytes signed_challenge = 1; -} - -message ConfirmAuthChallengeResponse { +message HelloResponse { + reserved 1; } // An event server for receiving application reported events service EventService { - // Generate an authentication challenge for the client to sign - rpc Authenticate(AuthenticateRequest) returns (AuthenticateResponse) {} - - // Confirm the signed authentication challenge from the client - rpc ConfirmAuthChallenge(ConfirmAuthChallengeRequest) returns (ConfirmAuthChallengeResponse) {} + // Send instance metadata on startup + rpc Hello(HelloRequest) returns (HelloResponse) {} // Stream of events from client to server, and acknowledgements from server to client rpc StreamEvents (stream StreamEventsRequest) returns (stream StreamEventsResponse); diff --git a/src/disco/keyguard/fd_keyguard.h b/src/disco/keyguard/fd_keyguard.h index 1f77a40b3bd..e798e96ba22 100644 --- a/src/disco/keyguard/fd_keyguard.h +++ b/src/disco/keyguard/fd_keyguard.h @@ -32,11 +32,10 @@ FD_PROTOTYPES_BEGIN #define FD_KEYGUARD_PAYLOAD_LG_PRUNE ( 2) /* Gossip PruneData */ #define FD_KEYGUARD_PAYLOAD_LG_SHRED ( 3) /* Solana legacy or merkle shred */ #define FD_KEYGUARD_PAYLOAD_LG_TLS_CV ( 4) /* TLS 1.3 certificate verify payload */ -#define FD_KEYGUARD_PAYLOAD_LG_REPAIR ( 6) /* RepairProtocol */ -#define FD_KEYGUARD_PAYLOAD_LG_PING ( 7) /* Gossip ping protocol */ -#define FD_KEYGUARD_PAYLOAD_LG_BUNDLE ( 8) /* Bundle block producer authentication */ -#define FD_KEYGUARD_PAYLOAD_LG_EVENT ( 9) /* Event reporter authentication */ -#define FD_KEYGUARD_PAYLOAD_LG_PONG (10) /* Gossip/Repair ping/pong protocol */ +#define FD_KEYGUARD_PAYLOAD_LG_REPAIR ( 5) /* RepairProtocol */ +#define FD_KEYGUARD_PAYLOAD_LG_PING ( 6) /* Gossip ping protocol */ +#define FD_KEYGUARD_PAYLOAD_LG_BUNDLE ( 7) /* Bundle block producer authentication */ +#define FD_KEYGUARD_PAYLOAD_LG_PONG ( 8) /* Gossip/Repair ping/pong protocol */ #define FD_KEYGUARD_PAYLOAD_TXN (1UL<public_key, &ctx->public_key_base58_sz, (char *)ctx->concat ); ctx->concat[ ctx->public_key_base58_sz ] = '-'; - - memcpy( ctx->event_auth_concat, "FD_EVENTS_AUTH-", 15UL ); } static void FD_FN_SENSITIVE @@ -244,11 +240,6 @@ after_frag_sensitive( void * _ctx, fd_ed25519_sign( dst, ctx->concat, ctx->public_key_base58_sz+1UL+9UL, ctx->public_key, ctx->private_key, ctx->sha512 ); break; } - case FD_KEYGUARD_SIGN_TYPE_FD_EVENTS_AUTH_CONCAT_ED25519: { - memcpy( ctx->event_auth_concat+15UL, ctx->_data, 32UL ); - fd_ed25519_sign( dst, ctx->event_auth_concat, 15UL+32UL, ctx->public_key, ctx->private_key, ctx->sha512 ); - break; - } default: FD_LOG_EMERG(( "invalid sign type: %d", sign_type )); } diff --git a/src/disco/metrics/generated/fd_metrics_event.c b/src/disco/metrics/generated/fd_metrics_event.c index 9ba74779226..cdbda10814f 100644 --- a/src/disco/metrics/generated/fd_metrics_event.c +++ b/src/disco/metrics/generated/fd_metrics_event.c @@ -11,7 +11,6 @@ const fd_metrics_meta_t FD_METRICS_EVENT[FD_METRICS_EVENT_TOTAL] = { DECLARE_METRIC( EVENT_EVENTS_ACKED, COUNTER ), DECLARE_METRIC( EVENT_BYTES_WRITTEN, COUNTER ), DECLARE_METRIC( EVENT_BYTES_READ, COUNTER ), - DECLARE_METRIC( EVENT_AUTH_FAIL, COUNTER ), DECLARE_METRIC( EVENT_INVALID_MSG, COUNTER ), DECLARE_METRIC( EVENT_CONNECT_ATTEMPTS, COUNTER ), DECLARE_METRIC( EVENT_HANDSHAKE_TIMEOUTS, COUNTER ), diff --git a/src/disco/metrics/generated/fd_metrics_event.h b/src/disco/metrics/generated/fd_metrics_event.h index 3f03c0865cd..67a7403a1ae 100644 --- a/src/disco/metrics/generated/fd_metrics_event.h +++ b/src/disco/metrics/generated/fd_metrics_event.h @@ -16,7 +16,6 @@ enum { FD_METRICS_COUNTER_EVENT_EVENTS_ACKED_OFF, FD_METRICS_COUNTER_EVENT_BYTES_WRITTEN_OFF, FD_METRICS_COUNTER_EVENT_BYTES_READ_OFF, - FD_METRICS_COUNTER_EVENT_AUTH_FAIL_OFF, FD_METRICS_COUNTER_EVENT_INVALID_MSG_OFF, FD_METRICS_COUNTER_EVENT_CONNECT_ATTEMPTS_OFF, FD_METRICS_COUNTER_EVENT_HANDSHAKE_TIMEOUTS_OFF, @@ -67,11 +66,6 @@ enum { #define FD_METRICS_COUNTER_EVENT_BYTES_READ_DESC "The total number of bytes read from the event service" #define FD_METRICS_COUNTER_EVENT_BYTES_READ_CVT (FD_METRICS_CONVERTER_NONE) -#define FD_METRICS_COUNTER_EVENT_AUTH_FAIL_NAME "event_auth_fail" -#define FD_METRICS_COUNTER_EVENT_AUTH_FAIL_TYPE (FD_METRICS_TYPE_COUNTER) -#define FD_METRICS_COUNTER_EVENT_AUTH_FAIL_DESC "The total number of authentication failures with the event service" -#define FD_METRICS_COUNTER_EVENT_AUTH_FAIL_CVT (FD_METRICS_CONVERTER_NONE) - #define FD_METRICS_COUNTER_EVENT_INVALID_MSG_NAME "event_invalid_msg" #define FD_METRICS_COUNTER_EVENT_INVALID_MSG_TYPE (FD_METRICS_TYPE_COUNTER) #define FD_METRICS_COUNTER_EVENT_INVALID_MSG_DESC "The total number of malformed messages received from the event service" @@ -84,10 +78,10 @@ enum { #define FD_METRICS_COUNTER_EVENT_HANDSHAKE_TIMEOUTS_NAME "event_handshake_timeouts" #define FD_METRICS_COUNTER_EVENT_HANDSHAKE_TIMEOUTS_TYPE (FD_METRICS_TYPE_COUNTER) -#define FD_METRICS_COUNTER_EVENT_HANDSHAKE_TIMEOUTS_DESC "The total number of authentication handshake timeouts with the event service" +#define FD_METRICS_COUNTER_EVENT_HANDSHAKE_TIMEOUTS_DESC "The total number of handshake timeouts with the event service" #define FD_METRICS_COUNTER_EVENT_HANDSHAKE_TIMEOUTS_CVT (FD_METRICS_CONVERTER_NONE) -#define FD_METRICS_EVENT_TOTAL (13UL) +#define FD_METRICS_EVENT_TOTAL (12UL) extern const fd_metrics_meta_t FD_METRICS_EVENT[FD_METRICS_EVENT_TOTAL]; #endif /* HEADER_fd_src_disco_metrics_generated_fd_metrics_event_h */ diff --git a/src/disco/metrics/metrics.xml b/src/disco/metrics/metrics.xml index 70791b476e8..0ffd664ce57 100644 --- a/src/disco/metrics/metrics.xml +++ b/src/disco/metrics/metrics.xml @@ -1443,10 +1443,9 @@ metric introduced. - - + From 1a0547f3eef8a803285acbd5a76524244592beaa Mon Sep 17 00:00:00 2001 From: Richard Patel Date: Mon, 1 Jun 2026 02:56:09 +0000 Subject: [PATCH 3/5] events: make OpenSSL mandatory An upcoming change will enforce mutual TLS for event telemetry connections, thus just assume OpenSSL is available in build configuration. --- src/app/firedancer-dev/main.h | 2 + src/app/firedancer/main.c | 2 + src/app/firedancer/topology.c | 4 ++ src/disco/events/Local.mk | 6 +- src/disco/events/fd_event_client.c | 94 +++++++++++------------------- src/disco/events/fd_event_client.h | 4 -- src/disco/events/fd_event_tile.c | 70 +++++++--------------- 7 files changed, 69 insertions(+), 113 deletions(-) diff --git a/src/app/firedancer-dev/main.h b/src/app/firedancer-dev/main.h index a1d6e96af67..e6a9c20a0ef 100644 --- a/src/app/firedancer-dev/main.h +++ b/src/app/firedancer-dev/main.h @@ -122,7 +122,9 @@ fd_topo_run_tile_t * TILES[] = { &fd_tile_shred, &fd_tile_sign, &fd_tile_metric, +# if FD_HAS_OPENSSL &fd_tile_event, +# endif &fd_tile_diag, &fd_tile_gui, &fd_tile_rpc, diff --git a/src/app/firedancer/main.c b/src/app/firedancer/main.c index 877f735eb8e..c7ac76cc882 100644 --- a/src/app/firedancer/main.c +++ b/src/app/firedancer/main.c @@ -107,7 +107,9 @@ fd_topo_run_tile_t * TILES[] = { &fd_tile_shred, &fd_tile_sign, &fd_tile_metric, +# if FD_HAS_OPENSSL &fd_tile_event, +# endif &fd_tile_diag, &fd_tile_gui, &fd_tile_rpc, diff --git a/src/app/firedancer/topology.c b/src/app/firedancer/topology.c index 14eb97e562b..d592e976b15 100644 --- a/src/app/firedancer/topology.c +++ b/src/app/firedancer/topology.c @@ -746,6 +746,7 @@ fd_topo_initialize( config_t * config ) { FOR(shred_tile_cnt) fd_topob_tile_in ( topo, "shred", i, "metric_in", "tower_out", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_POLLED ); FOR(shred_tile_cnt) fd_topob_tile_out( topo, "shred", i, "shred_net", i ); +# if FD_HAS_OPENSSL if( FD_LIKELY( telemetry_enabled ) ) { fd_topob_wksp( topo, "event" ); fd_topob_wksp( topo, "event_sign" ); @@ -772,6 +773,9 @@ fd_topo_initialize( config_t * config ) { fd_topob_tile_in( topo, "event", 0UL, "metric_in", "sign_event", 0UL, FD_TOPOB_UNRELIABLE, FD_TOPOB_UNPOLLED ); fd_topob_tile_out( topo, "sign", 0UL, "sign_event", 0UL ); } +# else /* no OpenSSL */ + if( telemetry_enabled ) FD_LOG_WARNING(( "ignoring [telemetry] = true: this build of Firedancer is missing OpenSSL" )); +# endif /* FD_HAS_OPENSSL */ if( FD_UNLIKELY( config->tiles.bundle.enabled ) ) { fd_topob_wksp( topo, "bundle_verif" ); diff --git a/src/disco/events/Local.mk b/src/disco/events/Local.mk index e09545fb715..ebe3becc334 100644 --- a/src/disco/events/Local.mk +++ b/src/disco/events/Local.mk @@ -3,8 +3,10 @@ $(call add-objs,fd_circq,fd_disco) $(call make-unit-test,test_circq,test_circq,fd_disco fd_flamenco fd_tango fd_util) $(call run-unit-test,test_circq) ifdef FD_HAS_HOSTED +ifdef FD_HAS_OPENSSL $(call add-objs,fd_event_client,fd_disco) ifdef FD_HAS_ALLOCA $(call add-objs,fd_event_tile,fd_disco) -endif -endif +endif # FD_HAS_ALLOCA +endif # FD_HAS_OPENSSL +endif # FD_HAS_HOSTED diff --git a/src/disco/events/fd_event_client.c b/src/disco/events/fd_event_client.c index 77a274be5e3..90ce30e145c 100644 --- a/src/disco/events/fd_event_client.c +++ b/src/disco/events/fd_event_client.c @@ -1,6 +1,10 @@ #define _GNU_SOURCE #include "fd_event_client.h" +#if !FD_HAS_OPENSSL +#error "Building fd_event_client requires FD_HAS_OPENSSL" +#endif + #include "../../waltz/resolv/fd_netdb.h" #include "../../waltz/http/fd_url.h" #include "../../waltz/grpc/fd_grpc_client.h" @@ -10,12 +14,9 @@ #include "../../util/net/fd_ip4.h" #include "../../util/log/fd_log.h" #include "../keyguard/fd_keyguard.h" - -#if FD_HAS_OPENSSL #include "../../waltz/openssl/fd_openssl.h" #include #include -#endif #include #include @@ -77,11 +78,8 @@ struct fd_event_client { int so_sndbuf; int sockfd; - int use_tls; -#if FD_HAS_OPENSSL SSL_CTX * ssl_ctx; SSL * ssl; -#endif /* wallclock deadline for hello handshake, LONG_MAX if not authenticating. */ @@ -127,7 +125,6 @@ fd_event_client_new( void * shmem, ulong boot_id, ulong machine_id, ulong buf_max, - int use_tls, void * ssl_ctx ) { if( FD_UNLIKELY( !shmem ) ) { FD_LOG_WARNING(( "NULL shmem" )); @@ -177,17 +174,8 @@ fd_event_client_new( void * shmem, client->so_sndbuf = so_sndbuf; client->sockfd = -1; - client->use_tls = use_tls; -#if FD_HAS_OPENSSL client->ssl_ctx = (SSL_CTX *)ssl_ctx; client->ssl = NULL; -#else - (void)ssl_ctx; - if( FD_UNLIKELY( use_tls ) ) { - FD_LOG_ERR(( "TLS requested for event service but this build does not include OpenSSL. " - "To install OpenSSL, re-run ./deps.sh and do a clean rebuild." )); - } -#endif client->hello_deadline = LONG_MAX; client->state = FD_EVENT_CLIENT_STATE_DISCONNECTED; client->disconnected.reconnect_deadline = 0L; @@ -276,12 +264,10 @@ disconnect( fd_event_client_t * client, int err, int _backoff ) { client->event_stream = NULL; -#if FD_HAS_OPENSSL if( FD_UNLIKELY( client->ssl ) ) { SSL_free( client->ssl ); client->ssl = NULL; } -#endif if( FD_LIKELY( -1!=client->sockfd ) ) { if( FD_UNLIKELY( -1==close( client->sockfd ) ) ) FD_LOG_ERR(( "close() failed (%d-%s)", errno, fd_io_strerror( errno ) )); client->sockfd = -1; @@ -349,7 +335,7 @@ reconnect( fd_event_client_t * client, *charge_busy = 1; client->metrics.connect_attempt_cnt++; - FD_LOG_INFO(( "connecting to event server %s://%.*s:%u", client->use_tls ? "https" : "http", (int)client->server_fqdn_len, client->server_fqdn, client->server_tcp_port )); + FD_LOG_INFO(( "connecting to event server https://%.*s:%u", (int)client->server_fqdn_len, client->server_fqdn, client->server_tcp_port )); /* FIXME IPv6 support */ fd_addrinfo_t hints = {0}; @@ -389,43 +375,39 @@ reconnect( fd_event_client_t * client, return; } -# if FD_HAS_OPENSSL - if( client->use_tls ) { - BIO * bio = fd_openssl_bio_new_socket( client->sockfd, BIO_NOCLOSE ); - if( FD_UNLIKELY( !bio ) ) { - FD_LOG_WARNING(( "fd_openssl_bio_new_socket failed" )); - disconnect( client, DISCONNECT_REASON_CONNECT_FAILED, 0, 1 ); - return; - } - - SSL * ssl = SSL_new( client->ssl_ctx ); - if( FD_UNLIKELY( !ssl ) ) { - FD_LOG_WARNING(( "SSL_new failed" )); - BIO_free( bio ); - disconnect( client, DISCONNECT_REASON_CONNECT_FAILED, 0, 1 ); - return; - } + BIO * bio = fd_openssl_bio_new_socket( client->sockfd, BIO_NOCLOSE ); + if( FD_UNLIKELY( !bio ) ) { + FD_LOG_WARNING(( "fd_openssl_bio_new_socket failed" )); + disconnect( client, DISCONNECT_REASON_CONNECT_FAILED, 0, 1 ); + return; + } - SSL_set_bio( ssl, bio, bio ); /* moves ownership of bio */ - SSL_set_connect_state( ssl ); + SSL * ssl = SSL_new( client->ssl_ctx ); + if( FD_UNLIKELY( !ssl ) ) { + FD_LOG_WARNING(( "SSL_new failed" )); + BIO_free( bio ); + disconnect( client, DISCONNECT_REASON_CONNECT_FAILED, 0, 1 ); + return; + } - /* SNI and hostname verification */ - if( FD_UNLIKELY( !SSL_set_tlsext_host_name( ssl, client->server_fqdn ) ) ) { - FD_LOG_WARNING(( "SSL_set_tlsext_host_name failed" )); - SSL_free( ssl ); - disconnect( client, DISCONNECT_REASON_CONNECT_FAILED, 0, 1 ); - return; - } - if( FD_UNLIKELY( !SSL_set1_host( ssl, client->server_fqdn ) ) ) { - FD_LOG_WARNING(( "SSL_set1_host failed" )); - SSL_free( ssl ); - disconnect( client, DISCONNECT_REASON_CONNECT_FAILED, 0, 1 ); - return; - } + SSL_set_bio( ssl, bio, bio ); /* moves ownership of bio */ + SSL_set_connect_state( ssl ); - client->ssl = ssl; + /* SNI and hostname verification */ + if( FD_UNLIKELY( !SSL_set_tlsext_host_name( ssl, client->server_fqdn ) ) ) { + FD_LOG_WARNING(( "SSL_set_tlsext_host_name failed" )); + SSL_free( ssl ); + disconnect( client, DISCONNECT_REASON_CONNECT_FAILED, 0, 1 ); + return; } -# endif /* FD_HAS_OPENSSL */ + if( FD_UNLIKELY( !SSL_set1_host( ssl, client->server_fqdn ) ) ) { + FD_LOG_WARNING(( "SSL_set1_host failed" )); + SSL_free( ssl ); + disconnect( client, DISCONNECT_REASON_CONNECT_FAILED, 0, 1 ); + return; + } + + client->ssl = ssl; fd_grpc_client_reset( client->grpc_client ); @@ -686,13 +668,7 @@ fd_event_client_poll( fd_event_client_t * client, return; } if( FD_LIKELY( client->state!=FD_EVENT_CLIENT_STATE_DISCONNECTED ) ) { - int rxtx_err; -# if FD_HAS_OPENSSL - if( client->use_tls ) - rxtx_err = fd_grpc_client_rxtx_ossl( client->grpc_client, client->ssl, charge_busy ); - else -# endif - rxtx_err = fd_grpc_client_rxtx_socket( client->grpc_client, client->sockfd, charge_busy ); + int rxtx_err = fd_grpc_client_rxtx_ossl( client->grpc_client, client->ssl, charge_busy ); if( FD_UNLIKELY( -1==rxtx_err ) ) { disconnect( client, DISCONNECT_REASON_TRANSPORT_FAILED, errno, 1 ); return; diff --git a/src/disco/events/fd_event_client.h b/src/disco/events/fd_event_client.h index 2b8b60a60e3..0c53c7024c7 100644 --- a/src/disco/events/fd_event_client.h +++ b/src/disco/events/fd_event_client.h @@ -4,10 +4,7 @@ #include "fd_circq.h" #include "../keyguard/fd_keyguard_client.h" #include "../../discof/genesis/fd_genesi_tile.h" - -#if FD_HAS_OPENSSL #include -#endif #define FD_EVENT_CLIENT_STATE_DISCONNECTED (0) #define FD_EVENT_CLIENT_STATE_CONNECTING (1) @@ -53,7 +50,6 @@ fd_event_client_new( void * shmem, ulong boot_id, ulong machine_id, ulong buf_max, - int use_tls, void * ssl_ctx ); fd_event_client_t * diff --git a/src/disco/events/fd_event_tile.c b/src/disco/events/fd_event_tile.c index a8d74db006b..e8d777b877f 100644 --- a/src/disco/events/fd_event_tile.c +++ b/src/disco/events/fd_event_tile.c @@ -2,6 +2,10 @@ #include "fd_circq.h" #include "fd_event_client.h" +#if !FD_HAS_OPENSSL +#error "Building fd_event_tile requires FD_HAS_OPENSSL" +#endif + #include "../fd_txn_m.h" #include "../metrics/fd_metrics.h" #include "../net/fd_net_tile.h" @@ -15,13 +19,10 @@ #include "../../ballet/lthash/fd_lthash.h" #include "../../ballet/pb/fd_pb_encode.h" #include "../../tango/tempo/fd_tempo.h" - -#if FD_HAS_OPENSSL #include "../../util/alloc/fd_alloc.h" #include "../../waltz/openssl/fd_openssl.h" #include "../../waltz/openssl/fd_openssl_tile.h" #include -#endif #include #include @@ -81,10 +82,7 @@ struct fd_event_tile { uchar identity_pubkey[ 32UL ]; - int use_tls; -#if FD_HAS_OPENSSL SSL_CTX * ssl_ctx; -#endif fd_keyguard_client_t keyguard_client[1]; fd_rng_t rng[1]; @@ -107,9 +105,7 @@ scratch_align( void ) { ulong a = alignof( fd_event_tile_t ); a = fd_ulong_max( a, fd_event_client_align() ); a = fd_ulong_max( a, fd_circq_align() ); -# if FD_HAS_OPENSSL a = fd_ulong_max( a, fd_alloc_align() ); -# endif return a; } @@ -121,20 +117,16 @@ scratch_footprint( fd_topo_tile_t const * tile ) { l = FD_LAYOUT_APPEND( l, alignof(fd_event_tile_t), sizeof(fd_event_tile_t) ); l = FD_LAYOUT_APPEND( l, fd_event_client_align(), fd_event_client_footprint( GRPC_BUF_MAX ) ); l = FD_LAYOUT_APPEND( l, fd_circq_align(), fd_circq_footprint( 1UL<<30UL ) ); /* 1GiB circq for events */ -# if FD_HAS_OPENSSL - l = FD_LAYOUT_APPEND( l, fd_alloc_align(), fd_alloc_footprint() ); -# endif + l = FD_LAYOUT_APPEND( l, fd_alloc_align(), fd_alloc_footprint() ); return FD_LAYOUT_FINI( l, scratch_align() ); } -# if FD_HAS_OPENSSL FD_FN_CONST static inline ulong loose_footprint( fd_topo_tile_t const * tile ) { (void)tile; /* Extra workspace memory for OpenSSL dynamic allocations */ return 1UL<<26UL; /* 64 MiB */ } -# endif static inline void metrics_write( fd_event_tile_t * ctx ) { @@ -338,10 +330,7 @@ privileged_init( fd_topo_t * topo, fd_event_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_event_tile_t), sizeof(fd_event_tile_t) ); FD_SCRATCH_ALLOC_APPEND( l, fd_event_client_align(), fd_event_client_footprint( GRPC_BUF_MAX ) ); FD_SCRATCH_ALLOC_APPEND( l, fd_circq_align(), fd_circq_footprint( 1UL<<30UL ) ); -# if FD_HAS_OPENSSL void * alloc_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_alloc_align(), fd_alloc_footprint() ); - (void)alloc_mem; -# endif ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, scratch_align() ); if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) ) @@ -383,37 +372,30 @@ privileged_init( fd_topo_t * topo, if( FD_UNLIKELY( fd_url_parse_endpoint( url, tile->event.url, strlen( tile->event.url ), &port, &is_ssl, "[tiles.event.url]" ) ) ) { FD_LOG_ERR(( "Could not parse [tiles.event.url]" )); } - ctx->use_tls = is_ssl; + if( FD_UNLIKELY( !is_ssl ) ) { + FD_LOG_ERR(( "[tiles.event.url] must start with \"https://\"" )); + } -# if FD_HAS_OPENSSL ctx->ssl_ctx = NULL; - if( ctx->use_tls ) { - fd_alloc_t * alloc = fd_alloc_join( fd_alloc_new( alloc_mem, 1UL ), tile->kind_id ); - if( FD_UNLIKELY( !alloc ) ) FD_LOG_ERR(( "fd_alloc_new failed" )); - fd_ossl_tile_init( alloc ); + fd_alloc_t * alloc = fd_alloc_join( fd_alloc_new( alloc_mem, 1UL ), tile->kind_id ); + if( FD_UNLIKELY( !alloc ) ) FD_LOG_ERR(( "fd_alloc_new failed" )); + fd_ossl_tile_init( alloc ); - SSL_CTX * ssl_ctx = SSL_CTX_new( TLS_client_method() ); - if( FD_UNLIKELY( !ssl_ctx ) ) FD_LOG_ERR(( "SSL_CTX_new failed" )); + SSL_CTX * ssl_ctx = SSL_CTX_new( TLS_client_method() ); + if( FD_UNLIKELY( !ssl_ctx ) ) FD_LOG_ERR(( "SSL_CTX_new failed" )); - if( FD_UNLIKELY( !SSL_CTX_set_mode( ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_AUTO_RETRY ) ) ) - FD_LOG_ERR(( "SSL_CTX_set_mode failed" )); + if( FD_UNLIKELY( !SSL_CTX_set_mode( ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_AUTO_RETRY ) ) ) + FD_LOG_ERR(( "SSL_CTX_set_mode failed" )); - if( FD_UNLIKELY( !SSL_CTX_set_min_proto_version( ssl_ctx, TLS1_3_VERSION ) ) ) - FD_LOG_ERR(( "SSL_CTX_set_min_proto_version(ssl_ctx,TLS1_3_VERSION) failed" )); + if( FD_UNLIKELY( !SSL_CTX_set_min_proto_version( ssl_ctx, TLS1_3_VERSION ) ) ) + FD_LOG_ERR(( "SSL_CTX_set_min_proto_version(ssl_ctx,TLS1_3_VERSION) failed" )); - if( FD_UNLIKELY( 0!=SSL_CTX_set_alpn_protos( ssl_ctx, (uchar const *)"\x02h2", 3 ) ) ) - FD_LOG_ERR(( "SSL_CTX_set_alpn_protos failed" )); + if( FD_UNLIKELY( 0!=SSL_CTX_set_alpn_protos( ssl_ctx, (uchar const *)"\x02h2", 3 ) ) ) + FD_LOG_ERR(( "SSL_CTX_set_alpn_protos failed" )); - fd_ossl_load_certs( ssl_ctx ); /* also sets SSL_VERIFY_PEER */ + fd_ossl_load_certs( ssl_ctx ); /* also sets SSL_VERIFY_PEER */ - ctx->ssl_ctx = ssl_ctx; - } -# else - if( FD_UNLIKELY( ctx->use_tls ) ) { - FD_LOG_ERR(( "TLS requested for event service (https:// URL) but this build " - "does not include OpenSSL. Re-run ./deps.sh and do a clean rebuild." )); - } -# endif + ctx->ssl_ctx = ssl_ctx; } static void @@ -425,9 +407,7 @@ unprivileged_init( fd_topo_t * topo, fd_event_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_event_tile_t), sizeof(fd_event_tile_t) ); void * _event_client = FD_SCRATCH_ALLOC_APPEND( l, fd_event_client_align(), fd_event_client_footprint( GRPC_BUF_MAX ) ); void * _circq = FD_SCRATCH_ALLOC_APPEND( l, fd_circq_align(), fd_circq_footprint( 1UL<<30UL ) ); -# if FD_HAS_OPENSSL FD_SCRATCH_ALLOC_APPEND( l, fd_alloc_align(), fd_alloc_footprint() ); -# endif ulong sign_in_idx = fd_topo_find_tile_in_link ( topo, tile, "sign_event", tile->kind_id ); ulong sign_out_idx = fd_topo_find_tile_out_link( topo, tile, "event_sign", tile->kind_id ); @@ -451,10 +431,7 @@ unprivileged_init( fd_topo_t * topo, ctx->circq = fd_circq_join( fd_circq_new( _circq, 1UL<<30UL /* 1GiB */ ) ); FD_TEST( ctx->circq ); - void * ssl_ctx_ptr = NULL; -# if FD_HAS_OPENSSL - ssl_ctx_ptr = ctx->ssl_ctx; -# endif + void * ssl_ctx_ptr = ctx->ssl_ctx; /* Rewrite the URL to hardcode port 7878 regardless of the port in the configured URL. */ @@ -483,7 +460,6 @@ unprivileged_init( fd_topo_t * topo, ctx->boot_id, ctx->machine_id, GRPC_BUF_MAX, - ctx->use_tls, ssl_ctx_ptr ) ); FD_TEST( ctx->client ); @@ -595,9 +571,7 @@ fd_topo_run_tile_t fd_tile_event = { .populate_allowed_fds = populate_allowed_fds, .scratch_align = scratch_align, .scratch_footprint = scratch_footprint, -# if FD_HAS_OPENSSL .loose_footprint = loose_footprint, -# endif .privileged_init = privileged_init, .unprivileged_init = unprivileged_init, .run = stem_run, From f5d5c8d5e23656e9bd457626b0105dc225ef2014 Mon Sep 17 00:00:00 2001 From: Richard Patel Date: Mon, 1 Jun 2026 04:21:17 +0000 Subject: [PATCH 4/5] events: mutual TLS authentication Allow clients to authenticate via RFC 7250 Raw Public Keys --- contrib/event-test-server/Cargo.toml | 6 +- contrib/event-test-server/src/main.rs | 184 ++++++++- src/app/firedancer/config/default.toml | 2 +- src/app/firedancer/topology.c | 2 +- src/disco/events/Local.mk | 2 +- src/disco/events/fd_event_auth.c | 424 +++++++++++++++++++++ src/disco/events/fd_event_auth.h | 31 ++ src/disco/events/fd_event_client.c | 10 +- src/disco/keyguard/fd_keyguard_authorize.c | 10 +- src/disco/keyguard/fd_sign_tile.c | 2 +- 10 files changed, 659 insertions(+), 14 deletions(-) create mode 100644 src/disco/events/fd_event_auth.c create mode 100644 src/disco/events/fd_event_auth.h diff --git a/contrib/event-test-server/Cargo.toml b/contrib/event-test-server/Cargo.toml index e3cc9cb81e6..1a34069b701 100644 --- a/contrib/event-test-server/Cargo.toml +++ b/contrib/event-test-server/Cargo.toml @@ -4,10 +4,12 @@ version = "0.1.0" edition = "2021" [dependencies] -tonic = "0.14" +async-stream = "0.3" +tonic = { version = "0.14", features = ["tls-ring"] } prost = "0.14" tonic-prost = "0.14" -tokio = { version = "1.0", features = ["macros"] } +tokio = { version = "1.0", features = ["macros", "net", "rt", "sync"] } +tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] } tokio-stream = "0.1" hex = "0.4" diff --git a/contrib/event-test-server/src/main.rs b/contrib/event-test-server/src/main.rs index 680864fd8d5..213bd51fb74 100644 --- a/contrib/event-test-server/src/main.rs +++ b/contrib/event-test-server/src/main.rs @@ -1,6 +1,24 @@ +use std::sync::Arc; + +use async_stream::try_stream; +use tokio::net::TcpListener; use tokio::sync::mpsc; +use tokio_rustls::rustls::client::danger::HandshakeSignatureValid; +use tokio_rustls::rustls::crypto::ring; +use tokio_rustls::rustls::pki_types::{ + CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer, SubjectPublicKeyInfoDer, UnixTime, +}; +use tokio_rustls::rustls::server::danger::{ClientCertVerified, ClientCertVerifier}; +use tokio_rustls::rustls::{ + version, DigitallySignedStruct, Error as TlsError, ServerConfig, SignatureScheme, +}; +use tokio_rustls::TlsAcceptor; use tokio_stream::wrappers::ReceiverStream; -use tonic::{transport::Server, Request, Response, Status}; +use tonic::{ + transport::server::{TcpConnectInfo, TlsConnectInfo}, + transport::Server, + Request, Response, Status, +}; pub mod events { tonic::include_proto!("events.v1"); @@ -10,6 +28,44 @@ use events::event::Event; use events::event_service_server::{EventService, EventServiceServer}; use events::{HelloRequest, HelloResponse, StreamEventsRequest, StreamEventsResponse}; +const ALPN_H2: &[u8] = b"h2"; +const ED25519_SPKI_PREFIX: &[u8] = &[ + 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00, +]; + +const SERVER_CERT_DER: &[u8] = &[ + 0x30, 0x82, 0x01, 0x4f, 0x30, 0x82, 0x01, 0x01, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x14, 0x72, + 0xe9, 0x82, 0x60, 0xef, 0x3b, 0xa1, 0x19, 0xd3, 0x9c, 0x24, 0x06, 0x7e, 0x63, 0xf0, 0x8f, 0x45, + 0x6b, 0x1f, 0x48, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x11, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x65, 0x73, + 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x36, 0x30, 0x36, + 0x30, 0x31, 0x30, 0x31, 0x30, 0x34, 0x34, 0x31, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x32, 0x36, 0x30, + 0x35, 0x30, 0x38, 0x30, 0x31, 0x30, 0x34, 0x34, 0x31, 0x5a, 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x11, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x65, 0x73, + 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, + 0x70, 0x03, 0x21, 0x00, 0x23, 0x8b, 0xc4, 0xee, 0x4d, 0xa9, 0x3f, 0x52, 0x43, 0x3e, 0xe3, 0x23, + 0xb7, 0x0d, 0xfe, 0xb7, 0xa3, 0x0c, 0x21, 0xd3, 0xb3, 0x23, 0x05, 0x6c, 0x6e, 0xa7, 0xd3, 0x17, + 0xbf, 0xfc, 0xe1, 0xa1, 0xa3, 0x53, 0x30, 0x51, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, + 0x16, 0x04, 0x14, 0x79, 0x03, 0xc0, 0xd9, 0x41, 0x63, 0x05, 0xbe, 0xca, 0x8c, 0xeb, 0x6b, 0x2b, + 0x69, 0xb1, 0xd9, 0xc0, 0x22, 0x59, 0x2a, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, + 0x30, 0x16, 0x80, 0x14, 0x79, 0x03, 0xc0, 0xd9, 0x41, 0x63, 0x05, 0xbe, 0xca, 0x8c, 0xeb, 0x6b, + 0x2b, 0x69, 0xb1, 0xd9, 0xc0, 0x22, 0x59, 0x2a, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, + 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, + 0x03, 0x41, 0x00, 0x9d, 0xbe, 0x72, 0xd7, 0xcb, 0x17, 0xb6, 0x61, 0x3d, 0x9f, 0x68, 0xc3, 0x07, + 0xb1, 0x6a, 0x3a, 0x69, 0xd7, 0xc8, 0xc3, 0xcd, 0x20, 0xc4, 0x43, 0x8b, 0x87, 0xa2, 0xb3, 0x44, + 0x83, 0xca, 0x7f, 0xb7, 0xbf, 0x11, 0x11, 0x11, 0x26, 0xd1, 0x44, 0xf0, 0x74, 0x99, 0xe2, 0xcf, + 0x1d, 0x19, 0xb7, 0xf0, 0xc9, 0x6d, 0xb5, 0x0d, 0x58, 0x12, 0x54, 0x10, 0x87, 0xcd, 0x65, 0x3b, + 0xa1, 0x68, 0x01, +]; + +// This key is plaintext intentionally -- this file is only used for +// testing. +const SERVER_KEY_DER: &[u8] = &[ + 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20, + 0x22, 0xe8, 0xff, 0xec, 0xd8, 0x12, 0x17, 0xc1, 0x60, 0xe2, 0xf6, 0xe7, 0xf4, 0x87, 0x6d, 0xdb, + 0x7d, 0xf4, 0x9b, 0x55, 0x83, 0x5f, 0x37, 0xb3, 0xe4, 0x50, 0x33, 0x8b, 0x6e, 0x33, 0xaf, 0xb9, +]; + fn event_kind_name(event: &Option) -> &'static str { match event.as_ref().and_then(|e| e.event.as_ref()) { Some(Event::Txn(_)) => "Txn", @@ -18,6 +74,119 @@ fn event_kind_name(event: &Option) -> &'static str { } } +fn client_public_key(request: &Request>) -> Option<[u8; 32]> { + let connect_info = request + .extensions() + .get::>()?; + let certs = connect_info.peer_certs()?; + let spki = certs.first()?.as_ref(); + spki.strip_prefix(ED25519_SPKI_PREFIX)?.try_into().ok() +} + +#[derive(Debug)] +struct AnyRpkVerifier { + supported_algs: tokio_rustls::rustls::crypto::WebPkiSupportedAlgorithms, +} + +impl AnyRpkVerifier { + fn new() -> Self { + Self { + supported_algs: ring::default_provider().signature_verification_algorithms, + } + } + + fn is_ed25519_subject_public_key_info(spki: &[u8]) -> bool { + spki.len() == ED25519_SPKI_PREFIX.len() + 32 && spki.starts_with(ED25519_SPKI_PREFIX) + } +} + +impl ClientCertVerifier for AnyRpkVerifier { + fn root_hint_subjects(&self) -> &[tokio_rustls::rustls::DistinguishedName] { + &[] + } + + fn verify_client_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + _now: UnixTime, + ) -> Result { + if !intermediates.is_empty() { + return Err(TlsError::General( + "client raw public key must not include intermediates".into(), + )); + } + if !Self::is_ed25519_subject_public_key_info(end_entity.as_ref()) { + return Err(TlsError::General( + "client raw public key must be Ed25519 SubjectPublicKeyInfo".into(), + )); + } + Ok(ClientCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result { + Err(TlsError::General( + "raw public key client authentication requires TLS 1.3".into(), + )) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + let spki = SubjectPublicKeyInfoDer::from(cert.as_ref()); + tokio_rustls::rustls::crypto::verify_tls13_signature_with_raw_key( + message, + &spki, + dss, + &self.supported_algs, + ) + } + + fn supported_verify_schemes(&self) -> Vec { + vec![SignatureScheme::ED25519] + } + + fn requires_raw_public_keys(&self) -> bool { + true + } +} + +fn tls_config() -> Result> { + let cert = CertificateDer::from(SERVER_CERT_DER.to_vec()); + let key = PrivateKeyDer::from(PrivatePkcs8KeyDer::from(SERVER_KEY_DER.to_vec())); + let verifier = Arc::new(AnyRpkVerifier::new()); + let mut config = ServerConfig::builder_with_protocol_versions(&[&version::TLS13]) + .with_client_cert_verifier(verifier) + .with_single_cert(vec![cert], key)?; + config.alpn_protocols.push(ALPN_H2.to_vec()); + Ok(config) +} + +fn tls_incoming( + listener: TcpListener, + acceptor: TlsAcceptor, +) -> impl tokio_stream::Stream< + Item = Result, std::io::Error>, +> { + try_stream! { + loop { + let (stream, peer_addr) = listener.accept().await?; + match acceptor.accept(stream).await { + Ok(tls_stream) => yield tls_stream, + Err(err) => eprintln!("TLS handshake failed from {peer_addr}: {err}"), + } + } + } +} + #[derive(Debug, Default)] pub struct MyEventService; @@ -37,7 +206,10 @@ impl EventService for MyEventService { &self, request: Request>, ) -> Result, Status> { - println!("Client connected"); + match client_public_key(&request) { + Some(public_key) => println!("Client connected: public_key={}", hex::encode(public_key)), + None => println!("Client connected: public_key="), + } let mut stream = request.into_inner(); let (tx, rx) = mpsc::channel(128); @@ -78,12 +250,14 @@ impl EventService for MyEventService { #[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), Box> { - let addr = "127.0.0.1:7878".parse()?; - println!("Listening on {}", addr); + let addr = "127.0.0.1:7878"; + let listener = TcpListener::bind(addr).await?; + let acceptor = TlsAcceptor::from(Arc::new(tls_config()?)); + println!("Listening on https://{}", addr); Server::builder() .add_service(EventServiceServer::new(MyEventService)) - .serve(addr) + .serve_with_incoming(tls_incoming(listener, acceptor)) .await?; Ok(()) diff --git a/src/app/firedancer/config/default.toml b/src/app/firedancer/config/default.toml index e73a8f1e9a7..48ad3b4f177 100644 --- a/src/app/firedancer/config/default.toml +++ b/src/app/firedancer/config/default.toml @@ -1463,7 +1463,7 @@ telemetry = true # If no URL is provided, event reporting is disabled, although # it is suggested to leave the default URL in place and set # `telemetry` to false to disable event reporting. - url = "http://events-in.firedancer.io" + url = "https://events-in.firedancer.io" # The gui tile receives data from the validator and serves an HTTP # endpoint to clients to view it. diff --git a/src/app/firedancer/topology.c b/src/app/firedancer/topology.c index d592e976b15..375ec782c30 100644 --- a/src/app/firedancer/topology.c +++ b/src/app/firedancer/topology.c @@ -751,7 +751,7 @@ fd_topo_initialize( config_t * config ) { fd_topob_wksp( topo, "event" ); fd_topob_wksp( topo, "event_sign" ); fd_topob_wksp( topo, "sign_event" ); - fd_topob_link( topo, "event_sign", "event_sign", 128UL, 32UL, 1UL ); + fd_topob_link( topo, "event_sign", "event_sign", 128UL, 162UL, 1UL ); fd_topob_link( topo, "sign_event", "sign_event", 128UL, 64UL, 1UL ); fd_topob_tile( topo, "event", "event", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 1, 0 ); fd_topob_tile_in( topo, "event", 0UL, "metric_in", "genesi_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); diff --git a/src/disco/events/Local.mk b/src/disco/events/Local.mk index ebe3becc334..8b19f9d9e45 100644 --- a/src/disco/events/Local.mk +++ b/src/disco/events/Local.mk @@ -4,7 +4,7 @@ $(call make-unit-test,test_circq,test_circq,fd_disco fd_flamenco fd_tango fd_uti $(call run-unit-test,test_circq) ifdef FD_HAS_HOSTED ifdef FD_HAS_OPENSSL -$(call add-objs,fd_event_client,fd_disco) +$(call add-objs,fd_event_auth fd_event_client,fd_disco) ifdef FD_HAS_ALLOCA $(call add-objs,fd_event_tile,fd_disco) endif # FD_HAS_ALLOCA diff --git a/src/disco/events/fd_event_auth.c b/src/disco/events/fd_event_auth.c new file mode 100644 index 00000000000..520539bdff7 --- /dev/null +++ b/src/disco/events/fd_event_auth.c @@ -0,0 +1,424 @@ +#define _GNU_SOURCE +#include "fd_event_auth.h" + +#if !FD_HAS_OPENSSL +#error "Building fd_event_auth requires FD_HAS_OPENSSL" +#endif + +#include "../keyguard/fd_keyguard.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FD_EVENT_RPK_PROVIDER_NAME "fd_event_rpk" +#define FD_EVENT_RPK_KEYGUARD_PARAM "fd-keyguard" + +typedef struct fd_event_rpk_key { + uchar pubkey[ 32UL ]; + fd_keyguard_client_t * keyguard_client; + int has_public; + int has_private; +} fd_event_rpk_key_t; + +typedef struct fd_event_rpk_sig_ctx { + fd_event_rpk_key_t * key; + uchar msg[ FD_KEYGUARD_SIGN_REQ_MTU ]; + ulong msg_sz; +} fd_event_rpk_sig_ctx_t; + +static void * +fd_event_rpk_keymgmt_new( void * provctx FD_PARAM_UNUSED ) { + return OPENSSL_zalloc( sizeof(fd_event_rpk_key_t) ); +} + +static void +fd_event_rpk_keymgmt_free( void * keydata ) { + OPENSSL_free( keydata ); +} + +static void * +fd_event_rpk_keymgmt_dup( void const * keydata_from, + int selection FD_PARAM_UNUSED ) { + fd_event_rpk_key_t const * from = keydata_from; + fd_event_rpk_key_t * to = OPENSSL_malloc( sizeof(fd_event_rpk_key_t) ); + if( FD_UNLIKELY( !to ) ) return NULL; + *to = *from; + return to; +} + +static int +fd_event_rpk_keymgmt_import( void * keydata, + int selection, + OSSL_PARAM const * params ) { + fd_event_rpk_key_t * key = keydata; + + if( selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY ) { + OSSL_PARAM const * pub = OSSL_PARAM_locate_const( params, OSSL_PKEY_PARAM_PUB_KEY ); + if( FD_UNLIKELY( !pub || pub->data_size!=32UL ) ) return 0; + fd_memcpy( key->pubkey, pub->data, 32UL ); + key->has_public = 1; + } + + if( selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ) { + OSSL_PARAM const * kg = OSSL_PARAM_locate_const( params, FD_EVENT_RPK_KEYGUARD_PARAM ); + ulong keyguard_ptr = 0UL; + if( FD_UNLIKELY( + !kg || + !OSSL_PARAM_get_ulong( kg, &keyguard_ptr ) || + !keyguard_ptr ) ) { + return 0; + } + key->keyguard_client = (fd_keyguard_client_t *)(uintptr_t)keyguard_ptr; + key->has_private = 1; + } + + return 1; +} + +static OSSL_PARAM const * +fd_event_rpk_keymgmt_import_types( int selection FD_PARAM_UNUSED ) { + static OSSL_PARAM const import_types[] = { + OSSL_PARAM_octet_string( OSSL_PKEY_PARAM_PUB_KEY, NULL, 0 ), + OSSL_PARAM_ulong ( FD_EVENT_RPK_KEYGUARD_PARAM, NULL ), + OSSL_PARAM_END + }; + return import_types; +} + +static OSSL_PARAM const * +fd_event_rpk_keymgmt_import_types_ex( void * provctx FD_PARAM_UNUSED, + int selection FD_PARAM_UNUSED ) { + return fd_event_rpk_keymgmt_import_types( selection ); +} + +static int +fd_event_rpk_keymgmt_export( void const * keydata, + int selection, + OSSL_CALLBACK * param_cb, + void * cbarg ) { + fd_event_rpk_key_t const * key = keydata; + if( FD_UNLIKELY( selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ) ) return 0; + if( FD_UNLIKELY( (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) && !key->has_public ) ) return 0; + + OSSL_PARAM params[] = { + OSSL_PARAM_construct_octet_string( OSSL_PKEY_PARAM_PUB_KEY, (void *)key->pubkey, 32UL ), + OSSL_PARAM_END + }; + return param_cb( params, cbarg ); +} + +static OSSL_PARAM const * +fd_event_rpk_keymgmt_export_types( int selection FD_PARAM_UNUSED ) { + static OSSL_PARAM const export_types[] = { + OSSL_PARAM_octet_string( OSSL_PKEY_PARAM_PUB_KEY, NULL, 0 ), + OSSL_PARAM_END + }; + return export_types; +} + +static OSSL_PARAM const * +fd_event_rpk_keymgmt_export_types_ex( void * provctx FD_PARAM_UNUSED, + int selection FD_PARAM_UNUSED ) { + return fd_event_rpk_keymgmt_export_types( selection ); +} + +static int +fd_event_rpk_keymgmt_get_params( void * keydata, + OSSL_PARAM params[] ) { + fd_event_rpk_key_t * key = keydata; + + OSSL_PARAM * p = OSSL_PARAM_locate( params, OSSL_PKEY_PARAM_BITS ); + if( p && FD_UNLIKELY( !OSSL_PARAM_set_int( p, 256 ) ) ) return 0; + p = OSSL_PARAM_locate( params, OSSL_PKEY_PARAM_SECURITY_BITS ); + if( p && FD_UNLIKELY( !OSSL_PARAM_set_int( p, 128 ) ) ) return 0; + p = OSSL_PARAM_locate( params, OSSL_PKEY_PARAM_MAX_SIZE ); + if( p && FD_UNLIKELY( !OSSL_PARAM_set_int( p, 64 ) ) ) return 0; + p = OSSL_PARAM_locate( params, OSSL_PKEY_PARAM_MANDATORY_DIGEST ); + if( p && FD_UNLIKELY( !OSSL_PARAM_set_utf8_string( p, "" ) ) ) return 0; + p = OSSL_PARAM_locate( params, OSSL_PKEY_PARAM_PUB_KEY ); + if( p && FD_UNLIKELY( !key->has_public || !OSSL_PARAM_set_octet_string( p, key->pubkey, 32UL ) ) ) return 0; + return 1; +} + +static OSSL_PARAM const * +fd_event_rpk_keymgmt_gettable_params( void * provctx FD_PARAM_UNUSED ) { + static OSSL_PARAM const gettable[] = { + OSSL_PARAM_int ( OSSL_PKEY_PARAM_BITS, NULL ), + OSSL_PARAM_int ( OSSL_PKEY_PARAM_SECURITY_BITS, NULL ), + OSSL_PARAM_int ( OSSL_PKEY_PARAM_MAX_SIZE, NULL ), + OSSL_PARAM_utf8_string ( OSSL_PKEY_PARAM_MANDATORY_DIGEST, NULL, 0 ), + OSSL_PARAM_octet_string( OSSL_PKEY_PARAM_PUB_KEY, NULL, 0 ), + OSSL_PARAM_END + }; + return gettable; +} + +static int +fd_event_rpk_keymgmt_has( void const * keydata, + int selection ) { + fd_event_rpk_key_t const * key = keydata; + if( (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY ) && !key->has_public ) return 0; + if( (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) && !key->has_private ) return 0; + return 1; +} + +static int +fd_event_rpk_keymgmt_validate( void const * keydata, + int selection, + int checktype FD_PARAM_UNUSED ) { + return fd_event_rpk_keymgmt_has( keydata, selection ); +} + +static int +fd_event_rpk_keymgmt_match( void const * keydata1, + void const * keydata2, + int selection ) { + fd_event_rpk_key_t const * key1 = keydata1; + fd_event_rpk_key_t const * key2 = keydata2; + if( selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY ) { + return key1->has_public && key2->has_public && !memcmp( key1->pubkey, key2->pubkey, 32UL ); + } + return 1; +} + +static char const * +fd_event_rpk_keymgmt_query_operation_name( int operation_id ) { + return operation_id==OSSL_OP_SIGNATURE ? "ED25519" : NULL; +} + +static void * +fd_event_rpk_signature_newctx( void * provctx FD_PARAM_UNUSED, + char const * propq FD_PARAM_UNUSED ) { + return OPENSSL_zalloc( sizeof(fd_event_rpk_sig_ctx_t) ); +} + +static void +fd_event_rpk_signature_freectx( void * ctx ) { + OPENSSL_free( ctx ); +} + +static int +fd_event_rpk_signature_digest_sign_init( void * ctx, + char const * mdname, + void * provkey, + OSSL_PARAM const * params FD_PARAM_UNUSED ) { + fd_event_rpk_sig_ctx_t * sig_ctx = ctx; + if( FD_UNLIKELY( mdname && mdname[0] ) ) return 0; + sig_ctx->key = provkey; + sig_ctx->msg_sz = 0UL; + return !!sig_ctx->key; +} + +static int +fd_event_rpk_signature_sign_init( void * ctx, + void * provkey, + OSSL_PARAM const * params ) { + return fd_event_rpk_signature_digest_sign_init( ctx, NULL, provkey, params ); +} + +static int +fd_event_rpk_signature_sign( void * ctx, + uchar * sig, + size_t * siglen, + size_t sigsize, + uchar const * tbs, + size_t tbslen ) { + fd_event_rpk_sig_ctx_t * sig_ctx = ctx; + if( FD_UNLIKELY( !sig_ctx->key || !sig_ctx->key->keyguard_client ) ) return 0; + if( !sig ) { + *siglen = 64UL; + return 1; + } + if( FD_UNLIKELY( sigsize<64UL || tbslen>FD_KEYGUARD_SIGN_REQ_MTU ) ) return 0; + + fd_keyguard_client_sign( sig_ctx->key->keyguard_client, + sig, + tbs, + (ulong)tbslen, + FD_KEYGUARD_SIGN_TYPE_ED25519 ); + *siglen = 64UL; + return 1; +} + +static int +fd_event_rpk_signature_digest_sign( void * ctx, + uchar * sigret, + size_t * siglen, + size_t sigsize, + uchar const * tbs, + size_t tbslen ) { + return fd_event_rpk_signature_sign( ctx, sigret, siglen, sigsize, tbs, tbslen ); +} + +static int +fd_event_rpk_signature_digest_sign_update( void * ctx, + uchar const * data, + size_t datalen ) { + fd_event_rpk_sig_ctx_t * sig_ctx = ctx; + if( FD_UNLIKELY( datalen>FD_KEYGUARD_SIGN_REQ_MTU-sig_ctx->msg_sz ) ) return 0; + fd_memcpy( sig_ctx->msg+sig_ctx->msg_sz, data, datalen ); + sig_ctx->msg_sz += datalen; + return 1; +} + +static int +fd_event_rpk_signature_digest_sign_final( void * ctx, + uchar * sig, + size_t * siglen, + size_t sigsize ) { + fd_event_rpk_sig_ctx_t * sig_ctx = ctx; + return fd_event_rpk_signature_sign( ctx, sig, siglen, sigsize, sig_ctx->msg, sig_ctx->msg_sz ); +} + +static int +fd_event_rpk_signature_get_ctx_params( void * ctx FD_PARAM_UNUSED, + OSSL_PARAM params[] ) { + OSSL_PARAM * p = OSSL_PARAM_locate( params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID ); + if( p ) { + static uchar const ed25519_alg_id[] = { 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70 }; + if( FD_UNLIKELY( !OSSL_PARAM_set_octet_string( p, ed25519_alg_id, sizeof(ed25519_alg_id) ) ) ) return 0; + } + return 1; +} + +static OSSL_PARAM const * +fd_event_rpk_signature_gettable_ctx_params( void * ctx FD_PARAM_UNUSED, + void * provctx FD_PARAM_UNUSED ) { + static OSSL_PARAM const gettable[] = { + OSSL_PARAM_octet_string( OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0 ), + OSSL_PARAM_END + }; + return gettable; +} + +static OSSL_DISPATCH const fd_event_rpk_keymgmt_fns[] = { + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))fd_event_rpk_keymgmt_new }, + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))fd_event_rpk_keymgmt_free }, + { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))fd_event_rpk_keymgmt_dup }, + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))fd_event_rpk_keymgmt_import }, + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))fd_event_rpk_keymgmt_import_types }, + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES_EX, (void (*)(void))fd_event_rpk_keymgmt_import_types_ex }, + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))fd_event_rpk_keymgmt_export }, + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))fd_event_rpk_keymgmt_export_types }, + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX, (void (*)(void))fd_event_rpk_keymgmt_export_types_ex }, + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*)(void))fd_event_rpk_keymgmt_get_params }, + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*)(void))fd_event_rpk_keymgmt_gettable_params }, + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))fd_event_rpk_keymgmt_has }, + { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))fd_event_rpk_keymgmt_validate }, + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))fd_event_rpk_keymgmt_match }, + { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME, (void (*)(void))fd_event_rpk_keymgmt_query_operation_name }, + OSSL_DISPATCH_END +}; + +static OSSL_DISPATCH const fd_event_rpk_signature_fns[] = { + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))fd_event_rpk_signature_newctx }, + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))fd_event_rpk_signature_freectx }, + { OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))fd_event_rpk_signature_sign_init }, + { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))fd_event_rpk_signature_sign }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, (void (*)(void))fd_event_rpk_signature_digest_sign_init }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN, (void (*)(void))fd_event_rpk_signature_digest_sign }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE, (void (*)(void))fd_event_rpk_signature_digest_sign_update }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL, (void (*)(void))fd_event_rpk_signature_digest_sign_final }, + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (void (*)(void))fd_event_rpk_signature_get_ctx_params }, + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, (void (*)(void))fd_event_rpk_signature_gettable_ctx_params }, + OSSL_DISPATCH_END +}; + +static OSSL_ALGORITHM const fd_event_rpk_keymgmt_algs[] = { + { "ED25519:1.3.101.112", "provider=" FD_EVENT_RPK_PROVIDER_NAME, fd_event_rpk_keymgmt_fns, "Firedancer event Ed25519 keyguard keymgmt" }, + { NULL, NULL, NULL, NULL } +}; + +static OSSL_ALGORITHM const fd_event_rpk_signature_algs[] = { + { "ED25519:1.3.101.112", "provider=" FD_EVENT_RPK_PROVIDER_NAME, fd_event_rpk_signature_fns, "Firedancer event Ed25519 keyguard signature" }, + { NULL, NULL, NULL, NULL } +}; + +static OSSL_ALGORITHM const * +fd_event_rpk_provider_query( void * provctx FD_PARAM_UNUSED, + int operation_id, + int * no_cache ) { + *no_cache = 0; + switch( operation_id ) { + case OSSL_OP_KEYMGMT: return fd_event_rpk_keymgmt_algs; + case OSSL_OP_SIGNATURE: return fd_event_rpk_signature_algs; + default: return NULL; + } +} + +static OSSL_DISPATCH const fd_event_rpk_provider_fns[] = { + { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))fd_event_rpk_provider_query }, + OSSL_DISPATCH_END +}; + +static int +fd_event_rpk_provider_init( OSSL_CORE_HANDLE const * handle FD_PARAM_UNUSED, + OSSL_DISPATCH const * in FD_PARAM_UNUSED, + OSSL_DISPATCH const ** out, + void ** provctx ) { + *provctx = NULL; + *out = fd_event_rpk_provider_fns; + return 1; +} + +static void +fd_event_rpk_provider_ensure_loaded( void ) { + static OSSL_PROVIDER * provider; + FD_ONCE_BEGIN { + if( FD_UNLIKELY( !OSSL_PROVIDER_add_builtin( NULL, FD_EVENT_RPK_PROVIDER_NAME, fd_event_rpk_provider_init ) ) ) { + FD_LOG_ERR(( "OSSL_PROVIDER_add_builtin(" FD_EVENT_RPK_PROVIDER_NAME ") failed" )); + } + provider = OSSL_PROVIDER_load( NULL, FD_EVENT_RPK_PROVIDER_NAME ); + if( FD_UNLIKELY( !provider ) ) { + FD_LOG_ERR(( "OSSL_PROVIDER_load(" FD_EVENT_RPK_PROVIDER_NAME ") failed" )); + } + } FD_ONCE_END; +} + +static EVP_PKEY * +fd_event_rpk_pkey_new( uchar const * identity_pubkey, + fd_keyguard_client_t * keyguard_client ) { + fd_event_rpk_provider_ensure_loaded(); + + EVP_PKEY_CTX * pkey_ctx = EVP_PKEY_CTX_new_from_name( NULL, "ED25519", "provider=" FD_EVENT_RPK_PROVIDER_NAME ); + if( FD_UNLIKELY( !pkey_ctx ) ) return NULL; + if( FD_UNLIKELY( EVP_PKEY_fromdata_init( pkey_ctx )<=0 ) ) { + EVP_PKEY_CTX_free( pkey_ctx ); + return NULL; + } + + ulong keyguard_ptr = (ulong)(uintptr_t)keyguard_client; + OSSL_PARAM params[] = { + OSSL_PARAM_construct_octet_string( OSSL_PKEY_PARAM_PUB_KEY, (void *)identity_pubkey, 32UL ), + OSSL_PARAM_construct_ulong ( FD_EVENT_RPK_KEYGUARD_PARAM, &keyguard_ptr ), + OSSL_PARAM_END + }; + + EVP_PKEY * pkey = NULL; + int const ok = EVP_PKEY_fromdata( pkey_ctx, &pkey, OSSL_KEYMGMT_SELECT_KEYPAIR, params ); + EVP_PKEY_CTX_free( pkey_ctx ); + return ok>0 ? pkey : NULL; +} + +int +fd_event_auth_set_identity( SSL * ssl, + uchar const * identity_pubkey, + fd_keyguard_client_t * keyguard_client ) { + uchar client_cert_type = TLSEXT_cert_type_rpk; + if( FD_UNLIKELY( !SSL_set1_client_cert_type( ssl, &client_cert_type, 1UL ) ) ) return 0; + + EVP_PKEY * rpk = fd_event_rpk_pkey_new( identity_pubkey, keyguard_client ); + if( FD_UNLIKELY( !rpk ) ) return 0; + + int const ok = SSL_use_PrivateKey( ssl, rpk ); + EVP_PKEY_free( rpk ); + return ok; +} diff --git a/src/disco/events/fd_event_auth.h b/src/disco/events/fd_event_auth.h new file mode 100644 index 00000000000..9be28582dab --- /dev/null +++ b/src/disco/events/fd_event_auth.h @@ -0,0 +1,31 @@ +#ifndef HEADER_fd_src_disco_events_fd_event_auth_h +#define HEADER_fd_src_disco_events_fd_event_auth_h + +/* fd_event_auth.h provides OpenSSL glue code to authenticate the + event client with the validator's identity key. + + This is done securely via 'remote' (IPC) signing via the sign tile. + The identity key is only used to sign TLS 1.3 CertificateVerify + messages. */ + +#include "../keyguard/fd_keyguard_client.h" +#include + +FD_PROTOTYPES_BEGIN + +/* fd_event_auth_set_identity configures an SSL connection to use RFC + 7250 RawPublicKey client authentication using the validator + identity Ed25519 public key. + + Signing is delegated to the sign tile via the provided keyguard + client object. (Retains mutable borrow on keyguard_client until the + SSL object is destroyed.) */ + +int +fd_event_auth_set_identity( SSL * ssl, + uchar const * identity_pubkey, + fd_keyguard_client_t * keyguard_client ); + +FD_PROTOTYPES_END + +#endif /* HEADER_fd_src_disco_events_fd_event_auth_h */ diff --git a/src/disco/events/fd_event_client.c b/src/disco/events/fd_event_client.c index 90ce30e145c..022876f65c0 100644 --- a/src/disco/events/fd_event_client.c +++ b/src/disco/events/fd_event_client.c @@ -1,5 +1,6 @@ #define _GNU_SOURCE #include "fd_event_client.h" +#include "fd_event_auth.h" #if !FD_HAS_OPENSSL #error "Building fd_event_client requires FD_HAS_OPENSSL" @@ -13,10 +14,8 @@ #include "../../ballet/pb/fd_pb_encode.h" #include "../../util/net/fd_ip4.h" #include "../../util/log/fd_log.h" -#include "../keyguard/fd_keyguard.h" #include "../../waltz/openssl/fd_openssl.h" #include -#include #include #include @@ -393,6 +392,13 @@ reconnect( fd_event_client_t * client, SSL_set_bio( ssl, bio, bio ); /* moves ownership of bio */ SSL_set_connect_state( ssl ); + if( FD_UNLIKELY( !fd_event_auth_set_identity( ssl, client->identity_pubkey, client->keyguard_client ) ) ) { + FD_LOG_WARNING(( "fd_event_auth_set_identity failed" )); + SSL_free( ssl ); + disconnect( client, DISCONNECT_REASON_CONNECT_FAILED, 0, 1 ); + return; + } + /* SNI and hostname verification */ if( FD_UNLIKELY( !SSL_set_tlsext_host_name( ssl, client->server_fqdn ) ) ) { FD_LOG_WARNING(( "SSL_set_tlsext_host_name failed" )); diff --git a/src/disco/keyguard/fd_keyguard_authorize.c b/src/disco/keyguard/fd_keyguard_authorize.c index 53e04e974f4..1592aa0efd4 100644 --- a/src/disco/keyguard/fd_keyguard_authorize.c +++ b/src/disco/keyguard/fd_keyguard_authorize.c @@ -278,7 +278,7 @@ fd_keyguard_authorize_tls_cv( fd_keyguard_authority_t const * authority FD_PARAM ulong sz, int sign_type ) { if( FD_UNLIKELY( sign_type != FD_KEYGUARD_SIGN_TYPE_ED25519 ) ) return 0; - if( FD_UNLIKELY( sz != 130 ) ) return 0; + if( FD_UNLIKELY( sz != 130UL && sz != 146UL && sz != 162UL ) ) return 0; /* validate client prefix against fd_tls */ return fd_memeq( fd_tls13_cli_sign_prefix, data, sizeof(fd_tls13_cli_sign_prefix) ); @@ -385,6 +385,14 @@ fd_keyguard_payload_authorize( fd_keyguard_authority_t const * authority, /* no further restrictions on bundle */ return 1; + case FD_KEYGUARD_ROLE_EVENT: + if( FD_UNLIKELY( payload_mask != FD_KEYGUARD_PAYLOAD_TLS_CV || + !fd_keyguard_authorize_tls_cv( authority, data, sz, sign_type ) ) ) { + FD_LOG_WARNING(( "unauthorized payload type for event (mask=%#lx)", payload_mask )); + return 0; + } + return 1; + case FD_KEYGUARD_ROLE_BUNDLE_CRANK: if( FD_UNLIKELY( payload_mask != FD_KEYGUARD_PAYLOAD_TXN ) ) { FD_LOG_WARNING(( "unauthorized payload type for event (mask=%#lx)", payload_mask )); diff --git a/src/disco/keyguard/fd_sign_tile.c b/src/disco/keyguard/fd_sign_tile.c index e2a06cb94ae..777be1c2182 100644 --- a/src/disco/keyguard/fd_sign_tile.c +++ b/src/disco/keyguard/fd_sign_tile.c @@ -376,7 +376,7 @@ unprivileged_init_sensitive( fd_topo_t * topo, } else if( !strcmp(in_link->name, "event_sign" ) ) { ctx->in[ i ].role = FD_KEYGUARD_ROLE_EVENT; FD_TEST( !strcmp( out_link->name, "sign_event" ) ); - FD_TEST( in_link->mtu==32UL ); + FD_TEST( in_link->mtu==162UL ); FD_TEST( out_link->mtu==64UL ); } else if( !strcmp(in_link->name, "pack_sign" ) ) { ctx->in[ i ].role = FD_KEYGUARD_ROLE_BUNDLE_CRANK; From b15790aaad97bc54eefcd4879f0aba3c4249089c Mon Sep 17 00:00:00 2001 From: Richard Patel Date: Mon, 1 Jun 2026 04:21:50 +0000 Subject: [PATCH 5/5] events: add option to skip server cert verify --- src/app/firedancer/config/default.toml | 5 +++++ src/app/firedancer/topology.c | 1 + src/app/shared/fd_config.h | 1 + src/app/shared/fd_config_parse.c | 1 + src/disco/events/fd_event_client.c | 7 +++++-- src/disco/events/fd_event_client.h | 3 ++- src/disco/events/fd_event_tile.c | 9 +++++++-- src/disco/topo/fd_topo.h | 1 + 8 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/app/firedancer/config/default.toml b/src/app/firedancer/config/default.toml index 48ad3b4f177..c80f5ac50c4 100644 --- a/src/app/firedancer/config/default.toml +++ b/src/app/firedancer/config/default.toml @@ -1465,6 +1465,11 @@ telemetry = true # `telemetry` to false to disable event reporting. url = "https://events-in.firedancer.io" + # By default, the TLS certificate of the event server is + # verified against the CA certs in /etc/ssl/certs. To disable + # certificate verification, set this option to 'false'. + tls_cert_verify = true + # The gui tile receives data from the validator and serves an HTTP # endpoint to clients to view it. [tiles.gui] diff --git a/src/app/firedancer/topology.c b/src/app/firedancer/topology.c index 375ec782c30..14ac30cbdb1 100644 --- a/src/app/firedancer/topology.c +++ b/src/app/firedancer/topology.c @@ -1123,6 +1123,7 @@ fd_topo_configure_tile( fd_topo_tile_t * tile, fd_cstr_ncpy( tile->event.identity_key_path, config->paths.identity_key, sizeof(tile->event.identity_key_path) ); fd_cstr_ncpy( tile->event.url, config->tiles.event.url, sizeof(tile->event.url) ); fd_cstr_ncpy( tile->event.action, config->action, sizeof(tile->event.action) ); + tile->event.tls_cert_verify = !!config->tiles.event.tls_cert_verify; } else if( FD_UNLIKELY( !strcmp( tile->name, "net" ) || !strcmp( tile->name, "sock" ) ) ) { diff --git a/src/app/shared/fd_config.h b/src/app/shared/fd_config.h index da9d9e76e36..20b4698fb9c 100644 --- a/src/app/shared/fd_config.h +++ b/src/app/shared/fd_config.h @@ -455,6 +455,7 @@ struct fd_config { struct { char url[ 256 ]; + int tls_cert_verify; } event; struct { diff --git a/src/app/shared/fd_config_parse.c b/src/app/shared/fd_config_parse.c index 8c22914015b..00f12782a6a 100644 --- a/src/app/shared/fd_config_parse.c +++ b/src/app/shared/fd_config_parse.c @@ -244,6 +244,7 @@ fd_config_extract_pod( uchar * pod, CFG_POP ( ushort, tiles.metric.prometheus_listen_port ); CFG_POP ( cstr, tiles.event.url ); + CFG_POP ( bool, tiles.event.tls_cert_verify ); CFG_POP ( bool, tiles.gui.enabled ); CFG_POP ( cstr, tiles.gui.gui_listen_address ); diff --git a/src/disco/events/fd_event_client.c b/src/disco/events/fd_event_client.c index 022876f65c0..41142cf9fa2 100644 --- a/src/disco/events/fd_event_client.c +++ b/src/disco/events/fd_event_client.c @@ -79,6 +79,7 @@ struct fd_event_client { SSL_CTX * ssl_ctx; SSL * ssl; + int tls_cert_verify; /* wallclock deadline for hello handshake, LONG_MAX if not authenticating. */ @@ -124,7 +125,8 @@ fd_event_client_new( void * shmem, ulong boot_id, ulong machine_id, ulong buf_max, - void * ssl_ctx ) { + void * ssl_ctx, + int tls_cert_verify ) { if( FD_UNLIKELY( !shmem ) ) { FD_LOG_WARNING(( "NULL shmem" )); return NULL; @@ -176,6 +178,7 @@ fd_event_client_new( void * shmem, client->ssl_ctx = (SSL_CTX *)ssl_ctx; client->ssl = NULL; client->hello_deadline = LONG_MAX; + client->tls_cert_verify = !!tls_cert_verify; client->state = FD_EVENT_CLIENT_STATE_DISCONNECTED; client->disconnected.reconnect_deadline = 0L; @@ -406,7 +409,7 @@ reconnect( fd_event_client_t * client, disconnect( client, DISCONNECT_REASON_CONNECT_FAILED, 0, 1 ); return; } - if( FD_UNLIKELY( !SSL_set1_host( ssl, client->server_fqdn ) ) ) { + if( FD_UNLIKELY( client->tls_cert_verify && !SSL_set1_host( ssl, client->server_fqdn ) ) ) { FD_LOG_WARNING(( "SSL_set1_host failed" )); SSL_free( ssl ); disconnect( client, DISCONNECT_REASON_CONNECT_FAILED, 0, 1 ); diff --git a/src/disco/events/fd_event_client.h b/src/disco/events/fd_event_client.h index 0c53c7024c7..5929678b856 100644 --- a/src/disco/events/fd_event_client.h +++ b/src/disco/events/fd_event_client.h @@ -50,7 +50,8 @@ fd_event_client_new( void * shmem, ulong boot_id, ulong machine_id, ulong buf_max, - void * ssl_ctx ); + void * ssl_ctx, + int tls_cert_verify ); fd_event_client_t * fd_event_client_join( void * shec ); diff --git a/src/disco/events/fd_event_tile.c b/src/disco/events/fd_event_tile.c index e8d777b877f..c1854257132 100644 --- a/src/disco/events/fd_event_tile.c +++ b/src/disco/events/fd_event_tile.c @@ -393,7 +393,11 @@ privileged_init( fd_topo_t * topo, if( FD_UNLIKELY( 0!=SSL_CTX_set_alpn_protos( ssl_ctx, (uchar const *)"\x02h2", 3 ) ) ) FD_LOG_ERR(( "SSL_CTX_set_alpn_protos failed" )); - fd_ossl_load_certs( ssl_ctx ); /* also sets SSL_VERIFY_PEER */ + if( tile->event.tls_cert_verify ) { + fd_ossl_load_certs( ssl_ctx ); /* also sets SSL_VERIFY_PEER */ + } else { + SSL_CTX_set_verify( ssl_ctx, SSL_VERIFY_NONE, NULL ); + } ctx->ssl_ctx = ssl_ctx; } @@ -460,7 +464,8 @@ unprivileged_init( fd_topo_t * topo, ctx->boot_id, ctx->machine_id, GRPC_BUF_MAX, - ssl_ctx_ptr ) ); + ssl_ctx_ptr, + tile->event.tls_cert_verify ) ); FD_TEST( ctx->client ); ctx->topo = topo; diff --git a/src/disco/topo/fd_topo.h b/src/disco/topo/fd_topo.h index efed347137f..85350377203 100644 --- a/src/disco/topo/fd_topo.h +++ b/src/disco/topo/fd_topo.h @@ -288,6 +288,7 @@ struct fd_topo_tile { char url[ 256 ]; char identity_key_path[ PATH_MAX ]; char action[ 16 ]; + uchar tls_cert_verify : 1; } event; struct {