// Copyright 2024 RustFS Team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! AWS metadata fetching implementation for identifying trusted proxy ranges. use async_trait::async_trait; use reqwest::Client; use std::str::FromStr; use std::time::Duration; use tracing::{debug, info}; use crate::AppError; use crate::CloudMetadataFetcher; /// Fetcher for AWS-specific metadata. #[derive(Debug, Clone)] pub struct AwsMetadataFetcher { client: Client, #[allow( dead_code, reason = "IMDS endpoint retained beside the client it configures; requests build their own URLs (backlog#1823)" )] metadata_endpoint: String, } impl AwsMetadataFetcher { /// Creates a new `AwsMetadataFetcher`. /// /// # Arguments /// /// * `timeout` - Duration to use for HTTP request timeouts. /// /// Returns a new instance of `AwsMetadataFetcher`. pub fn new(timeout: Duration) -> Self { let client = Client::builder().timeout(timeout).build().unwrap_or_else(|_| Client::new()); Self { client, metadata_endpoint: "http://169.254.169.254".to_string(), } } } #[async_trait] impl CloudMetadataFetcher for AwsMetadataFetcher { fn provider_name(&self) -> &str { "aws" } async fn fetch_network_cidrs(&self) -> Result, AppError> { // Simplified implementation: returns standard AWS VPC private ranges. let default_ranges = vec![ "10.0.0.0/8", // Large VPCs "172.16.0.0/12", // Medium VPCs "192.168.0.0/16", // Small VPCs ]; let networks: Result, _> = default_ranges.into_iter().map(ipnetwork::IpNetwork::from_str).collect(); match networks { Ok(networks) => { debug!( event = "trusted_proxies.cloud_metadata", component = "trusted_proxies", subsystem = "aws_metadata", provider = "aws", operation = "network_cidrs", result = "fallback", source = "default_ranges", range_count = networks.len(), "trusted proxy cloud metadata fallback applied" ); Ok(networks) } Err(e) => Err(AppError::cloud(format!("Failed to parse default AWS ranges: {}", e))), } } async fn fetch_public_ip_ranges(&self) -> Result, AppError> { let url = "https://ip-ranges.amazonaws.com/ip-ranges.json"; #[derive(Debug, serde::Deserialize)] struct AwsIpRanges { prefixes: Vec, } #[derive(Debug, serde::Deserialize)] struct AwsPrefix { ip_prefix: String, service: String, } match self.client.get(url).timeout(Duration::from_secs(5)).send().await { Ok(response) => { if response.status().is_success() { let ip_ranges: AwsIpRanges = response .json() .await .map_err(|e| AppError::cloud(format!("Failed to parse AWS IP ranges JSON: {}", e)))?; let mut networks = Vec::new(); for prefix in ip_ranges.prefixes { // Include EC2 and CloudFront ranges as potential trusted proxies. if (prefix.service == "EC2" || prefix.service == "CLOUDFRONT") && let Ok(network) = ipnetwork::IpNetwork::from_str(&prefix.ip_prefix) { networks.push(network); } } info!( event = "trusted_proxies.cloud_metadata", component = "trusted_proxies", subsystem = "aws_metadata", provider = "aws", operation = "public_ip_ranges", result = "loaded", source = "api", range_count = networks.len(), "trusted proxy cloud metadata loaded" ); Ok(networks) } else { debug!( event = "trusted_proxies.cloud_metadata", component = "trusted_proxies", subsystem = "aws_metadata", provider = "aws", operation = "public_ip_ranges", result = "http_error", status = %response.status(), "trusted proxy cloud metadata request failed" ); Ok(Vec::new()) } } Err(e) => { debug!( event = "trusted_proxies.cloud_metadata", component = "trusted_proxies", subsystem = "aws_metadata", provider = "aws", operation = "public_ip_ranges", result = "request_failed", error = %e, "trusted proxy cloud metadata request failed" ); Ok(Vec::new()) } } } }