Merge remote-tracking branch 'upstream/master' into overlord-state-lock-unlock

This commit is contained in:
Samuele Pedroni
2016-03-02 15:04:14 +01:00
14 changed files with 144 additions and 143 deletions

View File

@@ -49,7 +49,7 @@ type Daemon struct {
router *mux.Router
asserts *asserts.Database
interfaces *interfaces.Repository
// enableInternalInterfaceActions controls if adding and removing skills and slots is allowed.
// enableInternalInterfaceActions controls if adding and removing slots and plugs is allowed.
enableInternalInterfaceActions bool
}

View File

@@ -44,7 +44,7 @@ The following keys are optional:
`on-abnormal`, `on-abort`, and `always`. See `systemd.service(5)`
(search for `Restart=`) for details.
* `poststop`: (optional) a command that runs after the service has stopped
* `uses`: a list of `skill` names that the app uses
* `slots`: a map of interfaces
* `ports`: (optional) define what ports the service will work
* `internal`: the ports the service is going to connect to
* `tagname`: a free form name
@@ -75,11 +75,11 @@ The following keys are optional:
be specified with `listen-stream`. This option is
reserved for future use.
* `uses`: a map of names and skills
* `slots`: a map of interfaces
## Skills
## Interfaces
The `migration-skill` is used to make porting existing snaps easier.
The `old-security` interface is used to make porting existing snaps easier.
It provides the following parameters:
* `caps`: (optional) list of additional security policies to add.
See `security.md` for details

View File

@@ -622,7 +622,7 @@ returned assertions, 0 or more.
* Description: Get all the plugs and information about the slots connected to them.
* Access: authenticated
* Operation: sync
* Return: array of plugs containing array of slots using each skill.
* Return: array of plugs containing array of slots using each interface.
Sample result:

View File

@@ -1,12 +1,12 @@
# Security policy
Most of the security aspects of the system will be done via skills and
skill slots. However for compatibility with the 15.04 snappy
architecture there is a special skill type called `migration-skill`
Most of the security aspects of the system will be done via interfaces,
slots and plugs. However for compatibility with the 15.04 snappy
architecture there is a special interface called `old-security`
that can be used to migrate using the 15.04 syntax. See the example
below for the various ways the migration-skill can be used.
below for the various ways the `old-security` interface can be used.
## Security with the migration skill
## Security with the old-security interface
Snap packages run confined under a restrictive security sandbox by default.
The security policies and store policies work together to allow developers to
@@ -80,7 +80,8 @@ in the yaml as `caps`.
## Defining snap policy
The `snap.yaml` need not specify anything for default confinement. Several
options are available in the migration-skill to modify the confinement:
options are available in the `old-security` interface to modify the
confinement:
* `caps`: (optional) list of (easy to understand, human readable) additional
security policies to add. The system will translate these to generate
@@ -121,41 +122,41 @@ Eg, consider the following:
command: bar
baz:
command: baz
uses: [baz-caps]
slots: [baz-caps]
qux:
command: qux
uses: [qux-security]
slots: [qux-security]
quux:
command: quux
uses: [quux-policy]
slots: [quux-policy]
corge:
command: corge
daemon: simple
uses: [corge-override]
slots: [corge-override]
cli-exe:
command: cli-exe
uses: [no-caps]
uses:
slots: [no-caps]
slots:
baz-caps:
type: migration-skill
type: old-security
caps:
- network-client
- norf-framework_client
qux-security:
type: migration-skill
type: old-security
security-template: nondefault
quux-policy:
type: migration-skill
type: old-security
security-policy:
apparmor: meta/quux.profile
seccomp: meta/quux.filter
corge-override:
type: migration-skill
type: old-security
security-override:
apparmor: meta/corge.apparmor
seccomp: meta/corge.seccomp
no-caps:
type: migration-skill
type: old-security
caps: []

View File

@@ -80,13 +80,13 @@ func verifyAppYaml(app *AppYaml) error {
return verifyStructStringsAgainstWhitelist(*app, servicesBinariesStringsWhitelist)
}
func verifyUsesYaml(uses *usesYaml) error {
if err := verifyStructStringsAgainstWhitelist(*uses, servicesBinariesStringsWhitelist); err != nil {
func verifySlotYaml(slot *slotYaml) error {
if err := verifyStructStringsAgainstWhitelist(*slot, servicesBinariesStringsWhitelist); err != nil {
return err
}
if uses.Type != "migration-skill" {
return fmt.Errorf("can not use skill %q, only migration-skill supported", uses.Type)
if slot.Interface != "old-security" {
return fmt.Errorf("can not use interface %q, only `old-security` supported", slot.Interface)
}
return nil

View File

@@ -1008,13 +1008,13 @@ func (s *SnapTestSuite) TestBinariesWhitelistSimple(c *C) {
}
func (s *SnapTestSuite) TestUsesWhitelistSimple(c *C) {
c.Check(verifyUsesYaml(&usesYaml{
Type: "migration-skill",
c.Check(verifySlotYaml(&slotYaml{
Interface: "old-security",
SecurityDefinitions: SecurityDefinitions{
SecurityTemplate: "foo"},
}), IsNil)
c.Check(verifyUsesYaml(&usesYaml{
Type: "migration-skill",
c.Check(verifySlotYaml(&slotYaml{
Interface: "old-security",
SecurityDefinitions: SecurityDefinitions{
SecurityPolicy: &SecurityPolicyDefinition{
AppArmor: "foo"},
@@ -1029,19 +1029,19 @@ func (s *SnapTestSuite) TestBinariesWhitelistIllegal(c *C) {
}
func (s *SnapTestSuite) TestWrongType(c *C) {
c.Check(verifyUsesYaml(&usesYaml{
Type: "some-skill",
}), ErrorMatches, ".*can not use skill.* only migration-skill supported")
c.Check(verifySlotYaml(&slotYaml{
Interface: "some-interface",
}), ErrorMatches, ".*can not use interface.* only `old-security` supported")
}
func (s *SnapTestSuite) TestUsesWhitelistIllegal(c *C) {
c.Check(verifyUsesYaml(&usesYaml{
Type: "migration-skill",
c.Check(verifySlotYaml(&slotYaml{
Interface: "old-security",
SecurityDefinitions: SecurityDefinitions{
SecurityTemplate: "x\n"},
}), ErrorMatches, ".*contains illegal.*")
c.Check(verifyUsesYaml(&usesYaml{
Type: "migration-skill",
c.Check(verifySlotYaml(&slotYaml{
Interface: "old-security",
SecurityDefinitions: SecurityDefinitions{
SecurityPolicy: &SecurityPolicyDefinition{
AppArmor: "x\n"},

View File

@@ -99,7 +99,7 @@ func gadgetConfig() error {
return errNoSnapToActivate
}
if err := snap.activate(false, pb); err != nil {
logger.Noticef("failed to acitvate %s: %s", FullName(part), err)
logger.Noticef("failed to activate %s: %s", FullName(part), err)
}
}
@@ -150,7 +150,7 @@ func enableSystemSnaps() error {
logger.Noticef("Acitvating %s", FullName(part))
if err := activator.SetActive(part.(*SnapPart), true, pb); err != nil {
// we don't want this to fail for now
logger.Noticef("failed to acitvate %s: %s", FullName(part), err)
logger.Noticef("failed to activate %s: %s", FullName(part), err)
}
}
}

View File

@@ -71,13 +71,13 @@ var (
SecurityCaps: []string{},
}
// TODO This is not actually right. Even if there are skills,
// TODO This is not actually right. Even if there are interfaces,
// we still want to give the snap a default set of allowances,
// such as being able to read and write in its own directories
// and perhaps network access (we're still deciding on that
// one). So the real logic we want here is: give the snap a
// default set of permissions, and then whatever else the
// skills permit (migration or not). This is coming soon.
// interfaces permit (migration or not). This is coming soon.
defaultSecurityPolicy = &SecurityDefinitions{
SecurityCaps: []string{},
}
@@ -742,19 +742,19 @@ func hasConfig(baseDir string) bool {
return helpers.FileExists(filepath.Join(baseDir, "meta", "hooks", "config"))
}
func findSkillForApp(m *snapYaml, app *AppYaml) (*usesYaml, error) {
if len(app.UsesRef) == 0 {
func findSlotForApp(m *snapYaml, app *AppYaml) (*slotYaml, error) {
if len(app.SlotsRef) == 0 {
return nil, nil
}
if len(app.UsesRef) != 1 {
return nil, fmt.Errorf("only a single skill is supported, %d found", len(app.UsesRef))
if len(app.SlotsRef) != 1 {
return nil, fmt.Errorf("only a single slot is supported, %d found", len(app.SlotsRef))
}
skill, ok := m.Uses[app.UsesRef[0]]
slot, ok := m.Slots[app.SlotsRef[0]]
if !ok {
return nil, fmt.Errorf("can not find skill %q", app.UsesRef[0])
return nil, fmt.Errorf("can not find slot %q", app.SlotsRef[0])
}
return skill, nil
return slot, nil
}
func generatePolicy(m *snapYaml, baseDir string) error {
@@ -769,20 +769,20 @@ func generatePolicy(m *snapYaml, baseDir string) error {
}
for _, app := range m.Apps {
skill, err := findSkillForApp(m, app)
slot, err := findSlotForApp(m, app)
if err != nil {
return err
}
// if no skill is specified, use the defaultSecurityPolicy
if skill == nil {
// if no slot is specified, use the defaultSecurityPolicy
if slot == nil {
if err = defaultSecurityPolicy.generatePolicyForServiceBinary(m, app.Name, baseDir); err != nil {
logger.Noticef("Failed to generate policy for app %s: %v", app.Name, err)
}
continue
}
err = skill.generatePolicyForServiceBinary(m, app.Name, baseDir)
err = slot.generatePolicyForServiceBinary(m, app.Name, baseDir)
if err != nil {
foundError = err
logger.Noticef("Failed to generate policy for service %s: %v", app.Name, err)
@@ -872,15 +872,15 @@ func CompareGeneratePolicyFromFile(fn string) error {
baseDir := filepath.Dir(filepath.Dir(fn))
for _, app := range m.Apps {
skill, err := findSkillForApp(m, app)
slot, err := findSlotForApp(m, app)
if err != nil {
return err
}
if skill == nil {
if slot == nil {
continue
}
p, err := skill.generatePolicyForServiceBinaryResult(m, app.Name, baseDir)
p, err := slot.generatePolicyForServiceBinaryResult(m, app.Name, baseDir)
// FIXME: use apparmor_profile -p on both AppArmor profiles
if err != nil {
// FIXME: what to do here?

View File

@@ -603,16 +603,16 @@ vendor: someone
version: 1.0
apps:
binary1:
uses: [binary1]
slots: [binary1]
service1:
uses: [service1]
slots: [service1]
daemon: forking
uses:
slots:
binary1:
type: migration-skill
interface: old-security
caps: []
service1:
type: migration-skill
interface: old-security
caps: []
`
@@ -822,10 +822,10 @@ vendor: someone
version: 1.0
apps:
binary1:
uses: [binary1]
uses:
slots: [binary1]
slots:
binary1:
type: migration-skill
interface: old-security
caps: []
`
@@ -1034,44 +1034,44 @@ version: 123456789
}
func (a *SecurityTestSuite) TestFindSkillForAppEmpty(c *C) {
func (a *SecurityTestSuite) TestFindSlotForAppEmpty(c *C) {
app := &AppYaml{}
m := &snapYaml{}
skill, err := findSkillForApp(m, app)
slot, err := findSlotForApp(m, app)
c.Check(err, IsNil)
c.Check(skill, IsNil)
c.Check(slot, IsNil)
}
func (a *SecurityTestSuite) TestFindSkillForAppTooMany(c *C) {
func (a *SecurityTestSuite) TestFindSlotlForAppTooMany(c *C) {
app := &AppYaml{
UsesRef: []string{"one", "two"},
SlotsRef: []string{"one", "two"},
}
m := &snapYaml{}
skill, err := findSkillForApp(m, app)
c.Check(skill, IsNil)
c.Check(err, ErrorMatches, "only a single skill is supported, 2 found")
slot, err := findSlotForApp(m, app)
c.Check(slot, IsNil)
c.Check(err, ErrorMatches, "only a single slot is supported, 2 found")
}
func (a *SecurityTestSuite) TestFindSkillForAppNotFound(c *C) {
func (a *SecurityTestSuite) TestFindSlotForAppNotFound(c *C) {
app := &AppYaml{
UsesRef: []string{"not-there"},
SlotsRef: []string{"not-there"},
}
m := &snapYaml{}
skill, err := findSkillForApp(m, app)
c.Check(skill, IsNil)
c.Check(err, ErrorMatches, `can not find skill "not-there"`)
slot, err := findSlotForApp(m, app)
c.Check(slot, IsNil)
c.Check(err, ErrorMatches, `can not find slot "not-there"`)
}
func (a *SecurityTestSuite) TestFindSkillFinds(c *C) {
func (a *SecurityTestSuite) TestFindSlotFinds(c *C) {
app := &AppYaml{
UsesRef: []string{"skill"},
SlotsRef: []string{"slot"},
}
m := &snapYaml{
Uses: map[string]*usesYaml{
"skill": &usesYaml{Type: "some-type"},
Slots: map[string]*slotYaml{
"slot": &slotYaml{Interface: "some-type"},
},
}
skill, err := findSkillForApp(m, app)
slot, err := findSlotForApp(m, app)
c.Check(err, IsNil)
c.Check(skill.Type, Equals, "some-type")
c.Check(slot.Interface, Equals, "some-type")
}

View File

@@ -426,18 +426,18 @@ func (s *SnapPart) CanInstall(allowGadget bool, inter interacter) error {
func (s *SnapPart) RequestSecurityPolicyUpdate(policies, templates map[string]bool) error {
var foundError error
for name, app := range s.Apps() {
skill, err := findSkillForApp(s.m, app)
slot, err := findSlotForApp(s.m, app)
if err != nil {
logger.Noticef("Failed to find skill for %s: %v", name, err)
logger.Noticef("Failed to find slot for %s: %v", name, err)
foundError = err
continue
}
if skill == nil {
if slot == nil {
continue
}
if skill.NeedsAppArmorUpdate(policies, templates) {
err := skill.generatePolicyForServiceBinary(s.m, name, s.basedir)
if slot.NeedsAppArmorUpdate(policies, templates) {
err := slot.generatePolicyForServiceBinary(s.m, name, s.basedir)
if err != nil {
logger.Noticef("Failed to regenerate policy for %s: %v", name, err)
foundError = err

View File

@@ -82,12 +82,12 @@ type AppYaml struct {
// must be a pointer so that it can be "nil" and omitempty works
Ports *Ports `yaml:"ports,omitempty" json:"ports,omitempty"`
OffersRef []string `yaml:"offers"`
UsesRef []string `yaml:"uses"`
PlugsRef []string `yaml:"plugs"`
SlotsRef []string `yaml:"slots"`
}
type usesYaml struct {
Type string `yaml:"type"`
type slotYaml struct {
Interface string `yaml:"interface"`
SecurityDefinitions `yaml:",inline"`
}
@@ -111,8 +111,8 @@ type snapYaml struct {
// Apps can be both binary or service
Apps map[string]*AppYaml `yaml:"apps,omitempty"`
// Uses maps the used "skills" to the apps
Uses map[string]*usesYaml `yaml:"uses,omitempty"`
// Slots maps the used "interfaces" to the apps
Slots map[string]*slotYaml `yaml:"slots,omitempty"`
// FIXME: clarify those
@@ -169,9 +169,9 @@ func validateSnapYamlData(file string, yamlData []byte, m *snapYaml) error {
}
}
// check for "uses"
for _, uses := range m.Uses {
if err := verifyUsesYaml(uses); err != nil {
// check for "slots"
for _, slots := range m.Slots {
if err := verifySlotYaml(slots); err != nil {
return err
}
}
@@ -197,9 +197,9 @@ func parseSnapYamlData(yamlData []byte, hasConfig bool) (*snapYaml, error) {
app.Name = name
}
for name, uses := range m.Uses {
if uses.Type == "" {
uses.Type = name
for name, slot := range m.Slots {
if slot.Interface == "" {
slot.Interface = name
}
}

View File

@@ -31,11 +31,11 @@ var _ = Suite(&snapYamlTestSuite{})
func (s *snapYamlTestSuite) TestParseYamlSetsTypeInUsesFromName(c *C) {
snapYaml := []byte(`name: foo
version: 1.0
uses:
migration-skill:
slots:
old-security:
caps: []
`)
sy, err := parseSnapYamlData(snapYaml, false)
c.Assert(err, IsNil)
sy.Uses["migration-skill"].Type = "migration-skill"
sy.Slots["old-security"].Interface = "old-security"
}

View File

@@ -376,11 +376,11 @@ version: 1.10
apps:
some-binary:
command: some-binary
uses: [some-binary]
slots: [some-binary]
uses:
slots:
some-binary:
type: migration-skill
interface: old-security
security-template: not-there
`)
// install but our missing security-template will break the install

View File

@@ -985,29 +985,29 @@ apps:
testme:
command: bin/testme
description: "testme client"
uses: [testme]
slots: [testme]
testme-override:
command: bin/testme-override
uses: [testme-override]
slots: [testme-override]
testme-policy:
command: bin/testme-policy
uses: [testme-policy]
slots: [testme-policy]
uses:
slots:
testme:
type: migration-skill
interface: old-security
caps:
- "foo_group"
security-template: "foo_template"
testme-override:
type: migration-skill
interface: old-security
security-override:
read-paths:
- "/foo"
syscalls:
- "bar"
testme-policy:
type: migration-skill
interface: old-security
security-policy:
apparmor: meta/testme-policy.profile
@@ -1019,20 +1019,20 @@ func (s *SnapTestSuite) TestSnapYamlSecurityBinaryParsing(c *C) {
c.Assert(m.Apps["testme"].Name, Equals, "testme")
c.Assert(m.Apps["testme"].Command, Equals, "bin/testme")
c.Assert(m.Uses["testme"].SecurityCaps, HasLen, 1)
c.Assert(m.Uses["testme"].SecurityCaps[0], Equals, "foo_group")
c.Assert(m.Uses["testme"].SecurityTemplate, Equals, "foo_template")
c.Assert(m.Slots["testme"].SecurityCaps, HasLen, 1)
c.Assert(m.Slots["testme"].SecurityCaps[0], Equals, "foo_group")
c.Assert(m.Slots["testme"].SecurityTemplate, Equals, "foo_template")
c.Assert(m.Apps["testme-override"].Name, Equals, "testme-override")
c.Assert(m.Apps["testme-override"].Command, Equals, "bin/testme-override")
c.Assert(m.Uses["testme-override"].SecurityCaps, HasLen, 0)
c.Assert(m.Uses["testme-override"].SecurityOverride.ReadPaths[0], Equals, "/foo")
c.Assert(m.Uses["testme-override"].SecurityOverride.Syscalls[0], Equals, "bar")
c.Assert(m.Slots["testme-override"].SecurityCaps, HasLen, 0)
c.Assert(m.Slots["testme-override"].SecurityOverride.ReadPaths[0], Equals, "/foo")
c.Assert(m.Slots["testme-override"].SecurityOverride.Syscalls[0], Equals, "bar")
c.Assert(m.Apps["testme-policy"].Name, Equals, "testme-policy")
c.Assert(m.Apps["testme-policy"].Command, Equals, "bin/testme-policy")
c.Assert(m.Uses["testme-policy"].SecurityCaps, HasLen, 0)
c.Assert(m.Uses["testme-policy"].SecurityPolicy.AppArmor, Equals, "meta/testme-policy.profile")
c.Assert(m.Slots["testme-policy"].SecurityCaps, HasLen, 0)
c.Assert(m.Slots["testme-policy"].SecurityPolicy.AppArmor, Equals, "meta/testme-policy.profile")
}
var securityServiceSnapYaml = []byte(`name: test-snap
@@ -1043,11 +1043,11 @@ apps:
daemon: forking
stop: bin/testme-service.stop
description: "testme service"
uses: [testme-service]
slots: [testme-service]
uses:
slots:
testme-service:
type: migration-skill
interface: old-security
caps:
- "network-client"
- "foo_group"
@@ -1061,10 +1061,10 @@ func (s *SnapTestSuite) TestSnapYamlSecurityServiceParsing(c *C) {
c.Assert(m.Apps["testme-service"].Name, Equals, "testme-service")
c.Assert(m.Apps["testme-service"].Command, Equals, "bin/testme-service.start")
c.Assert(m.Apps["testme-service"].Stop, Equals, "bin/testme-service.stop")
c.Assert(m.Uses["testme-service"].SecurityCaps, HasLen, 2)
c.Assert(m.Uses["testme-service"].SecurityCaps[0], Equals, "network-client")
c.Assert(m.Uses["testme-service"].SecurityCaps[1], Equals, "foo_group")
c.Assert(m.Uses["testme-service"].SecurityTemplate, Equals, "foo_template")
c.Assert(m.Slots["testme-service"].SecurityCaps, HasLen, 2)
c.Assert(m.Slots["testme-service"].SecurityCaps[0], Equals, "network-client")
c.Assert(m.Slots["testme-service"].SecurityCaps[1], Equals, "foo_group")
c.Assert(m.Slots["testme-service"].SecurityTemplate, Equals, "foo_template")
}
func (s *SnapTestSuite) TestDetectsAlreadyInstalled(c *C) {
@@ -1272,16 +1272,16 @@ func (s *SnapTestSuite) TestNeedsAppArmorUpdatePolicyAbsent(c *C) {
func (s *SnapTestSuite) TestRequestSecurityPolicyUpdateService(c *C) {
// if one of the services needs updating, it's updated and returned
svc := &AppYaml{
Name: "svc",
UsesRef: []string{"svc"},
Name: "svc",
SlotsRef: []string{"svc"},
}
part := &SnapPart{
m: &snapYaml{
Name: "part",
Apps: map[string]*AppYaml{"svc": svc},
Version: "42",
Uses: map[string]*usesYaml{
"svc": &usesYaml{
Slots: map[string]*slotYaml{
"svc": &slotYaml{
SecurityDefinitions: SecurityDefinitions{SecurityTemplate: "foo"},
},
},
@@ -1296,16 +1296,16 @@ func (s *SnapTestSuite) TestRequestSecurityPolicyUpdateService(c *C) {
func (s *SnapTestSuite) TestRequestSecurityPolicyUpdateBinary(c *C) {
// if one of the binaries needs updating, the part needs updating
bin := &AppYaml{
Name: "echo",
UsesRef: []string{"echo"},
Name: "echo",
SlotsRef: []string{"echo"},
}
part := &SnapPart{
m: &snapYaml{
Name: "part",
Apps: map[string]*AppYaml{"echo": bin},
Version: "42",
Uses: map[string]*usesYaml{
"echo": &usesYaml{
Slots: map[string]*slotYaml{
"echo": &slotYaml{
SecurityDefinitions: SecurityDefinitions{SecurityTemplate: "foo"},
},
},
@@ -1319,12 +1319,12 @@ func (s *SnapTestSuite) TestRequestSecurityPolicyUpdateBinary(c *C) {
func (s *SnapTestSuite) TestRequestSecurityPolicyUpdateNothing(c *C) {
svc := &AppYaml{
Name: "svc",
UsesRef: []string{"svc"},
Name: "svc",
SlotsRef: []string{"svc"},
}
bin := &AppYaml{
Name: "echo",
UsesRef: []string{"echo"},
Name: "echo",
SlotsRef: []string{"echo"},
}
part := &SnapPart{
m: &snapYaml{
@@ -1333,11 +1333,11 @@ func (s *SnapTestSuite) TestRequestSecurityPolicyUpdateNothing(c *C) {
"echo": bin,
},
Version: "42",
Uses: map[string]*usesYaml{
"svc": &usesYaml{
Slots: map[string]*slotYaml{
"svc": &slotYaml{
SecurityDefinitions: SecurityDefinitions{SecurityTemplate: "foo"},
},
"echo": &usesYaml{
"echo": &slotYaml{
SecurityDefinitions: SecurityDefinitions{SecurityTemplate: "foo"},
},
},