tcp: Require init_net CAP_NET_ADMIN for tcp_child_ehash_entries
Subsystems (2)
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH 1/1 tcp: Require init_net CAP_NET_ADMIN for tcp_child_ehash_entries
From: Zhiling Zou <[email protected]>
tcp_child_ehash_entries controls the size of the private TCP established hash table allocated for subsequently created child network namespaces. The value is consumed during child netns creation by tcp_set_hashinfo() and passed to inet_pernet_hashinfo_alloc(), which can allocate a large per-netns ehash.
The sysctl is writable in each network namespace, and net sysctl permissions allow a task with CAP_NET_ADMIN in the namespace's owning user namespace to write it. An unprivileged user can therefore create a user and network namespace, set tcp_child_ehash_entries to its maximum value, and repeatedly create nested network namespaces to force large kernel allocations and exhaust host memory.
Require CAP_NET_ADMIN in the initial user namespace before accepting writes to tcp_child_ehash_entries. This keeps the tuning knob available to the host administrator while preventing unprivileged user namespaces from using it to drive host-wide memory consumption.
--- net/ipv4/sysctl_net_ipv4.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
net/ipv4/sysctl_net_ipv4.c +12 -1
@@ -9,6 +9,7 @@#include <linux/sysctl.h> #include <linux/seqlock.h> #include <linux/init.h> +#include <linux/capability.h> #include <linux/slab.h> #include <net/icmp.h> #include <net/ip.h>
@@ -415,6 +416,16 @@ static int proc_tcp_ehash_entries(const struct ctl_table *table, int write,return proc_dointvec (&tbl, write, buffer, lenp, ppos); } +static int proc_tcp_child_ehash_entries (const struct ctl_table *table,int write, +void *buffer,size_t *lenp, +loff_t *ppos) +{ +if (write && !capable (CAP_NET_ADMIN )) +return -EPERM ; + +return proc_douintvec_minmax (table, write, buffer, lenp, ppos); +} +static int proc_udp_hash_entries (const struct ctl_table *table,int write,void *buffer,size_t *lenp,loff_t *ppos) {
@@ -1524,7 +1535,7 @@ static struct ctl_table ipv4_net_table[] = { .data = &init_net.ipv4.sysctl_tcp_child_ehash_entries, .maxlen = sizeof(unsigned int), .mode = 0644, - .proc_handler = proc_douintvec_minmax, + .proc_handler = proc_tcp_child_ehash_entries, .extra1 = SYSCTL_ZERO, .extra2 = &tcp_child_ehash_entries_max, },
Signature · 1 line
2.43.0
Quoted 16 lines · Ren Wei
From: Zhiling Zou <[email protected]>
tcp_child_ehash_entriescontrols the size of the private TCP established hash table allocated for subsequently created child network namespaces. The value is consumed during child netns creation bytcp_set_hashinfo()and passed toinet_pernet_hashinfo_alloc(), which can allocate a large per-netns ehash.The sysctl is writable in each network namespace, and net sysctl permissions allow a task with
CAP_NET_ADMINin the namespace's owning user namespace to write it. An unprivileged user can therefore create a user and network namespace, settcp_child_ehash_entriesto its maximum value, and repeatedly create nested network namespaces to force large kernel allocations and exhaust host memory.
Quoted 4 lines
Require
CAP_NET_ADMINin the initial user namespace before accepting writes totcp_child_ehash_entries. This keeps the tuning knob available to the host administrator while preventing unprivileged user namespaces from using it to drive host-wide memory consumption.
I do not think this patch is desirable.
It breaks nested container use cases.
A container runtime running inside a container (with namespace-local CAP_NET_ADMIN but not global)
would no longer be able to tune tcp_child_ehash_entries for its own nested child namespaces.
inet_pernet_hashinfo_alloc() uses GFP_KERNEL_ACCOUNT for a reason, I suggest you start using memcg :)
Keep in mind the sysctl could be set (by root) in init_net for some reason, so only memcg will protect against OOM.
Quoted 30 lines · Eric Dumazet
Quoted 16 lines · Ren Wei
From: Zhiling Zou <[email protected]>
tcp_child_ehash_entriescontrols the size of the private TCP established hash table allocated for subsequently created child network namespaces. The value is consumed during child netns creation bytcp_set_hashinfo()and passed toinet_pernet_hashinfo_alloc(), which can allocate a large per-netns ehash.The sysctl is writable in each network namespace, and net sysctl permissions allow a task with
CAP_NET_ADMINin the namespace's owning user namespace to write it. An unprivileged user can therefore create a user and network namespace, settcp_child_ehash_entriesto its maximum value, and repeatedly create nested network namespaces to force large kernel allocations and exhaust host memory.Quoted 4 lines
Require
CAP_NET_ADMINin the initial user namespace before accepting writes totcp_child_ehash_entries. This keeps the tuning knob available to the host administrator while preventing unprivileged user namespaces from using it to drive host-wide memory consumption.I do not think this patch is desirable.
It breaks nested container use cases.
A container runtime running inside a container (with namespace-localCAP_NET_ADMINbut not global)
would no longer be able to tunetcp_child_ehash_entriesfor its own nested child namespaces.
Quoted 5 lines
inet_pernet_hashinfo_alloc()usesGFP_KERNEL_ACCOUNTfor a reason, I suggest you start using memcg :)Keep in mind the sysctl could be set (by root) in
init_netfor some reason, so only memcg will protect against OOM.
Thanks for the review.
Agreed, restricting writes to init_user_ns CAP_NET_ADMIN is too broad and would break nested container use cases. We will drop this approach.
We will re-check the memcg accounting/fallback behavior for the per-netns ehash allocation and prepare a different fix if there is still a path to host-wide OOM outside the intended memcg limits.