From 86b9c72af102de7d91d5440970abab57694a473f Mon Sep 17 00:00:00 2001 From: Luca Bruno Date: Wed, 17 Aug 2016 15:12:31 +0000 Subject: [PATCH 1/2] spec: add os/unix/sysctl isolator This commit introduces an "os/unix/sysctl" isolator, which applies at pod-level. --- examples/pod_runtime.json | 6 ++++++ spec/ace.md | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/examples/pod_runtime.json b/examples/pod_runtime.json index 14076105..658dba14 100644 --- a/examples/pod_runtime.json +++ b/examples/pod_runtime.json @@ -98,6 +98,12 @@ { "name": "resource/memory", "value": {"limit": "4G"} + }, + { + "name": "os/unix/sysctl", + "value": { + "net.ipv4.tcp_fin_timeout": "6" + } } ], "annotations": [ diff --git a/spec/ace.md b/spec/ace.md index 87712a62..e05dab66 100644 --- a/spec/ace.md +++ b/spec/ace.md @@ -383,6 +383,36 @@ Small quantities can be represented directly as decimals (e.g., 0.3), or using m **NOTE**: Network limits MUST NOT apply to localhost communication between apps in a pod. +### Unix Isolators + +These isolators take care of configuring several settings that are typically available in UNIX-like environments. +This set includes isolators to tweak some standard POSIX features as well as other technologies that have been ported across multiple kernel families. + +#### os/unix/sysctl + +Sysctl parameters allow an application to configure kernel level options for its environment. +A number of these values are namespace-aware as well, which makes them a natural fit for for an ACE to configure. + +* Scope: pod + +**Parameters:** + +* dictionary of `sysctl(8)` key-value entries to set kernel runtime parameters. + +```json +"name": "os/unix/sysctl", +"value": { + "net.ipv4.tcp_mtu_probing": "1", + "net.ipv4.tcp_keepalive_time": "300", + "net.ipv4.tcp_keepalive_probes": "5", + "net.ipv4.tcp_keepalive_intvl": "15" +} +``` +**Notes**: + 1. Only a single `os/unix/sysctl` isolator can be specified per-pod. + 2. Implementations SHOULD validate keys before applying sysctl parameters. + For example, a Linux implementation may want to only allow entries related to a specific namespaced subtree like `net.*`. + In such case, implementations MUST either ignore forbidden parameters or refuse to run the pod alltogether. ## App Container Metadata Service From a00638cd6560742af7acdacc6d8de62f15522a70 Mon Sep 17 00:00:00 2001 From: Luca Bruno Date: Wed, 17 Aug 2016 15:13:58 +0000 Subject: [PATCH 2/2] schema/types: implement sysctl isolator --- schema/types/isolator_test.go | 44 +++++++++++++--- schema/types/isolator_unix.go | 83 ++++++++++++++++++++++++++++++ schema/types/isolator_unix_test.go | 57 ++++++++++++++++++++ 3 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 schema/types/isolator_unix.go create mode 100644 schema/types/isolator_unix_test.go diff --git a/schema/types/isolator_test.go b/schema/types/isolator_test.go index e7be9e7c..3a317e95 100644 --- a/schema/types/isolator_test.go +++ b/schema/types/isolator_test.go @@ -172,6 +172,27 @@ func TestIsolatorUnmarshal(t *testing.T) { }`, true, }, + { + `{ + "name": "os/unix/sysctl", + "value": {"net.ipv4.tcp_rfc1337": "1"} + }`, + false, + }, + { + `{ + "name": "os/unix/sysctl", + "value": {"net.ipv4.tcp_rfc1337": 1} + }`, + true, + }, + { + `{ + "name": "os/unix/sysctl", + "value": {["net.ipv4.tcp_rfc1337"]} + }`, + true, + }, } for i, tt := range tests { @@ -205,6 +226,10 @@ func TestIsolatorsGetByName(t *testing.T) { { "name": "os/linux/no-new-privileges", "value": true + }, + { + "name": "os/unix/sysctl", + "value": {"net.ipv4.tcp_rfc1337": "1"} } ] ` @@ -215,12 +240,14 @@ func TestIsolatorsGetByName(t *testing.T) { wrequest int64 wset []LinuxCapability wprivs LinuxNoNewPrivileges + wsysctl UnixSysctl }{ - {"resource/cpu", 1, 30, nil, false}, - {"resource/memory", 2147483648, 1000000000, nil, false}, - {"os/linux/capabilities-retain-set", 0, 0, []LinuxCapability{"CAP_KILL"}, false}, - {"os/linux/capabilities-remove-set", 0, 0, []LinuxCapability{"CAP_KILL"}, false}, - {"os/linux/no-new-privileges", 0, 0, nil, LinuxNoNewPrivileges(true)}, + {"resource/cpu", 1, 30, nil, false, nil}, + {"resource/memory", 2147483648, 1000000000, nil, false, nil}, + {"os/linux/capabilities-retain-set", 0, 0, []LinuxCapability{"CAP_KILL"}, false, nil}, + {"os/linux/capabilities-remove-set", 0, 0, []LinuxCapability{"CAP_KILL"}, false, nil}, + {"os/linux/no-new-privileges", 0, 0, nil, LinuxNoNewPrivileges(true), nil}, + {"os/unix/sysctl", 0, 0, nil, false, UnixSysctl{"net.ipv4.tcp_rfc1337": "1"}}, } var is Isolators @@ -269,8 +296,13 @@ func TestIsolatorsGetByName(t *testing.T) { t.Errorf("#%d: got %v, want %v", i, v, tt.wprivs) } + case *UnixSysctl: + if !reflect.DeepEqual(*v, tt.wsysctl) { + t.Errorf("#%d: got %v, want %v", i, *v, tt.wsysctl) + } + default: - panic("unexecpected type") + panic("unexpected type") } } } diff --git a/schema/types/isolator_unix.go b/schema/types/isolator_unix.go new file mode 100644 index 00000000..ac0111a7 --- /dev/null +++ b/schema/types/isolator_unix.go @@ -0,0 +1,83 @@ +// Copyright 2016 The appc Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "encoding/json" +) + +var ( + UnixIsolatorNames = make(map[ACIdentifier]struct{}) +) + +const ( + //TODO(lucab): add "ulimit" isolators + UnixSysctlName = "os/unix/sysctl" +) + +func init() { + for name, con := range map[ACIdentifier]IsolatorValueConstructor{ + UnixSysctlName: func() IsolatorValue { return &UnixSysctl{} }, + } { + AddIsolatorName(name, UnixIsolatorNames) + AddIsolatorValueConstructor(name, con) + } +} + +type UnixSysctl map[string]string + +func (s *UnixSysctl) UnmarshalJSON(b []byte) error { + var v map[string]string + err := json.Unmarshal(b, &v) + if err != nil { + return err + } + *s = UnixSysctl(v) + return err +} + +func (s UnixSysctl) AssertValid() error { + return nil +} + +func (s UnixSysctl) multipleAllowed() bool { + return false +} +func (s UnixSysctl) Conflicts() []ACIdentifier { + return nil +} + +func (s UnixSysctl) AsIsolator() Isolator { + isol := isolatorMap[UnixSysctlName]() + + b, err := json.Marshal(s) + if err != nil { + panic(err) + } + valRaw := json.RawMessage(b) + return Isolator{ + Name: UnixSysctlName, + ValueRaw: &valRaw, + value: isol, + } +} + +func NewUnixSysctlIsolator(cfg map[string]string) (*UnixSysctl, error) { + s := UnixSysctl(cfg) + if err := s.AssertValid(); err != nil { + return nil, err + } + return &s, nil +} diff --git a/schema/types/isolator_unix_test.go b/schema/types/isolator_unix_test.go new file mode 100644 index 00000000..55e7e1e0 --- /dev/null +++ b/schema/types/isolator_unix_test.go @@ -0,0 +1,57 @@ +// Copyright 2016 The appc Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "reflect" + "testing" +) + +func TestUnixSysctlIsolator(t *testing.T) { + tests := []struct { + inCfg map[string]string + + expectedErr bool + expectedRes UnixSysctl + }{ + // empty isolator - valid + { + make(map[string]string), + + false, + UnixSysctl{}, + }, + // simple isolator - valid + { + map[string]string{ + "foo": "bar", + }, + + false, + UnixSysctl{ + "foo": "bar", + }, + }, + } + for i, tt := range tests { + gotRes, err := NewUnixSysctlIsolator(tt.inCfg) + if gotErr := err != nil; gotErr != tt.expectedErr { + t.Errorf("#%d: want err=%t, got %t (err=%v)", i, tt.expectedErr, gotErr, err) + } + if !reflect.DeepEqual(tt.expectedRes, *gotRes) { + t.Errorf("#%d: want %s, got %s", i, tt.expectedRes, *gotRes) + } + } +}