summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorMatt Heon <mheon@redhat.com>2024-04-05 10:58:32 -0400
committerMatt Heon <mheon@redhat.com>2024-05-29 13:26:16 -0400
commit011f352577bd00cd3aaeb7994a73997a6878af5b (patch)
treed4702f2d0109ee5a74d3b8e086fa7caadf036495 /test
parent7b2891db2e35ccb8961e52a46c775f52722d402d (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.bats50
-rw-r--r--test/helpers.bash18
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"