Skip to content

Commit

Permalink
http: use separate sockets for IPv4 and IPv6 (#221)
Browse files Browse the repository at this point in the history
  • Loading branch information
greatest-ape authored Jan 16, 2025
1 parent 048c297 commit 94e3af2
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 130 deletions.
4 changes: 2 additions & 2 deletions .github/actions/test-file-transfers/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,14 @@ address_ipv4 = '127.0.0.1:3000'" > udp.toml
echo "log_level = 'debug'
[network]
address = '127.0.0.1:3004'" > http.toml
address_ipv4 = '127.0.0.1:3004'" > http.toml
./target/debug/aquatic http -c http.toml > "$HOME/http.log" 2>&1 &

# HTTP with TLS
echo "log_level = 'debug'
[network]
address = '127.0.0.1:3001'
address_ipv4 = '127.0.0.1:3001'
enable_tls = true
tls_certificate_path = './server.crt'
tls_private_key_path = './key.pk8'
Expand Down
4 changes: 2 additions & 2 deletions crates/http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ Generate the configuration file:
./target/release/aquatic_http -p > "aquatic-http-config.toml"
```

Make necessary adjustments to the file. You will likely want to adjust `address`
(listening address) under the `network` section.
Make necessary adjustments to the file. You will likely want to adjust
listening addresses under the `network` section.

To run over TLS, configure certificate and private key files.

Expand Down
1 change: 1 addition & 0 deletions crates/http/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use aquatic_http_protocol::{
use glommio::channels::shared_channel::SharedSender;
use slotmap::new_key_type;

#[allow(dead_code)]
#[derive(Copy, Clone, Debug)]
pub struct ConsumerId(pub usize);

Expand Down
51 changes: 31 additions & 20 deletions crates/http/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::{net::SocketAddr, path::PathBuf};
use std::{
net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
path::PathBuf,
};

use aquatic_common::{access_list::AccessListConfig, privileges::PrivilegeConfig};
use aquatic_toml_config::TomlConfig;
Expand Down Expand Up @@ -70,25 +73,24 @@ impl aquatic_common::cli::Config for Config {
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct NetworkConfig {
/// Bind to this address
/// Use IPv4
pub use_ipv4: bool,
/// Use IPv6
pub use_ipv6: bool,
/// IPv4 address and port
///
/// When providing an IPv4 style address, only IPv4 traffic will be
/// handled. Examples:
/// - "0.0.0.0:3000" binds to port 3000 on all network interfaces
/// - "127.0.0.1:3000" binds to port 3000 on the loopback interface
/// (localhost)
/// Examples:
/// - Use 0.0.0.0:3000 to bind to all interfaces on port 3000
/// - Use 127.0.0.1:3000 to bind to the loopback interface (localhost) on
/// port 3000
pub address_ipv4: SocketAddrV4,
/// IPv6 address and port
///
/// When it comes to IPv6-style addresses, behaviour is more complex and
/// differs between operating systems. On Linux, to accept both IPv4 and
/// IPv6 traffic on any interface, use "[::]:3000". Set the "only_ipv6"
/// flag below to limit traffic to IPv6. To bind to the loopback interface
/// and only accept IPv6 packets, use "[::1]:3000" and set the only_ipv6
/// flag. Receiving both IPv4 and IPv6 traffic on loopback is currently
/// not supported. For other operating systems, please refer to their
/// respective documentation.
pub address: SocketAddr,
/// Only allow access over IPv6
pub only_ipv6: bool,
/// Examples:
/// - Use [::]:3000 to bind to all interfaces on port 3000
/// - Use [::1]:3000 to bind to the loopback interface (localhost) on
/// port 3000
pub address_ipv6: SocketAddrV6,
/// Maximum number of pending TCP connections
pub tcp_backlog: i32,
/// Enable TLS
Expand Down Expand Up @@ -125,21 +127,30 @@ pub struct NetworkConfig {
/// header. Works with typical multi-IP setups (e.g., "X-Forwarded-For")
/// as well as for single-IP setups (e.g., nginx "X-Real-IP")
pub reverse_proxy_ip_header_format: ReverseProxyPeerIpHeaderFormat,
/// Set flag on IPv6 socket to only accept IPv6 traffic.
///
/// This should typically be set to true unless your OS does not support
/// double-stack sockets (that is, sockets that receive both IPv4 and IPv6
/// packets).
pub set_only_ipv6: bool,
}

impl Default for NetworkConfig {
fn default() -> Self {
Self {
address: SocketAddr::from(([0, 0, 0, 0], 3000)),
use_ipv4: true,
use_ipv6: true,
address_ipv4: SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 3000),
address_ipv6: SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 3000, 0, 0),
enable_tls: false,
tls_certificate_path: "".into(),
tls_private_key_path: "".into(),
only_ipv6: false,
tcp_backlog: 1024,
keep_alive: true,
runs_behind_reverse_proxy: false,
reverse_proxy_ip_header_name: "X-Forwarded-For".into(),
reverse_proxy_ip_header_format: Default::default(),
set_only_ipv6: true,
}
}
}
Expand Down
24 changes: 21 additions & 3 deletions crates/http/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ const SHARED_CHANNEL_SIZE: usize = 1024;
pub fn run(config: Config) -> ::anyhow::Result<()> {
let mut signals = Signals::new([SIGUSR1])?;

if !(config.network.use_ipv4 || config.network.use_ipv6) {
return Result::Err(anyhow::anyhow!(
"Both use_ipv4 and use_ipv6 can not be set to false"
));
}

let state = State::default();

update_access_list(&config.access_list, &state.access_list)?;
Expand All @@ -35,7 +41,14 @@ pub fn run(config: Config) -> ::anyhow::Result<()> {
config.socket_workers + config.swarm_workers,
SHARED_CHANNEL_SIZE,
);
let priv_dropper = PrivilegeDropper::new(config.privileges.clone(), config.socket_workers);

let num_sockets_per_worker =
if config.network.use_ipv4 { 1 } else { 0 } + if config.network.use_ipv6 { 1 } else { 0 };

let priv_dropper = PrivilegeDropper::new(
config.privileges.clone(),
config.socket_workers * num_sockets_per_worker,
);

let opt_tls_config = if config.network.enable_tls {
Some(Arc::new(ArcSwap::from_pointee(create_rustls_config(
Expand All @@ -55,7 +68,12 @@ pub fn run(config: Config) -> ::anyhow::Result<()> {
let state = state.clone();
let opt_tls_config = opt_tls_config.clone();
let request_mesh_builder = request_mesh_builder.clone();
let priv_dropper = priv_dropper.clone();

let mut priv_droppers = Vec::new();

for _ in 0..num_sockets_per_worker {
priv_droppers.push(priv_dropper.clone());
}

let handle = Builder::new()
.name(format!("socket-{:02}", i + 1))
Expand All @@ -68,7 +86,7 @@ pub fn run(config: Config) -> ::anyhow::Result<()> {
state,
opt_tls_config,
request_mesh_builder,
priv_dropper,
priv_droppers,
server_start_instant,
i,
))
Expand Down
Loading

0 comments on commit 94e3af2

Please sign in to comment.