Skip to content
This repository has been archived by the owner on Aug 14, 2020. It is now read-only.

Commit

Permalink
ace: initial seccomp proposal
Browse files Browse the repository at this point in the history
  • Loading branch information
lucab committed May 31, 2016
1 parent baa3e9c commit c783643
Show file tree
Hide file tree
Showing 5 changed files with 358 additions and 3 deletions.
56 changes: 54 additions & 2 deletions actool/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ var (
patchMounts string
patchPorts string
patchIsolators string
patchSeccompMode string
patchSeccompSet string

catPrettyPrint bool

Expand All @@ -69,6 +71,8 @@ var (
[--ports=query,protocol=tcp,port=8080[:query2,...]]
[--supplementary-groups=gid1,gid2,...]
[--isolators=resource/cpu,request=50m,limit=100m[:resource/memory,...]]
[--seccomp-mode=remove|retain[,errno=EPERM]]
[--seccomp-set=syscall1,syscall2,...]]
[--replace]
INPUT_ACI_FILE
[OUTPUT_ACI_FILE]`,
Expand Down Expand Up @@ -99,6 +103,8 @@ func init() {
cmdPatchManifest.Flags.StringVar(&patchMounts, "mounts", "", "Replace mount points")
cmdPatchManifest.Flags.StringVar(&patchPorts, "ports", "", "Replace ports")
cmdPatchManifest.Flags.StringVar(&patchIsolators, "isolators", "", "Replace isolators")
cmdPatchManifest.Flags.StringVar(&patchSeccompMode, "seccomp-mode", "", "Enable and configure seccomp isolator")
cmdPatchManifest.Flags.StringVar(&patchSeccompSet, "seccomp-set", "", "Set of syscalls for seccomp isolator enforcing")

cmdCatManifest.Flags.BoolVar(&catPrettyPrint, "pretty-print", false, "Print with better style")
}
Expand Down Expand Up @@ -250,6 +256,48 @@ func patchManifest(im *schema.ImageManifest) error {
}
}

if patchSeccompMode != "" {
var errno, mode string
retainIsolator := app.Isolators.GetByName(types.LinuxSeccompRetainSetName)
removeIsolator := app.Isolators.GetByName(types.LinuxSeccompRemoveSetName)
if removeIsolator != nil || retainIsolator != nil {
return fmt.Errorf("a seccomp isolator already exists")
}

args := strings.Split(patchSeccompMode, ",")
for _, a := range args {
kv := strings.Split(a, "=")
switch len(kv) {
case 1:
// mode, either "remove" or "retain"
mode = kv[0]
case 2:
// k=v argument, only "errno" allowed for now
if kv[0] == "errno" {
errno = kv[1]
}
default:
return fmt.Errorf("cannot parse seccomp-mode argument: %s", a)
}
}

// Instantiate a Isolator with the content specified by the --seccomp-set parameter.
var err error
var seccomp types.AsIsolator
switch mode {
case "remove":
seccomp, err = types.NewLinuxSeccompRemoveSet(errno, strings.Split(patchSeccompSet, ",")...)
case "retain":
seccomp, err = types.NewLinuxSeccompRetainSet(errno, strings.Split(patchSeccompSet, ",")...)
default:
err = fmt.Errorf("Unknown seccomp mode %s", mode)
}
if err != nil {
return fmt.Errorf("cannot parse seccomp isolator: %s", err)
}
app.Isolators = append(app.Isolators, seccomp.AsIsolator())
}

if patchIsolators != "" {
isolators := strings.Split(patchIsolators, ":")
for _, is := range isolators {
Expand All @@ -260,14 +308,18 @@ func patchManifest(im *schema.ImageManifest) error {

_, ok := types.ResourceIsolatorNames[name]

if name == types.LinuxNoNewPrivilegesName {
switch name {
case types.LinuxNoNewPrivilegesName:
ok = true
kv := strings.Split(is, ",")
if len(kv) != 2 {
return fmt.Errorf("isolator %s: invalid format", name)
}

isolatorStr = fmt.Sprintf(`{ "name": "%s", "value": %s }`, name, kv[1])
case types.LinuxSeccompRemoveSetName:
fallthrough
case types.LinuxSeccompRetainSetName:
ok = false
}

if !ok {
Expand Down
4 changes: 4 additions & 0 deletions examples/image.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@
{
"name": "os/linux/no-new-privileges",
"value": true
},
{
"name": "os/linux/seccomp-remove-set",
"value": {"errno": "EACCESS", "set": ["clock_settime", "clock_adjtime", "reboot"]}
}
],
"mountPoints": [
Expand Down
128 changes: 128 additions & 0 deletions schema/types/isolator_linux_specific.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ package types
import (
"encoding/json"
"errors"
"unicode"
)

const (
LinuxCapabilitiesRetainSetName = "os/linux/capabilities-retain-set"
LinuxCapabilitiesRevokeSetName = "os/linux/capabilities-remove-set"
LinuxNoNewPrivilegesName = "os/linux/no-new-privileges"
LinuxSeccompRemoveSetName = "os/linux/seccomp-remove-set"
LinuxSeccompRetainSetName = "os/linux/seccomp-retain-set"
)

var LinuxIsolatorNames = make(map[ACIdentifier]struct{})
Expand All @@ -32,6 +35,8 @@ func init() {
LinuxCapabilitiesRevokeSetName: func() IsolatorValue { return &LinuxCapabilitiesRevokeSet{} },
LinuxCapabilitiesRetainSetName: func() IsolatorValue { return &LinuxCapabilitiesRetainSet{} },
LinuxNoNewPrivilegesName: func() IsolatorValue { v := LinuxNoNewPrivileges(false); return &v },
LinuxSeccompRemoveSetName: func() IsolatorValue { return &LinuxSeccompRemoveSet{} },
LinuxSeccompRetainSetName: func() IsolatorValue { return &LinuxSeccompRetainSet{} },
} {
AddIsolatorName(name, LinuxIsolatorNames)
AddIsolatorValueConstructor(name, con)
Expand All @@ -56,6 +61,10 @@ func (l *LinuxNoNewPrivileges) UnmarshalJSON(b []byte) error {
return nil
}

type AsIsolator interface {
AsIsolator() Isolator
}

type LinuxCapabilitiesSet interface {
Set() []LinuxCapability
AssertValid() error
Expand Down Expand Up @@ -161,3 +170,122 @@ func (l LinuxCapabilitiesRevokeSet) AsIsolator() Isolator {
value: &l,
}
}

type LinuxSeccompSet interface {
Set() []LinuxSeccompEntry
Errno() LinuxSeccompErrno
AssertValid() error
}

type LinuxSeccompEntry string
type LinuxSeccompErrno string

type linuxSeccompValue struct {
Set []LinuxSeccompEntry `json:"set"`
Errno LinuxSeccompErrno `json:"errno"`
}

type linuxSeccompBase struct {
val linuxSeccompValue
}

func (l linuxSeccompBase) AssertValid() error {
if l.val.Errno != "" {
for _, c := range l.val.Errno {
if !unicode.IsUpper(c) {
return errors.New("invalid errno")
}
}
}
return nil
}

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

l.val = v

return err
}

func (l linuxSeccompBase) Set() []LinuxSeccompEntry {
return l.val.Set
}

func (l linuxSeccompBase) Errno() LinuxSeccompErrno {
return l.val.Errno
}

type LinuxSeccompRetainSet struct {
linuxSeccompBase
}

func NewLinuxSeccompRetainSet(errno string, syscall ...string) (*LinuxSeccompRetainSet, error) {
l := LinuxSeccompRetainSet{
linuxSeccompBase{
linuxSeccompValue{
make([]LinuxSeccompEntry, len(syscall)),
LinuxSeccompErrno(errno),
},
},
}
for i, c := range syscall {
l.linuxSeccompBase.val.Set[i] = LinuxSeccompEntry(c)
}
if err := l.AssertValid(); err != nil {
return nil, err
}
return &l, nil
}

func (l LinuxSeccompRetainSet) AsIsolator() Isolator {
b, err := json.Marshal(l.linuxSeccompBase.val)
if err != nil {
panic(err)
}
rm := json.RawMessage(b)
return Isolator{
Name: LinuxSeccompRetainSetName,
ValueRaw: &rm,
value: &l,
}
}

type LinuxSeccompRemoveSet struct {
linuxSeccompBase
}

func NewLinuxSeccompRemoveSet(errno string, syscall ...string) (*LinuxSeccompRemoveSet, error) {
l := LinuxSeccompRemoveSet{
linuxSeccompBase{
linuxSeccompValue{
make([]LinuxSeccompEntry, len(syscall)),
LinuxSeccompErrno(errno),
},
},
}
for i, c := range syscall {
l.linuxSeccompBase.val.Set[i] = LinuxSeccompEntry(c)
}
if err := l.AssertValid(); err != nil {
return nil, err
}
return &l, nil
}

func (l LinuxSeccompRemoveSet) AsIsolator() Isolator {
b, err := json.Marshal(l.linuxSeccompBase.val)
if err != nil {
panic(err)
}
rm := json.RawMessage(b)
return Isolator{
Name: LinuxSeccompRemoveSetName,
ValueRaw: &rm,
value: &l,
}
}
110 changes: 110 additions & 0 deletions schema/types/isolator_linux_specific_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,113 @@ func TestNewLinuxCapabilitiesRevokeSet(t *testing.T) {
}

}

func TestNewLinuxSeccompRemoveSet(t *testing.T) {
tests := []struct {
set []string
errno string

wset []LinuxSeccompEntry
werrno LinuxSeccompErrno
werr bool
}{
{
[]string{},
"-EPERM",
nil,
"",
true,
},
{
[]string{"@all"},
"EACCESS",
[]LinuxSeccompEntry{"@all"},
LinuxSeccompErrno("EACCESS"),
false,
},
{
[]string{"chmod", "chown"},
"",
[]LinuxSeccompEntry{"chmod", "chown"},
LinuxSeccompErrno(""),
false,
},
{
[]string{},
"",
[]LinuxSeccompEntry{},
LinuxSeccompErrno(""),
false,
},
}
for i, tt := range tests {
c, err := NewLinuxSeccompRemoveSet(tt.errno, tt.set...)
if tt.werr {
if err == nil {
t.Errorf("#%d: did not get expected error", i)
}
continue
}
if gset := c.Set(); !reflect.DeepEqual(gset, tt.wset) {
t.Errorf("#%d: got set %#v, expected set %#v", i, gset, tt.wset)
}
if gerrno := c.Errno(); !reflect.DeepEqual(gerrno, tt.werrno) {
t.Errorf("#%d: got errno %#v, expected errno %#v", i, gerrno, tt.werrno)
}
}
}

func TestNewLinuxSeccompRetainSet(t *testing.T) {
tests := []struct {
set []string
errno string

wset []LinuxSeccompEntry
werrno LinuxSeccompErrno
werr bool
}{
{
[]string{},
"eaccess",
nil,
"",
true,
},
{
[]string{"chmod"},
"EACCESS",
[]LinuxSeccompEntry{"chmod"},
LinuxSeccompErrno("EACCESS"),
false,
},
{
[]string{"chmod", "chown"},
"",
[]LinuxSeccompEntry{"chmod", "chown"},
LinuxSeccompErrno(""),
false,
},
{
[]string{},
"",
[]LinuxSeccompEntry{},
LinuxSeccompErrno(""),
false,
},
}
for i, tt := range tests {
c, err := NewLinuxSeccompRetainSet(tt.errno, tt.set...)
if tt.werr {
if err == nil {
t.Errorf("#%d: did not get expected error", i)
}
continue
}
if gset := c.Set(); !reflect.DeepEqual(gset, tt.wset) {
t.Errorf("#%d: got set %#v, expected set %#v", i, gset, tt.wset)
}
if gerrno := c.Errno(); !reflect.DeepEqual(gerrno, tt.werrno) {
t.Errorf("#%d: got errno %#v, expected errno %#v", i, gerrno, tt.werrno)
}
}
}
Loading

0 comments on commit c783643

Please sign in to comment.