LCOV - code coverage report
Current view: top level - proxy/src/control_plane - mod.rs (source / functions) Coverage Total Hit
Test: c8f8d331b83562868d9054d9e0e68f866772aeaa.info Lines: 53.8 % 52 28
Test Date: 2025-07-26 17:20:05 Functions: 66.7 % 3 2

            Line data    Source code
       1              : //! Various stuff for dealing with the Neon Console.
       2              : //! Later we might move some API wrappers here.
       3              : 
       4              : /// Payloads used in the console's APIs.
       5              : pub mod messages;
       6              : 
       7              : /// Wrappers for console APIs and their mocks.
       8              : pub mod client;
       9              : 
      10              : pub(crate) mod errors;
      11              : 
      12              : use std::sync::Arc;
      13              : 
      14              : use messages::EndpointRateLimitConfig;
      15              : 
      16              : use crate::auth::backend::ComputeUserInfo;
      17              : use crate::auth::backend::jwt::AuthRule;
      18              : use crate::auth::{AuthError, IpPattern, check_peer_addr_is_in_list};
      19              : use crate::cache::node_info::CachedNodeInfo;
      20              : use crate::context::RequestContext;
      21              : use crate::control_plane::messages::MetricsAuxInfo;
      22              : use crate::intern::{AccountIdInt, EndpointIdInt, ProjectIdInt};
      23              : use crate::protocol2::ConnectionInfoExtra;
      24              : use crate::rate_limiter::{EndpointRateLimiter, LeakyBucketConfig};
      25              : use crate::types::{EndpointId, RoleName};
      26              : use crate::{compute, scram};
      27              : 
      28              : /// Various cache-related types.
      29              : pub mod caches {
      30              :     pub use super::client::ApiCaches;
      31              : }
      32              : 
      33              : /// Various cache-related types.
      34              : pub mod locks {
      35              :     pub use super::client::ApiLocks;
      36              : }
      37              : 
      38              : /// Console's management API.
      39              : pub mod mgmt;
      40              : 
      41              : /// Auth secret which is managed by the cloud.
      42              : #[derive(Clone, Eq, PartialEq, Debug)]
      43              : pub(crate) enum AuthSecret {
      44              :     /// [SCRAM](crate::scram) authentication info.
      45              :     Scram(scram::ServerSecret),
      46              : }
      47              : 
      48              : #[derive(Default)]
      49              : pub(crate) struct AuthInfo {
      50              :     pub(crate) secret: Option<AuthSecret>,
      51              :     /// List of IP addresses allowed for the autorization.
      52              :     pub(crate) allowed_ips: Vec<IpPattern>,
      53              :     /// List of VPC endpoints allowed for the autorization.
      54              :     pub(crate) allowed_vpc_endpoint_ids: Vec<String>,
      55              :     /// Project ID. This is used for cache invalidation.
      56              :     pub(crate) project_id: Option<ProjectIdInt>,
      57              :     /// Account ID. This is used for cache invalidation.
      58              :     pub(crate) account_id: Option<AccountIdInt>,
      59              :     /// Are public connections or VPC connections blocked?
      60              :     pub(crate) access_blocker_flags: AccessBlockerFlags,
      61              :     /// The rate limits for this endpoint.
      62              :     pub(crate) rate_limits: EndpointRateLimitConfig,
      63              : }
      64              : 
      65              : /// Info for establishing a connection to a compute node.
      66              : #[derive(Clone)]
      67              : pub(crate) struct NodeInfo {
      68              :     pub(crate) conn_info: compute::ConnectInfo,
      69              : 
      70              :     /// Labels for proxy's metrics.
      71              :     pub(crate) aux: MetricsAuxInfo,
      72              : }
      73              : 
      74              : #[derive(Copy, Clone, Default, Debug)]
      75              : pub(crate) struct AccessBlockerFlags {
      76              :     pub public_access_blocked: bool,
      77              :     pub vpc_access_blocked: bool,
      78              : }
      79              : 
      80              : #[derive(Clone, Debug)]
      81              : pub struct RoleAccessControl {
      82              :     pub secret: Option<AuthSecret>,
      83              : }
      84              : 
      85              : #[derive(Clone, Debug)]
      86              : pub struct EndpointAccessControl {
      87              :     pub allowed_ips: Arc<Vec<IpPattern>>,
      88              :     pub allowed_vpce: Arc<Vec<String>>,
      89              :     pub flags: AccessBlockerFlags,
      90              : 
      91              :     pub rate_limits: EndpointRateLimitConfig,
      92              : }
      93              : 
      94              : impl EndpointAccessControl {
      95            3 :     pub fn check(
      96            3 :         &self,
      97            3 :         ctx: &RequestContext,
      98            3 :         check_ip_allowed: bool,
      99            3 :         check_vpc_allowed: bool,
     100            3 :     ) -> Result<(), AuthError> {
     101            3 :         if check_ip_allowed && !check_peer_addr_is_in_list(&ctx.peer_addr(), &self.allowed_ips) {
     102            0 :             return Err(AuthError::IpAddressNotAllowed(ctx.peer_addr()));
     103            3 :         }
     104              : 
     105              :         // check if a VPC endpoint ID is coming in and if yes, if it's allowed
     106            3 :         if check_vpc_allowed {
     107            0 :             if self.flags.vpc_access_blocked {
     108            0 :                 return Err(AuthError::NetworkNotAllowed);
     109            0 :             }
     110              : 
     111            0 :             let incoming_vpc_endpoint_id = match ctx.extra() {
     112            0 :                 None => return Err(AuthError::MissingVPCEndpointId),
     113            0 :                 Some(ConnectionInfoExtra::Aws { vpce_id }) => vpce_id.to_string(),
     114            0 :                 Some(ConnectionInfoExtra::Azure { link_id }) => link_id.to_string(),
     115              :             };
     116              : 
     117            0 :             let vpce = &self.allowed_vpce;
     118              :             // TODO: For now an empty VPC endpoint ID list means all are allowed. We should replace that.
     119            0 :             if !vpce.is_empty() && !vpce.contains(&incoming_vpc_endpoint_id) {
     120            0 :                 return Err(AuthError::vpc_endpoint_id_not_allowed(
     121            0 :                     incoming_vpc_endpoint_id,
     122            0 :                 ));
     123            0 :             }
     124            3 :         } else if self.flags.public_access_blocked {
     125            0 :             return Err(AuthError::NetworkNotAllowed);
     126            3 :         }
     127              : 
     128            3 :         Ok(())
     129            3 :     }
     130              : 
     131            3 :     pub fn connection_attempt_rate_limit(
     132            3 :         &self,
     133            3 :         ctx: &RequestContext,
     134            3 :         endpoint: &EndpointId,
     135            3 :         rate_limiter: &EndpointRateLimiter,
     136            3 :     ) -> Result<(), AuthError> {
     137            3 :         let endpoint = EndpointIdInt::from(endpoint);
     138              : 
     139            3 :         let limits = &self.rate_limits.connection_attempts;
     140            3 :         let config = match ctx.protocol() {
     141            0 :             crate::metrics::Protocol::Http => limits.http,
     142            0 :             crate::metrics::Protocol::Ws => limits.ws,
     143            3 :             crate::metrics::Protocol::Tcp => limits.tcp,
     144            0 :             crate::metrics::Protocol::SniRouter => return Ok(()),
     145              :         };
     146            3 :         let config = config.and_then(|config| {
     147            0 :             if config.rps <= 0.0 || config.burst <= 0.0 {
     148            0 :                 return None;
     149            0 :             }
     150              : 
     151            0 :             Some(LeakyBucketConfig::new(config.rps, config.burst))
     152            0 :         });
     153              : 
     154            3 :         if !rate_limiter.check(endpoint, config, 1) {
     155            0 :             return Err(AuthError::too_many_connections());
     156            3 :         }
     157              : 
     158            3 :         Ok(())
     159            3 :     }
     160              : }
     161              : 
     162              : /// This will allocate per each call, but the http requests alone
     163              : /// already require a few allocations, so it should be fine.
     164              : pub(crate) trait ControlPlaneApi {
     165              :     async fn get_role_access_control(
     166              :         &self,
     167              :         ctx: &RequestContext,
     168              :         endpoint: &EndpointId,
     169              :         role: &RoleName,
     170              :     ) -> Result<RoleAccessControl, errors::GetAuthInfoError>;
     171              : 
     172              :     async fn get_endpoint_access_control(
     173              :         &self,
     174              :         ctx: &RequestContext,
     175              :         endpoint: &EndpointId,
     176              :         role: &RoleName,
     177              :     ) -> Result<EndpointAccessControl, errors::GetAuthInfoError>;
     178              : 
     179              :     async fn get_endpoint_jwks(
     180              :         &self,
     181              :         ctx: &RequestContext,
     182              :         endpoint: &EndpointId,
     183              :     ) -> Result<Vec<AuthRule>, errors::GetEndpointJwksError>;
     184              : 
     185              :     /// Wake up the compute node and return the corresponding connection info.
     186              :     async fn wake_compute(
     187              :         &self,
     188              :         ctx: &RequestContext,
     189              :         user_info: &ComputeUserInfo,
     190              :     ) -> Result<CachedNodeInfo, errors::WakeComputeError>;
     191              : }
        

Generated by: LCOV version 2.1-beta