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 : }
|