diff options
author | Matt Heon <mheon@redhat.com> | 2024-04-05 10:58:32 -0400 |
---|---|---|
committer | Matt Heon <mheon@redhat.com> | 2024-05-29 13:26:16 -0400 |
commit | 011f352577bd00cd3aaeb7994a73997a6878af5b (patch) | |
tree | d4702f2d0109ee5a74d3b8e086fa7caadf036495 /test | |
parent | 7b2891db2e35ccb8961e52a46c775f52722d402d (diff) |
Internal networks cannot make external DNS requests
Internal networks cannot connect to the internet thanks to
routing, but they can connect to Aardvark, which will happily
forward their DNS requests to the internet.
This could theoretically be used to build a data-exfiltration
sidechannel.
Fix this by identifying internal networks with a filename suffix
(using a character disallowed in actual network names to ensure
we don't conflict with another network) and explicitly setting
their DNS servers to an empty list (and refusing to set
per-container DNS at all). We could actually error on finding DNS
servers in an internal network, but silently ignoring prevents
possible compatibility issues with Netavark.
Signed-off-by: Matt Heon <mheon@redhat.com>
Diffstat (limited to 'test')
-rw-r--r-- | test/100-basic-name-resolution.bats | 50 | ||||
-rw-r--r-- | test/helpers.bash | 18 |
2 files changed, 61 insertions, 7 deletions
diff --git a/test/100-basic-name-resolution.bats b/test/100-basic-name-resolution.bats index f0f1bd7..e178717 100644 --- a/test/100-basic-name-resolution.bats +++ b/test/100-basic-name-resolution.bats @@ -165,3 +165,53 @@ load helpers # contain unexpected warning. assert "$output" !~ "WARNING: recursion requested but not available" } + +# Internal network, meaning no DNS servers. +# Hence all external requests must fail. +@test "basic container - internal network has no DNS" { + setup_slirp4netns + + subnet_a=$(random_subnet) + create_config network_name="podman1" internal=true container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" custom_dns_server='"1.1.1.1","8.8.8.8"' aliases='"a1", "1a"' + config_a1=$config + # Network name is still recorded as podman1 + ip_a1=$(echo "$config_a1" | jq -r .networks.podman1.static_ips[0]) + gw=$(echo "$config_a1" | jq -r .network_info.podman1.subnets[0].gateway) + create_container "$config_a1" + a1_pid=$CONTAINER_NS_PID + run_in_container_netns "$a1_pid" "dig" "+short" "aone" "@$gw" + assert "$ip_a1" + # Set recursion bit is already set if requested so output must not + # contain unexpected warning. + assert "$output" !~ "WARNING: recursion requested but not available" + + # Internal network means no DNS server means this should hard-fail + expected_rc=1 run_in_container_netns "$a1_pid" "host" "-t" "ns" "google.com" "$gw" + assert "$output" =~ "Host google.com not found" + assert "$output" =~ "NXDOMAIN" +} + +# Internal network, but this time with IPv6. Same result as above expected. +@test "basic container - internal network has no DNS - ipv6" { + setup_slirp4netns + + subnet_a=$(random_subnet 6) + # Cloudflare and Google public anycast DNS v6 nameservers + create_config network_name="podman1" internal=true container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" custom_dns_server='"2606:4700:4700::1111","2001:4860:4860::8888"' aliases='"a1", "1a"' + config_a1=$config + # Network name is still recorded as podman1 + ip_a1=$(echo "$config_a1" | jq -r .networks.podman1.static_ips[0]) + gw=$(echo "$config_a1" | jq -r .network_info.podman1.subnets[0].gateway) + create_container "$config_a1" + a1_pid=$CONTAINER_NS_PID + run_in_container_netns "$a1_pid" "dig" "+short" "aone" "@$gw" "AAAA" + assert "$ip_a1" + # Set recursion bit is already set if requested so output must not + # contain unexpected warning. + assert "$output" !~ "WARNING: recursion requested but not available" + + # Internal network means no DNS server means this should hard-fail + expected_rc=1 run_in_container_netns "$a1_pid" "host" "-t" "ns" "google.com" "$gw" + assert "$output" =~ "Host google.com not found" + assert "$output" =~ "NXDOMAIN" +} diff --git a/test/helpers.bash b/test/helpers.bash index 947e563..9537af8 100644 --- a/test/helpers.bash +++ b/test/helpers.bash @@ -366,6 +366,7 @@ function run_in_host_netns() { # subnet=$subnet specifies the network subnet # custom_dns_serve=$custom_dns_server # aliases=$aliases comma seperated container aliases for dns resolution. +# internal={true,false} default is false function create_config() { local network_name="" local container_id="" @@ -373,6 +374,7 @@ function create_config() { local subnet="" local custom_dns_server local aliases="" + local internal=false # parse arguments while [[ "$#" -gt 0 ]]; do @@ -396,6 +398,9 @@ function create_config() { aliases) aliases="$value" ;; + internal) + internal="$value" + ;; *) die "unknown argument for '$arg' create_config" ;; esac shift @@ -407,7 +412,7 @@ function create_config() { subnets="{\"subnet\":\"$subnet\",\"gateway\":\"$container_gw\"}" create_network "$network_name" "$container_ip" "eth0" "$aliases" - create_network_infos "$network_name" $(random_string 64) "$subnets" + create_network_infos "$network_name" $(random_string 64) "$subnets" "$internal" read -r -d '\0' config <<EOF { @@ -431,13 +436,12 @@ EOF # arg1 is network name # arg2 network_id # arg3 is subnets +# arg4 is internal function create_network_infos() { local net_name=$1 - shift - local net_id=$1 - shift - local subnets=$1 - shift + local net_id=$2 + local subnets=$3 + local internal=${4:-false} local interface_name=${net_name:0:7} read -r -d '\0' new_network_info <<EOF @@ -450,7 +454,7 @@ function create_network_infos() { $subnets ], "ipv6_enabled": true, - "internal": false, + "internal": $internal, "dns_enabled": true, "ipam_options": { "driver": "host-local" |