From a9bb11ec3cb09ffd0442082a0290c1c6d275a869 Mon Sep 17 00:00:00 2001 From: Mengjiao Liu Date: Thu, 4 Nov 2021 11:03:39 +0800 Subject: [PATCH] Fix the conversion of sysctl variable dots and slashes Signed-off-by: Mengjiao Liu --- libcontainer/configs/validate/validator.go | 32 +++++++++++- .../configs/validate/validator_test.go | 51 ++++++++++++++----- 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/libcontainer/configs/validate/validator.go b/libcontainer/configs/validate/validator.go index a2a79c294..6493124a3 100644 --- a/libcontainer/configs/validate/validator.go +++ b/libcontainer/configs/validate/validator.go @@ -128,6 +128,36 @@ func (v *ConfigValidator) cgroupnamespace(config *configs.Config) error { return nil } +// convertSysctlVariableToDotsSeparator can return sysctl variables in dots separator format. +// The '/' separator is also accepted in place of a '.'. +// Convert the sysctl variables to dots separator format for validation. +// More info: +// https://man7.org/linux/man-pages/man8/sysctl.8.html +// https://man7.org/linux/man-pages/man5/sysctl.d.5.html +// For example: +// Input sysctl variable "net/ipv4/conf/eno2.100.rp_filter" +// will return the converted value "net.ipv4.conf.eno2/100.rp_filter" +func convertSysctlVariableToDotsSeparator(val string) string { + if val == "" { + return val + } + firstSepIndex := strings.IndexAny(val, "./") + if firstSepIndex == -1 || val[firstSepIndex] == '.' { + return val + } + + f := func(r rune) rune { + switch r { + case '.': + return '/' + case '/': + return '.' + } + return r + } + return strings.Map(f, val) +} + // sysctl validates that the specified sysctl keys are valid or not. // /proc/sys isn't completely namespaced and depending on which namespaces // are specified, a subset of sysctls are permitted. @@ -150,7 +180,7 @@ func (v *ConfigValidator) sysctl(config *configs.Config) error { ) for s := range config.Sysctl { - s := strings.Replace(s, "/", ".", -1) + s := convertSysctlVariableToDotsSeparator(s) if validSysctlMap[s] || strings.HasPrefix(s, "fs.mqueue.") { if config.Namespaces.Contains(configs.NEWIPC) { continue diff --git a/libcontainer/configs/validate/validator_test.go b/libcontainer/configs/validate/validator_test.go index 94d238ed7..5181333fb 100644 --- a/libcontainer/configs/validate/validator_test.go +++ b/libcontainer/configs/validate/validator_test.go @@ -184,14 +184,40 @@ func TestValidateUsernamespaceWithoutUserNS(t *testing.T) { } } +// TestConvertSysctlVariableToDotsSeparator tests whether the sysctl variable +// can be correctly converted to a dot as a separator. +func TestConvertSysctlVariableToDotsSeparator(t *testing.T) { + type testCase struct { + in string + out string + } + valid := []testCase{ + {in: "kernel.shm_rmid_forced", out: "kernel.shm_rmid_forced"}, + {in: "kernel/shm_rmid_forced", out: "kernel.shm_rmid_forced"}, + {in: "net.ipv4.conf.eno2/100.rp_filter", out: "net.ipv4.conf.eno2/100.rp_filter"}, + {in: "net/ipv4/conf/eno2.100/rp_filter", out: "net.ipv4.conf.eno2/100.rp_filter"}, + {in: "net/ipv4/ip_local_port_range", out: "net.ipv4.ip_local_port_range"}, + {in: "kernel/msgmax", out: "kernel.msgmax"}, + {in: "kernel/sem", out: "kernel.sem"}, + } + + for _, test := range valid { + convertSysctlVal := convertSysctlVariableToDotsSeparator(test.in) + if convertSysctlVal != test.out { + t.Errorf("The sysctl variable was not converted correctly. got: %s, want: %s", convertSysctlVal, test.out) + } + } +} + func TestValidateSysctl(t *testing.T) { sysctl := map[string]string{ - "fs.mqueue.ctl": "ctl", - "fs/mqueue/ctl": "ctl", - "net.ctl": "ctl", - "net/ctl": "ctl", - "kernel.ctl": "ctl", - "kernel/ctl": "ctl", + "fs.mqueue.ctl": "ctl", + "fs/mqueue/ctl": "ctl", + "net.ctl": "ctl", + "net/ctl": "ctl", + "net.ipv4.conf.eno2/100.rp_filter": "ctl", + "kernel.ctl": "ctl", + "kernel/ctl": "ctl", } for k, v := range sysctl { @@ -210,12 +236,13 @@ func TestValidateSysctl(t *testing.T) { func TestValidateValidSysctl(t *testing.T) { sysctl := map[string]string{ - "fs.mqueue.ctl": "ctl", - "fs/mqueue/ctl": "ctl", - "net.ctl": "ctl", - "net/ctl": "ctl", - "kernel.msgmax": "ctl", - "kernel/msgmax": "ctl", + "fs.mqueue.ctl": "ctl", + "fs/mqueue/ctl": "ctl", + "net.ctl": "ctl", + "net/ctl": "ctl", + "net.ipv4.conf.eno2/100.rp_filter": "ctl", + "kernel.msgmax": "ctl", + "kernel/msgmax": "ctl", } for k, v := range sysctl {