Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

interface: add firewall-control from ubuntu-core-security #720

Merged
merged 5 commits into from Mar 24, 2016
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions interfaces/builtin/all.go
Expand Up @@ -25,6 +25,7 @@ import (

var allInterfaces = []interfaces.Interface{
&BoolFileInterface{},
NewFirewallControlInterface(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add a test for this to all_test.go

NewNetworkInterface(),
NewNetworkBindInterface(),
}
Expand Down
1 change: 1 addition & 0 deletions interfaces/builtin/all_test.go
Expand Up @@ -33,6 +33,7 @@ var _ = Suite(&AllSuite{})
func (s *AllSuite) TestInterfaces(c *C) {
all := builtin.Interfaces()
c.Check(all, Contains, &builtin.BoolFileInterface{})
c.Check(all, DeepContains, builtin.NewFirewallControlInterface())
c.Check(all, DeepContains, builtin.NewNetworkInterface())
c.Check(all, DeepContains, builtin.NewNetworkBindInterface())
}
119 changes: 119 additions & 0 deletions interfaces/builtin/firewall_control.go
@@ -0,0 +1,119 @@
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2016 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package builtin

import (
"github.com/ubuntu-core/snappy/interfaces"
)

// http://bazaar.launchpad.net/~ubuntu-security/ubuntu-core-security/trunk/view/head:/data/apparmor/policygroups/ubuntu-core/16.04/firewall-control
const firewallControlConnectedPlugAppArmor = `
# Description: Can configure firewall. This is restricted because it gives
# privileged access to networking and should only be used with trusted apps.
# Usage: reserved

#include <abstractions/nameservice>

capability net_admin,

/{,usr/}{,s}bin/iptables{,-save,-restore} ixr,
/{,usr/}{,s}bin/ip6tables{,-save,-restore} ixr,
/{,usr/}{,s}bin/iptables-apply ixr,
/{,usr/}{,s}bin/xtables-multi ixr, # ip[6]tables*

# ping - child profile would be nice but seccomp causes problems with that
/{,usr/}{,s}bin/ping ixr,
/{,usr/}{,s}bin/ping6 ixr,
capability net_raw,
capability setuid,
network inet raw,
network inet6 raw,

# iptables (note, we don't want to allow loading modules, but
# we can allow reading @{PROC}/sys/kernel/modprobe). Also,
# snappy needs to have iptable_filter and ip6table_filter loaded,
# they don't autoload.
unix (bind) type=stream addr="@xtables",
@{PROC}/sys/kernel/modprobe r,

@{PROC}/@{pid}/net/ r,
@{PROC}/@{pid}/net/** r,

# sysctl
/{,usr/}{,s}bin/sysctl ixr,
@{PROC}/sys/ r,
@{PROC}/sys/net/ r,
@{PROC}/sys/net/core/ r,
@{PROC}/sys/net/core/** r,
@{PROC}/sys/net/ipv{4,6}/ r,
@{PROC}/sys/net/ipv{4,6}/** r,
@{PROC}/sys/net/netfilter/ r,
@{PROC}/sys/net/netfilter/** r,
@{PROC}/sys/net/nf_conntrack_max r,

# various firewall related sysctl files
@{PROC}/sys/net/ipv4/conf/*/rp_filter w,
@{PROC}/sys/net/ipv{4,6}/conf/*/accept_source_route w,
@{PROC}/sys/net/ipv{4,6}/conf/*/accept_redirects w,
@{PROC}/sys/net/ipv4/icmp_echo_ignore_broadcasts w,
@{PROC}/sys/net/ipv4/icmp_ignore_bogus_error_responses w,
@{PROC}/sys/net/ipv4/icmp_echo_ignore_all w,
@{PROC}/sys/net/ipv4/ip_forward w,
@{PROC}/sys/net/ipv4/conf/*/log_martians w,
@{PROC}/sys/net/ipv4/tcp_syncookies w,
@{PROC}/sys/net/ipv6/conf/*/forwarding w,
`

// http://bazaar.launchpad.net/~ubuntu-security/ubuntu-core-security/trunk/view/head:/data/seccomp/policygroups/ubuntu-core/16.04/firewall-control
const firewallControlConnectedPlugSecComp = `
# Description: Can configure firewall. This is restricted because it gives
# privileged access to networking and should only be used with trusted apps.
# Usage: reserved

# for connecting to xtables abstract socket
bind
connect
getsockname
getsockopt
recv
recvfrom
recvmsg
recvmmsg
send
sendmmsg
sendmsg
sendto
setsockopt
socket

# for ping and ping6
capset
setuid
`

// NewFirewallControlInterface returns a new "firewall-control" interface.
func NewFirewallControlInterface() interfaces.Interface {
return &commonInterface{
name: "firewall-control",
connectedPlugAppArmor: firewallControlConnectedPlugAppArmor,
connectedPlugSecComp: firewallControlConnectedPlugSecComp,
reservedForOS: true,
}
}
128 changes: 128 additions & 0 deletions interfaces/builtin/firewall_control_test.go
@@ -0,0 +1,128 @@
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2016 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package builtin_test

import (
. "gopkg.in/check.v1"

"github.com/ubuntu-core/snappy/interfaces"
"github.com/ubuntu-core/snappy/interfaces/builtin"
"github.com/ubuntu-core/snappy/snap"
)

type FirewallControlInterfaceSuite struct {
iface interfaces.Interface
slot *interfaces.Slot
plug *interfaces.Plug
}

var _ = Suite(&FirewallControlInterfaceSuite{
iface: builtin.NewFirewallControlInterface(),
slot: &interfaces.Slot{
SlotInfo: &snap.SlotInfo{
Snap: &snap.Info{Name: "ubuntu-core"},
Name: "firewall-control",
Interface: "firewall-control",
},
},
plug: &interfaces.Plug{
PlugInfo: &snap.PlugInfo{
Snap: &snap.Info{Name: "other"},
Name: "firewall-control",
Interface: "firewall-control",
},
},
})

func (s *FirewallControlInterfaceSuite) TestName(c *C) {
c.Assert(s.iface.Name(), Equals, "firewall-control")
}

func (s *FirewallControlInterfaceSuite) TestSanitizeSlot(c *C) {
err := s.iface.SanitizeSlot(s.slot)
c.Assert(err, IsNil)
err = s.iface.SanitizeSlot(&interfaces.Slot{SlotInfo: &snap.SlotInfo{
Snap: &snap.Info{Name: "some-snap"},
Name: "firewall-control",
Interface: "firewall-control",
}})
c.Assert(err, ErrorMatches, "firewall-control slots are reserved for the operating system snap")
}

func (s *FirewallControlInterfaceSuite) TestSanitizePlug(c *C) {
err := s.iface.SanitizePlug(s.plug)
c.Assert(err, IsNil)
}

func (s *FirewallControlInterfaceSuite) TestSanitizeIncorrectInterface(c *C) {
c.Assert(func() { s.iface.SanitizeSlot(&interfaces.Slot{SlotInfo: &snap.SlotInfo{Interface: "other"}}) },
PanicMatches, `slot is not of interface "firewall-control"`)
c.Assert(func() { s.iface.SanitizePlug(&interfaces.Plug{PlugInfo: &snap.PlugInfo{Interface: "other"}}) },
PanicMatches, `plug is not of interface "firewall-control"`)
}

func (s *FirewallControlInterfaceSuite) TestUnusedSecuritySystems(c *C) {
systems := [...]interfaces.SecuritySystem{interfaces.SecurityAppArmor,
interfaces.SecuritySecComp, interfaces.SecurityDBus,
interfaces.SecurityUDev}
for _, system := range systems {
snippet, err := s.iface.PermanentPlugSnippet(s.plug, system)
c.Assert(err, IsNil)
c.Assert(snippet, IsNil)
snippet, err = s.iface.PermanentSlotSnippet(s.slot, system)
c.Assert(err, IsNil)
c.Assert(snippet, IsNil)
snippet, err = s.iface.ConnectedSlotSnippet(s.plug, s.slot, system)
c.Assert(err, IsNil)
c.Assert(snippet, IsNil)
}
snippet, err := s.iface.ConnectedPlugSnippet(s.plug, s.slot, interfaces.SecurityDBus)
c.Assert(err, IsNil)
c.Assert(snippet, IsNil)
snippet, err = s.iface.ConnectedPlugSnippet(s.plug, s.slot, interfaces.SecurityUDev)
c.Assert(err, IsNil)
c.Assert(snippet, IsNil)
}

func (s *FirewallControlInterfaceSuite) TestUsedSecuritySystems(c *C) {
// connected plugs have a non-nil security snippet for apparmor
snippet, err := s.iface.ConnectedPlugSnippet(s.plug, s.slot, interfaces.SecurityAppArmor)
c.Assert(err, IsNil)
c.Assert(snippet, Not(IsNil))
// connected plugs have a non-nil security snippet for seccomp
snippet, err = s.iface.ConnectedPlugSnippet(s.plug, s.slot, interfaces.SecuritySecComp)
c.Assert(err, IsNil)
c.Assert(snippet, Not(IsNil))
}

func (s *FirewallControlInterfaceSuite) TestUnexpectedSecuritySystems(c *C) {
snippet, err := s.iface.PermanentPlugSnippet(s.plug, "foo")
c.Assert(err, Equals, interfaces.ErrUnknownSecurity)
c.Assert(snippet, IsNil)
snippet, err = s.iface.ConnectedPlugSnippet(s.plug, s.slot, "foo")
c.Assert(err, Equals, interfaces.ErrUnknownSecurity)
c.Assert(snippet, IsNil)
snippet, err = s.iface.PermanentSlotSnippet(s.slot, "foo")
c.Assert(err, Equals, interfaces.ErrUnknownSecurity)
c.Assert(snippet, IsNil)
snippet, err = s.iface.ConnectedSlotSnippet(s.plug, s.slot, "foo")
c.Assert(err, Equals, interfaces.ErrUnknownSecurity)
c.Assert(snippet, IsNil)
}