@@ -365,6 +365,24 @@ fn dns_resolvers() -> &'static HashSet<IpAddr> {
365365
366366impl NetworkPolicy {
367367 fn check ( & self , addr : & std:: net:: SocketAddr ) -> Result < ( ) > {
368+ // Block link-local addresses (169.254.0.0/16, fe80::/10) for all
369+ // policy variants. In cloud environments the IPv4 link-local range
370+ // hosts the instance metadata service (169.254.169.254) which hands
371+ // out credentials without authentication.
372+ let is_link_local = match addr. ip ( ) {
373+ std:: net:: IpAddr :: V4 ( v4) => v4. is_link_local ( ) ,
374+ std:: net:: IpAddr :: V6 ( v6) => {
375+ let seg = v6. segments ( ) ;
376+ ( seg[ 0 ] & 0xffc0 ) == 0xfe80
377+ }
378+ } ;
379+ if is_link_local {
380+ return Err ( anyhow ! (
381+ "network policy denies connection to link-local address {}" ,
382+ addr
383+ ) ) ;
384+ }
385+
368386 match self {
369387 NetworkPolicy :: AllowAll => Ok ( ( ) ) ,
370388 NetworkPolicy :: AllowList ( al) => {
@@ -2724,6 +2742,37 @@ mod tests {
27242742 assert ! ( result. is_err( ) ) ;
27252743 }
27262744
2745+ #[ test]
2746+ fn net_link_local_blocked ( ) {
2747+ use std:: net:: { Ipv4Addr , Ipv6Addr , SocketAddr } ;
2748+
2749+ // AllowAll still blocks link-local
2750+ let policy = NetworkPolicy :: AllowAll ;
2751+ let meta = SocketAddr :: new ( Ipv4Addr :: new ( 169 , 254 , 169 , 254 ) . into ( ) , 80 ) ;
2752+ assert ! (
2753+ policy. check( & meta) . is_err( ) ,
2754+ "AllowAll must block IPv4 link-local"
2755+ ) ;
2756+
2757+ let link6 = SocketAddr :: new ( Ipv6Addr :: new ( 0xfe80 , 0 , 0 , 0 , 0 , 0 , 0 , 1 ) . into ( ) , 80 ) ;
2758+ assert ! (
2759+ policy. check( & link6) . is_err( ) ,
2760+ "AllowAll must block IPv6 link-local"
2761+ ) ;
2762+
2763+ // BlockList (even empty) also blocks link-local
2764+ let bl = BlockList :: from_hosts ( & [ "192.0.2.1" ] ) . unwrap ( ) ;
2765+ let policy = NetworkPolicy :: BlockList ( bl) ;
2766+ assert ! (
2767+ policy. check( & meta) . is_err( ) ,
2768+ "BlockList must block IPv4 link-local"
2769+ ) ;
2770+ assert ! (
2771+ policy. check( & link6) . is_err( ) ,
2772+ "BlockList must block IPv6 link-local"
2773+ ) ;
2774+ }
2775+
27272776 #[ test]
27282777 fn net_tools_registered_with_blocklist ( ) {
27292778 let mut tools = ToolRegistry :: new ( ) ;
0 commit comments