Skip to content
This repository was archived by the owner on Aug 14, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions schema/types/isolator_linux_specific.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"encoding/json"
"errors"
"fmt"
"strings"
"unicode"
)

Expand All @@ -29,6 +30,7 @@ const (
LinuxSeccompRetainSetName = "os/linux/seccomp-retain-set"
LinuxOOMScoreAdjName = "os/linux/oom-score-adj"
LinuxCPUSharesName = "os/linux/cpu-shares"
LinuxSELinuxContextName = "os/linux/selinux-context"
)

var LinuxIsolatorNames = make(map[ACIdentifier]struct{})
Expand All @@ -42,6 +44,7 @@ func init() {
LinuxCPUSharesName: func() IsolatorValue { v := LinuxCPUShares(1024); return &v },
LinuxSeccompRemoveSetName: func() IsolatorValue { return &LinuxSeccompRemoveSet{} },
LinuxSeccompRetainSetName: func() IsolatorValue { return &LinuxSeccompRetainSet{} },
LinuxSELinuxContextName: func() IsolatorValue { return &LinuxSELinuxContext{} },
} {
AddIsolatorName(name, LinuxIsolatorNames)
AddIsolatorValueConstructor(name, con)
Expand Down Expand Up @@ -430,3 +433,97 @@ func (l LinuxOOMScoreAdj) AsIsolator() Isolator {
value: &l,
}
}

type LinuxSELinuxUser string
type LinuxSELinuxRole string
type LinuxSELinuxType string
type LinuxSELinuxLevel string

type linuxSELinuxValue struct {
User LinuxSELinuxUser `json:"user"`
Role LinuxSELinuxRole `json:"role"`
Type LinuxSELinuxType `json:"type"`
Level LinuxSELinuxLevel `json:"level"`
}

type LinuxSELinuxContext struct {
val linuxSELinuxValue
}

func (l LinuxSELinuxContext) AssertValid() error {
if l.val.User == "" || strings.Contains(string(l.val.User), ":") {
return fmt.Errorf("invalid user value %q", l.val.User)
}
if l.val.Role == "" || strings.Contains(string(l.val.Role), ":") {
return fmt.Errorf("invalid role value %q", l.val.Role)
}
if l.val.Type == "" || strings.Contains(string(l.val.Type), ":") {
return fmt.Errorf("invalid type value %q", l.val.Type)
}
if l.val.Level == "" {
return fmt.Errorf("invalid level value %q", l.val.Level)
}
return nil
}

func (l *LinuxSELinuxContext) UnmarshalJSON(b []byte) error {
var v linuxSELinuxValue
err := json.Unmarshal(b, &v)
if err != nil {
return err
}
l.val = v
return nil
}

func (l LinuxSELinuxContext) User() LinuxSELinuxUser {
return l.val.User
}

func (l LinuxSELinuxContext) Role() LinuxSELinuxRole {
return l.val.Role
}

func (l LinuxSELinuxContext) Type() LinuxSELinuxType {
return l.val.Type
}

func (l LinuxSELinuxContext) Level() LinuxSELinuxLevel {
return l.val.Level
}

func (l LinuxSELinuxContext) multipleAllowed() bool {
return false
}

func (l LinuxSELinuxContext) Conflicts() []ACIdentifier {
return nil
}

func NewLinuxSELinuxContext(selinuxUser, selinuxRole, selinuxType, selinuxLevel string) (*LinuxSELinuxContext, error) {
l := LinuxSELinuxContext{
linuxSELinuxValue{
LinuxSELinuxUser(selinuxUser),
LinuxSELinuxRole(selinuxRole),
LinuxSELinuxType(selinuxType),
LinuxSELinuxLevel(selinuxLevel),
},
}
if err := l.AssertValid(); err != nil {
return nil, err
}
return &l, nil
}

func (l LinuxSELinuxContext) AsIsolator() (*Isolator, error) {
b, err := json.Marshal(l.val)
if err != nil {
return nil, err
}
rm := json.RawMessage(b)
return &Isolator{
Name: LinuxSELinuxContextName,
ValueRaw: &rm,
value: &l,
}, nil
}
70 changes: 70 additions & 0 deletions schema/types/isolator_linux_specific_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,76 @@ func TestNewLinuxCapabilitiesRetainSet(t *testing.T) {

}

func TestNewLinuxSELinuxContext(t *testing.T) {
tests := []struct {
inUser string
inRole string
inType string
inLevel string

wuser LinuxSELinuxUser
wrole LinuxSELinuxRole
wtype LinuxSELinuxType
wlevel LinuxSELinuxLevel
werr bool
}{
{
"unconfined_u", "object_r", "user_home_t", "s0",
LinuxSELinuxUser("unconfined_u"),
LinuxSELinuxRole("object_r"),
LinuxSELinuxType("user_home_t"),
LinuxSELinuxLevel("s0"),
false,
},
{
"unconfined_u", "object_r", "user_home_t", "s0-s0:c0",
LinuxSELinuxUser("unconfined_u"),
LinuxSELinuxRole("object_r"),
LinuxSELinuxType("user_home_t"),
LinuxSELinuxLevel("s0-s0:c0"),
false,
},
{
"", "object_r", "user_home_t", "s0",
"",
"",
"",
"",
true,
},
{
"unconfined_u:unconfined_t", "object_r", "user_home_t", "s0",
"",
"",
"",
"",
true,
},
}
for i, tt := range tests {
c, err := NewLinuxSELinuxContext(tt.inUser, tt.inRole, tt.inType, tt.inLevel)
if tt.werr {
if err == nil {
t.Errorf("#%d: did not get expected error", i)
}
continue
}
if guser := c.User(); !reflect.DeepEqual(guser, tt.wuser) {
t.Errorf("#%d: got user %#v, want user %#v", i, guser, tt.wuser)
}
if grole := c.Role(); !reflect.DeepEqual(grole, tt.wrole) {
t.Errorf("#%d: got role %#v, want role %#v", i, grole, tt.wrole)
}
if gtype := c.Type(); !reflect.DeepEqual(gtype, tt.wtype) {
t.Errorf("#%d: got type %#v, want type %#v", i, gtype, tt.wtype)
}
if glevel := c.Level(); !reflect.DeepEqual(glevel, tt.wlevel) {
t.Errorf("#%d: got level %#v, want level %#v", i, glevel, tt.wlevel)
}
}

}

func TestNewLinuxCapabilitiesRevokeSet(t *testing.T) {
tests := []struct {
in []string
Expand Down
28 changes: 28 additions & 0 deletions schema/types/isolator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,34 @@ func TestIsolatorUnmarshal(t *testing.T) {
}`,
true,
},
{
`{
"name": "os/linux/selinux-context",
"value": {"user" : "user_u", "role": "role_r", "type": "type_r", "level": "s0-s0"}
}`,
false,
},
{
`{
"name": "os/linux/selinux-context",
"value": {"user" : "user_u", "role": "role_r", "type": "type_r", "level": "s0-s0:c0"}
}`,
false,
},
{
`{
"name": "os/linux/selinux-context",
"value": {"user" : "user_u", "role": "role_r:type_t", "type": "type_r", "level": "s0-s0"}
}`,
true,
},
{
`{
"name": "os/linux/selinux-context",
"value": {"user" : "user_u", "role": "", "type": "type_r", "level": "s0-s0"}
}`,
true,
},
}

for i, tt := range tests {
Expand Down
32 changes: 32 additions & 0 deletions spec/ace.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,38 @@ In the example above, the process will not be allowed to invoke `clock_adjtime(2

In the example above, the process will be only allowed to invoke syscalls specified in the custom network-related set (and any other syscall in the implementation-specific whitelist). All other syscalls will result in app termination via `SIGSYS` signal.

#### os/linux/selinux-context

* Scope: app/pod

**Parameters:**

* **user** case-sensitive string containing the user portion of the SELinux security context to be used to label the current pod or application.
* **role** case-sensitive string containing the role portion of the SELinux security context to be used to label the current pod or application.
* **type** case-sensitive string containing the type/domain portion of the SELinux security context to be used to label the current pod or application.
* **level** case-sensitive string containing the level portion of the SELinux security context to be used to label the current pod or application.

**Notes:**
1. Only a single `os/linux/selinux-context` isolator can be specified per-pod.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a context is applied to a pod, does this just mean it's for all apps (unless they have overrides), or also for any executor processes involved in running the pod? (i.e. stage1 for rkt)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also applies to other processes in the pod context. Thanks for highlighting this, I'll make it explicit in the spec.

2. Only a single `os/linux/selinux-context` isolator can be specified per-app.
3. If a SELinux security context is specified at pod level, it applies to all processes involved in running the pod.
4. If specified both at pod and app level, app values override pod ones.
5. Implementations MAY ignore this isolator if the host does not support SELinux labeling.

**Example:**

```json
"name": "os/linux/selinux-context",
"value": {
"user": "system_u",
"role": "system_r",
"type": "dhcpc_t",
"level": "s0",
}
```

In the example above, the SELinux security context `system_u:system_r:dhcpc_t:s0` will be applied to a pod or to a single application, depending on where this isolator is specified.

#### os/linux/capabilities-remove-set

* Scope: app
Expand Down