Dear BIRD community, I think many of us use good ole' TCP-MD5 to authenticate IBGP sessions, even if TCP-MD5 is imperfect from key rolling perspective. TCP-MD5 is easy to configure, and supported on a broad range of platforms, and beats doing nothing.
RPKI-To-Router (RTR) sessions seem to be similar security-sensitivity as IBGP sessions, but at the moment of writing BIRD offers a choice of either "plain TCP" (meh) or "SSH" (secure, albeit a bit more hassle to set up than TCP-MD5). I'd like to add TCP-MD5 as another option. TCP-MD5 for RTR is specified through RFC 6810 section 7.3 and RFC 8210 section 9.3. Minimal bird.conf: router id 10.0.0.1; roa4 table r4; roa6 table r6; protocol rpki rpki1 { roa4 { table r4; }; roa6 { table r6; }; remote 165.254.255.17 port 8282; transport tcp password "test"; /* password keyword is new here! */ debug all; } This config was tested on Linux against a WIP based on StayRTR, tcpdump suggests this works as expected. job@josephine:~/bird$ ./birdc -s /tmp/bird show protocols all BIRD v2.15.1-29-g35f74c4b-x ready. Name Proto Table State Since Info rpki1 RPKI --- up 15:16:41.677 Established Cache server: 165.254.255.17 Cache port: 8282 Status: Established Transport: TCP-MD5 I can leave the server at 165.254.255.17 port 8282 with TCP-MD5 authentication password "test" running for a bit so others can more easily test the below patch without having to compile/run StayRTR. Below is a changeset for your consideration! Feedback is welcome. Kind regards, Job ps. It seems TCP-MD5 for BGP doesn't work out-of-the-box on OpenBSD, downstream porters apply a few minimal patches: https://github.com/openbsd/ports/tree/master/net/bird/2/patches perhaps these can be upstreamed so that we can work towards TCP-MD5 RTR support in BIRD on OpenBSD as well? :-) Author: Job Snijders <j...@fastly.com> Date: Wed Sep 18 10:46:06 2024 +0000 Add RTR TCP-MD5 authentication support diff --git a/doc/bird.sgml b/doc/bird.sgml index e2050c13..07bedef9 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -5735,7 +5735,7 @@ protocol rpki [<name>] { refresh [keep] <num>; retry [keep] <num>; expire [keep] <num>; - transport tcp; + transport tcp [password <m/string/]; transport ssh { bird private key "</path/to/id_rsa>"; remote public key "</path/to/known_host>"; @@ -5791,9 +5791,10 @@ specify both channels. instead. This may be useful for implementing loose RPKI check for blackholes. Default: disabled. - <tag>transport tcp</tag> Unprotected transport over TCP. It's a default - transport. Should be used only on secure private networks. - Default: tcp + <tag>transport tcp [password <m/string/]</tag> transport over TCP. It's + the default transport. Should be used only on secure private networks. + If a password is specified, the session is authenticated using TCP-MD5 + (<rfc id="2385">). Default: tcp, no authentication. <tag>transport ssh { <m/SSH transport options.../ }</tag> It enables a SSHv2 transport encryption. Cannot be combined with a TCP transport. diff --git a/proto/rpki/config.Y b/proto/rpki/config.Y index 769ebb2c..a0b678be 100644 --- a/proto/rpki/config.Y +++ b/proto/rpki/config.Y @@ -32,7 +32,8 @@ rpki_check_unused_transport(void) CF_DECLS CF_KEYWORDS(RPKI, REMOTE, BIRD, PRIVATE, PUBLIC, KEY, TCP, SSH, TRANSPORT, USER, - RETRY, REFRESH, EXPIRE, KEEP, IGNORE, MAX, LENGTH, LOCAL, ADDRESS) + RETRY, REFRESH, EXPIRE, KEEP, IGNORE, MAX, LENGTH, LOCAL, ADDRESS, + PASSWORD) %type <i> rpki_keep_interval @@ -109,6 +110,7 @@ rpki_cache_addr: text_or_ipa rpki_transport: TCP rpki_transport_tcp_init + | TCP PASSWORD text rpki_transport_tcp_init { RPKI_CFG->password = $3; } | SSH rpki_transport_ssh_init '{' rpki_transport_ssh_opts '}' rpki_transport_ssh_check ; diff --git a/proto/rpki/rpki.c b/proto/rpki/rpki.c index 4ec48e3b..d793ee56 100644 --- a/proto/rpki/rpki.c +++ b/proto/rpki/rpki.c @@ -673,6 +673,12 @@ rpki_reconfigure_cache(struct rpki_proto *p UNUSED, struct rpki_cache *cache, st return NEED_RESTART; } + if (old->password != new->password) + { + CACHE_TRACE(D_EVENTS, cache, "MD5 authentication changed"); + return NEED_RESTART; + } + if (old->tr_config.type != new->tr_config.type) { CACHE_TRACE(D_EVENTS, cache, "Transport type changed"); @@ -843,7 +849,10 @@ rpki_show_proto_info(struct proto *P) break; #endif case RPKI_TR_TCP: - transport_name = "Unprotected over TCP"; + if (cf->password == NULL) + transport_name = "Unprotected over TCP"; + else + transport_name = "TCP-MD5"; default_port = RPKI_TCP_PORT; break; }; diff --git a/proto/rpki/rpki.h b/proto/rpki/rpki.h index e67eb0e3..d8fd72ce 100644 --- a/proto/rpki/rpki.h +++ b/proto/rpki/rpki.h @@ -127,6 +127,7 @@ struct rpki_config { u8 keep_retry_interval:1; /* Do not overwrite retry interval by cache server update */ u8 keep_expire_interval:1; /* Do not overwrite expire interval by cache server update */ u8 ignore_max_length:1; /* Ignore received max length and use MAX_PREFIX_LENGTH instead */ + const char *password; /* Password used for MD5 authentication */ }; void rpki_check_config(struct rpki_config *cf); diff --git a/proto/rpki/transport.c b/proto/rpki/transport.c index 26571977..46b00822 100644 --- a/proto/rpki/transport.c +++ b/proto/rpki/transport.c @@ -88,6 +88,9 @@ rpki_tr_open(struct rpki_tr_sock *tr) sk->tos = IP_PREC_INTERNET_CONTROL; sk->vrf = cache->p->p.vrf; + if (cf->tr_config.type == RPKI_TR_TCP && cf->password != NULL) + sk->password = cf->password; + if (ipa_zero(sk->daddr) && sk->host) { const char *err_msg;