Update libcontainer to fd6df76562137aa3b18e44b790c
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
56261daf32
commit
7f5ebdcaac
|
@ -66,7 +66,7 @@ if [ "$1" = '--go' ]; then
|
||||||
mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar
|
mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar
|
||||||
fi
|
fi
|
||||||
|
|
||||||
clone git github.com/docker/libcontainer f60d7b9195f8dc0b5d343abbc3293da7c17bb11c
|
clone git github.com/docker/libcontainer fd6df76562137aa3b18e44b790cb484fe2b6fa0b
|
||||||
# see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file)
|
# see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file)
|
||||||
rm -rf src/github.com/docker/libcontainer/vendor
|
rm -rf src/github.com/docker/libcontainer/vendor
|
||||||
eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli')"
|
eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli')"
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// smallFields is a small size data set for benchmarking
|
||||||
|
var smallFields = Fields{
|
||||||
|
"foo": "bar",
|
||||||
|
"baz": "qux",
|
||||||
|
"one": "two",
|
||||||
|
"three": "four",
|
||||||
|
}
|
||||||
|
|
||||||
|
// largeFields is a large size data set for benchmarking
|
||||||
|
var largeFields = Fields{
|
||||||
|
"foo": "bar",
|
||||||
|
"baz": "qux",
|
||||||
|
"one": "two",
|
||||||
|
"three": "four",
|
||||||
|
"five": "six",
|
||||||
|
"seven": "eight",
|
||||||
|
"nine": "ten",
|
||||||
|
"eleven": "twelve",
|
||||||
|
"thirteen": "fourteen",
|
||||||
|
"fifteen": "sixteen",
|
||||||
|
"seventeen": "eighteen",
|
||||||
|
"nineteen": "twenty",
|
||||||
|
"a": "b",
|
||||||
|
"c": "d",
|
||||||
|
"e": "f",
|
||||||
|
"g": "h",
|
||||||
|
"i": "j",
|
||||||
|
"k": "l",
|
||||||
|
"m": "n",
|
||||||
|
"o": "p",
|
||||||
|
"q": "r",
|
||||||
|
"s": "t",
|
||||||
|
"u": "v",
|
||||||
|
"w": "x",
|
||||||
|
"y": "z",
|
||||||
|
"this": "will",
|
||||||
|
"make": "thirty",
|
||||||
|
"entries": "yeah",
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSmallTextFormatter(b *testing.B) {
|
||||||
|
doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkLargeTextFormatter(b *testing.B) {
|
||||||
|
doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSmallColoredTextFormatter(b *testing.B) {
|
||||||
|
doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkLargeColoredTextFormatter(b *testing.B) {
|
||||||
|
doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSmallJSONFormatter(b *testing.B) {
|
||||||
|
doBenchmark(b, &JSONFormatter{}, smallFields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkLargeJSONFormatter(b *testing.B) {
|
||||||
|
doBenchmark(b, &JSONFormatter{}, largeFields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doBenchmark(b *testing.B, formatter Formatter, fields Fields) {
|
||||||
|
entry := &Entry{
|
||||||
|
Time: time.Time{},
|
||||||
|
Level: InfoLevel,
|
||||||
|
Message: "message",
|
||||||
|
Data: fields,
|
||||||
|
}
|
||||||
|
var d []byte
|
||||||
|
var err error
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
d, err = formatter.Format(entry)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
b.SetBytes(int64(len(d)))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Papertrail Hook for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:" />
|
||||||
|
|
||||||
|
[Papertrail](https://papertrailapp.com) provides hosted log management. Once stored in Papertrail, you can [group](http://help.papertrailapp.com/kb/how-it-works/groups/) your logs on various dimensions, [search](http://help.papertrailapp.com/kb/how-it-works/search-syntax) them, and trigger [alerts](http://help.papertrailapp.com/kb/how-it-works/alerts).
|
||||||
|
|
||||||
|
In most deployments, you'll want to send logs to Papertrail via their [remote_syslog](http://help.papertrailapp.com/kb/configuration/configuring-centralized-logging-from-text-log-files-in-unix/) daemon, which requires no application-specific configuration. This hook is intended for relatively low-volume logging, likely in managed cloud hosting deployments where installing `remote_syslog` is not possible.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
You can find your Papertrail UDP port on your [Papertrail account page](https://papertrailapp.com/account/destinations). Substitute it below for `YOUR_PAPERTRAIL_UDP_PORT`.
|
||||||
|
|
||||||
|
For `YOUR_APP_NAME`, substitute a short string that will readily identify your application or service in the logs.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"log/syslog"
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/Sirupsen/logrus/hooks/papertrail"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log := logrus.New()
|
||||||
|
hook, err := logrus_papertrail.NewPapertrailHook("logs.papertrailapp.com", YOUR_PAPERTRAIL_UDP_PORT, YOUR_APP_NAME)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
log.Hooks.Add(hook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -0,0 +1,54 @@
|
||||||
|
package logrus_papertrail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
format = "Jan 2 15:04:05"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PapertrailHook to send logs to a logging service compatible with the Papertrail API.
|
||||||
|
type PapertrailHook struct {
|
||||||
|
Host string
|
||||||
|
Port int
|
||||||
|
AppName string
|
||||||
|
UDPConn net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPapertrailHook creates a hook to be added to an instance of logger.
|
||||||
|
func NewPapertrailHook(host string, port int, appName string) (*PapertrailHook, error) {
|
||||||
|
conn, err := net.Dial("udp", fmt.Sprintf("%s:%d", host, port))
|
||||||
|
return &PapertrailHook{host, port, appName, conn}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire is called when a log event is fired.
|
||||||
|
func (hook *PapertrailHook) Fire(entry *logrus.Entry) error {
|
||||||
|
date := time.Now().Format(format)
|
||||||
|
payload := fmt.Sprintf("<22> %s %s: [%s] %s", date, hook.AppName, entry.Data["level"], entry.Message)
|
||||||
|
|
||||||
|
bytesWritten, err := hook.UDPConn.Write([]byte(payload))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Unable to send log line to Papertrail via UDP. Wrote %d bytes before error: %v", bytesWritten, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Levels returns the available logging levels.
|
||||||
|
func (hook *PapertrailHook) Levels() []logrus.Level {
|
||||||
|
return []logrus.Level{
|
||||||
|
logrus.PanicLevel,
|
||||||
|
logrus.FatalLevel,
|
||||||
|
logrus.ErrorLevel,
|
||||||
|
logrus.WarnLevel,
|
||||||
|
logrus.InfoLevel,
|
||||||
|
logrus.DebugLevel,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package logrus_papertrail
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/stvp/go-udp-testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWritingToUDP(t *testing.T) {
|
||||||
|
port := 16661
|
||||||
|
udp.SetAddr(fmt.Sprintf(":%d", port))
|
||||||
|
|
||||||
|
hook, err := NewPapertrailHook("localhost", port, "test")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unable to connect to local UDP server.")
|
||||||
|
}
|
||||||
|
|
||||||
|
log := logrus.New()
|
||||||
|
log.Hooks.Add(hook)
|
||||||
|
|
||||||
|
udp.ShouldReceive(t, "foo", func() {
|
||||||
|
log.Info("foo")
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
image: dockercore/libcontainer
|
||||||
|
script:
|
||||||
|
# Setup the DockerInDocker environment.
|
||||||
|
- /dind
|
||||||
|
- sed -i 's!docker/docker!docker/libcontainer!' /go/src/github.com/docker/docker/hack/make/.validate
|
||||||
|
- bash /go/src/github.com/docker/docker/hack/make/validate-dco
|
||||||
|
- bash /go/src/github.com/docker/docker/hack/make/validate-gofmt
|
||||||
|
- export GOPATH="$GOPATH:/go:$(pwd)/vendor" # Drone mucks with our GOPATH
|
||||||
|
- make direct-test
|
|
@ -1,36 +0,0 @@
|
||||||
language: go
|
|
||||||
go: 1.3
|
|
||||||
|
|
||||||
# let us have pretty experimental Docker-based Travis workers
|
|
||||||
sudo: false
|
|
||||||
|
|
||||||
env:
|
|
||||||
- TRAVIS_GLOBAL_WTF=1
|
|
||||||
- _GOOS=linux _GOARCH=amd64 CGO_ENABLED=1
|
|
||||||
- _GOOS=linux _GOARCH=amd64 CGO_ENABLED=0
|
|
||||||
# - _GOOS=linux _GOARCH=386 CGO_ENABLED=1 # TODO add this once Travis can handle it (https://github.com/travis-ci/travis-ci/issues/2207#issuecomment-49625061)
|
|
||||||
- _GOOS=linux _GOARCH=386 CGO_ENABLED=0
|
|
||||||
- _GOOS=linux _GOARCH=arm CGO_ENABLED=0
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go get code.google.com/p/go.tools/cmd/cover
|
|
||||||
- mkdir -pv "${GOPATH%%:*}/src/github.com/docker" && [ -d "${GOPATH%%:*}/src/github.com/docker/libcontainer" ] || ln -sv "$(readlink -f .)" "${GOPATH%%:*}/src/github.com/docker/libcontainer"
|
|
||||||
- if [ -z "$TRAVIS_GLOBAL_WTF" ]; then
|
|
||||||
gvm cross "$_GOOS" "$_GOARCH";
|
|
||||||
export GOOS="$_GOOS" GOARCH="$_GOARCH";
|
|
||||||
fi
|
|
||||||
- export GOPATH="$GOPATH:$(pwd)/vendor"
|
|
||||||
- if [ -z "$TRAVIS_GLOBAL_WTF" ]; then go env; fi
|
|
||||||
- go get -d -v ./... # TODO remove this if /docker/docker gets purged from our includes
|
|
||||||
- if [ "$TRAVIS_GLOBAL_WTF" ]; then
|
|
||||||
export DOCKER_PATH="${GOPATH%%:*}/src/github.com/docker/docker";
|
|
||||||
mkdir -p "$DOCKER_PATH/hack/make";
|
|
||||||
( cd "$DOCKER_PATH/hack/make" && wget -c 'https://raw.githubusercontent.com/docker/docker/master/hack/make/'{.validate,validate-dco,validate-gofmt} );
|
|
||||||
sed -i 's!docker/docker!docker/libcontainer!' "$DOCKER_PATH/hack/make/.validate";
|
|
||||||
fi
|
|
||||||
|
|
||||||
script:
|
|
||||||
- if [ "$TRAVIS_GLOBAL_WTF" ]; then bash "$DOCKER_PATH/hack/make/validate-dco"; fi
|
|
||||||
- if [ "$TRAVIS_GLOBAL_WTF" ]; then bash "$DOCKER_PATH/hack/make/validate-gofmt"; fi
|
|
||||||
- if [ -z "$TRAVIS_GLOBAL_WTF" ]; then make direct-build; fi
|
|
||||||
- if [ -z "$TRAVIS_GLOBAL_WTF" -a "$GOARCH" != 'arm' ]; then make direct-test-short; fi
|
|
|
@ -2,5 +2,4 @@ Michael Crosby <michael@docker.com> (@crosbymichael)
|
||||||
Rohit Jnagal <jnagal@google.com> (@rjnagal)
|
Rohit Jnagal <jnagal@google.com> (@rjnagal)
|
||||||
Victor Marmol <vmarmol@google.com> (@vmarmol)
|
Victor Marmol <vmarmol@google.com> (@vmarmol)
|
||||||
Mrunal Patel <mpatel@redhat.com> (@mrunalp)
|
Mrunal Patel <mpatel@redhat.com> (@mrunalp)
|
||||||
.travis.yml: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
|
||||||
update-vendor.sh: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
update-vendor.sh: Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
## libcontainer - reference implementation for containers [![Build Status](https://travis-ci.org/docker/libcontainer.png?branch=master)](https://travis-ci.org/docker/libcontainer)
|
## libcontainer - reference implementation for containers [![Build Status](https://ci.dockerproject.com/github.com/docker/libcontainer/status.svg?branch=master)](https://ci.dockerproject.com/github.com/docker/libcontainer)
|
||||||
|
|
||||||
### Note on API changes:
|
### Note on API changes:
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ func TestGetCgroupParamsInt(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if value != 0 {
|
} else if value != 0 {
|
||||||
t.Fatalf("Expected %d to equal %f", value, 0)
|
t.Fatalf("Expected %d to equal %d", value, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Success with negative values lesser than min int64
|
// Success with negative values lesser than min int64
|
||||||
|
@ -70,7 +70,7 @@ func TestGetCgroupParamsInt(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if value != 0 {
|
} else if value != 0 {
|
||||||
t.Fatalf("Expected %d to equal %f", value, 0)
|
t.Fatalf("Expected %d to equal %d", value, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not a float.
|
// Not a float.
|
||||||
|
|
|
@ -43,6 +43,13 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func newProp(name string, units interface{}) systemd.Property {
|
||||||
|
return systemd.Property{
|
||||||
|
Name: name,
|
||||||
|
Value: dbus.MakeVariant(units),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func UseSystemd() bool {
|
func UseSystemd() bool {
|
||||||
s, err := os.Stat("/run/systemd/system")
|
s, err := os.Stat("/run/systemd/system")
|
||||||
if err != nil || !s.IsDir() {
|
if err != nil || !s.IsDir() {
|
||||||
|
@ -99,27 +106,27 @@ func Apply(c *cgroups.Cgroup, pid int) (cgroups.ActiveCgroup, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
properties = append(properties,
|
properties = append(properties,
|
||||||
systemd.Property{"Slice", dbus.MakeVariant(slice)},
|
systemd.PropSlice(slice),
|
||||||
systemd.Property{"Description", dbus.MakeVariant("docker container " + c.Name)},
|
systemd.PropDescription("docker container "+c.Name),
|
||||||
systemd.Property{"PIDs", dbus.MakeVariant([]uint32{uint32(pid)})},
|
newProp("PIDs", []uint32{uint32(pid)}),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Always enable accounting, this gets us the same behaviour as the fs implementation,
|
// Always enable accounting, this gets us the same behaviour as the fs implementation,
|
||||||
// plus the kernel has some problems with joining the memory cgroup at a later time.
|
// plus the kernel has some problems with joining the memory cgroup at a later time.
|
||||||
properties = append(properties,
|
properties = append(properties,
|
||||||
systemd.Property{"MemoryAccounting", dbus.MakeVariant(true)},
|
newProp("MemoryAccounting", true),
|
||||||
systemd.Property{"CPUAccounting", dbus.MakeVariant(true)},
|
newProp("CPUAccounting", true),
|
||||||
systemd.Property{"BlockIOAccounting", dbus.MakeVariant(true)})
|
newProp("BlockIOAccounting", true))
|
||||||
|
|
||||||
if c.Memory != 0 {
|
if c.Memory != 0 {
|
||||||
properties = append(properties,
|
properties = append(properties,
|
||||||
systemd.Property{"MemoryLimit", dbus.MakeVariant(uint64(c.Memory))})
|
newProp("MemoryLimit", uint64(c.Memory)))
|
||||||
}
|
}
|
||||||
// TODO: MemoryReservation and MemorySwap not available in systemd
|
// TODO: MemoryReservation and MemorySwap not available in systemd
|
||||||
|
|
||||||
if c.CpuShares != 0 {
|
if c.CpuShares != 0 {
|
||||||
properties = append(properties,
|
properties = append(properties,
|
||||||
systemd.Property{"CPUShares", dbus.MakeVariant(uint64(c.CpuShares))})
|
newProp("CPUShares", uint64(c.CpuShares)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := theConn.StartTransientUnit(unitName, "replace", properties...); err != nil {
|
if _, err := theConn.StartTransientUnit(unitName, "replace", properties...); err != nil {
|
||||||
|
|
|
@ -103,7 +103,7 @@ func getDeviceNodes(path string) ([]*Device, error) {
|
||||||
switch {
|
switch {
|
||||||
case f.IsDir():
|
case f.IsDir():
|
||||||
switch f.Name() {
|
switch f.Name() {
|
||||||
case "pts", "shm", "fd":
|
case "pts", "shm", "fd", "mqueue":
|
||||||
continue
|
continue
|
||||||
default:
|
default:
|
||||||
sub, err := getDeviceNodes(filepath.Join(path, f.Name()))
|
sub, err := getDeviceNodes(filepath.Join(path, f.Name()))
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/docker/libcontainer/namespaces"
|
"github.com/docker/libcontainer/namespaces"
|
||||||
"github.com/docker/libcontainer/syncpipe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// init runs the libcontainer initialization code because of the busybox style needs
|
// init runs the libcontainer initialization code because of the busybox style needs
|
||||||
|
@ -27,12 +26,7 @@ func init() {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
syncPipe, err := syncpipe.NewSyncPipeFromFd(0, 3)
|
if err := namespaces.Init(container, rootfs, "", os.NewFile(3, "pipe"), os.Args[3:]); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("unable to create sync pipe: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := namespaces.Init(container, rootfs, "", syncPipe, os.Args[3:]); err != nil {
|
|
||||||
log.Fatalf("unable to initialize for container: %s", err)
|
log.Fatalf("unable to initialize for container: %s", err)
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -43,3 +43,15 @@ func ReserveLabel(label string) error {
|
||||||
func UnreserveLabel(label string) error {
|
func UnreserveLabel(label string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DupSecOpt takes an process label and returns security options that
|
||||||
|
// can be used to set duplicate labels on future container processes
|
||||||
|
func DupSecOpt(src string) []string {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableSecOpt returns a security opt that can disable labeling
|
||||||
|
// support for future container processes
|
||||||
|
func DisableSecOpt() []string {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ func InitLabels(options []string) (string, string, error) {
|
||||||
if !selinux.SelinuxEnabled() {
|
if !selinux.SelinuxEnabled() {
|
||||||
return "", "", nil
|
return "", "", nil
|
||||||
}
|
}
|
||||||
var err error
|
|
||||||
processLabel, mountLabel := selinux.GetLxcContexts()
|
processLabel, mountLabel := selinux.GetLxcContexts()
|
||||||
if processLabel != "" {
|
if processLabel != "" {
|
||||||
pcon := selinux.NewContext(processLabel)
|
pcon := selinux.NewContext(processLabel)
|
||||||
|
@ -38,7 +37,7 @@ func InitLabels(options []string) (string, string, error) {
|
||||||
processLabel = pcon.Get()
|
processLabel = pcon.Get()
|
||||||
mountLabel = mcon.Get()
|
mountLabel = mcon.Get()
|
||||||
}
|
}
|
||||||
return processLabel, mountLabel, err
|
return processLabel, mountLabel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEPRECATED: The GenLabels function is only to be used during the transition to the official API.
|
// DEPRECATED: The GenLabels function is only to be used during the transition to the official API.
|
||||||
|
@ -130,3 +129,15 @@ func UnreserveLabel(label string) error {
|
||||||
selinux.FreeLxcContexts(label)
|
selinux.FreeLxcContexts(label)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DupSecOpt takes an process label and returns security options that
|
||||||
|
// can be used to set duplicate labels on future container processes
|
||||||
|
func DupSecOpt(src string) []string {
|
||||||
|
return selinux.DupSecOpt(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableSecOpt returns a security opt that can disable labeling
|
||||||
|
// support for future container processes
|
||||||
|
func DisableSecOpt() []string {
|
||||||
|
return selinux.DisableSecOpt()
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
package label
|
package label
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/libcontainer/selinux"
|
"github.com/docker/libcontainer/selinux"
|
||||||
|
@ -33,7 +34,7 @@ func TestInit(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if plabel != "user_u:user_r:user_t:s0:c1,c15" || mlabel != "user_u:object_r:svirt_sandbox_file_t:s0:c1,c15" {
|
if plabel != "user_u:user_r:user_t:s0:c1,c15" || mlabel != "user_u:object_r:svirt_sandbox_file_t:s0:c1,c15" {
|
||||||
t.Log("InitLabels User Failed")
|
t.Log("InitLabels User Match Failed")
|
||||||
t.Log(plabel, mlabel)
|
t.Log(plabel, mlabel)
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -46,3 +47,43 @@ func TestInit(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func TestDuplicateLabel(t *testing.T) {
|
||||||
|
secopt := DupSecOpt("system_u:system_r:svirt_lxc_net_t:s0:c1,c2")
|
||||||
|
t.Log(secopt)
|
||||||
|
for _, opt := range secopt {
|
||||||
|
con := strings.SplitN(opt, ":", 3)
|
||||||
|
if len(con) != 3 || con[0] != "label" {
|
||||||
|
t.Errorf("Invalid DupSecOpt return value")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if con[1] == "user" {
|
||||||
|
if con[2] != "system_u" {
|
||||||
|
t.Errorf("DupSecOpt Failed user incorrect")
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if con[1] == "role" {
|
||||||
|
if con[2] != "system_r" {
|
||||||
|
t.Errorf("DupSecOpt Failed role incorrect")
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if con[1] == "type" {
|
||||||
|
if con[2] != "svirt_lxc_net_t" {
|
||||||
|
t.Errorf("DupSecOpt Failed type incorrect")
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if con[1] == "level" {
|
||||||
|
if con[2] != "s0:c1,c2" {
|
||||||
|
t.Errorf("DupSecOpt Failed level incorrect")
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Errorf("DupSecOpt Failed invalid field %q", con[1])
|
||||||
|
}
|
||||||
|
secopt = DisableSecOpt()
|
||||||
|
if secopt[0] != "label:disable" {
|
||||||
|
t.Errorf("DisableSecOpt Failed level incorrect")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -97,7 +97,7 @@ func InitializeMountNamespace(rootfs, console string, sysReadonly bool, mountCon
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// mountSystem sets up linux specific system mounts like sys, proc, shm, and devpts
|
// mountSystem sets up linux specific system mounts like mqueue, sys, proc, shm, and devpts
|
||||||
// inside the mount namespace
|
// inside the mount namespace
|
||||||
func mountSystem(rootfs string, sysReadonly bool, mountConfig *MountConfig) error {
|
func mountSystem(rootfs string, sysReadonly bool, mountConfig *MountConfig) error {
|
||||||
for _, m := range newSystemMounts(rootfs, mountConfig.MountLabel, sysReadonly) {
|
for _, m := range newSystemMounts(rootfs, mountConfig.MountLabel, sysReadonly) {
|
||||||
|
@ -168,6 +168,7 @@ func newSystemMounts(rootfs, mountLabel string, sysReadonly bool) []mount {
|
||||||
{source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaultMountFlags},
|
{source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaultMountFlags},
|
||||||
{source: "tmpfs", path: filepath.Join(rootfs, "dev"), device: "tmpfs", flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME, data: label.FormatMountLabel("mode=755", mountLabel)},
|
{source: "tmpfs", path: filepath.Join(rootfs, "dev"), device: "tmpfs", flags: syscall.MS_NOSUID | syscall.MS_STRICTATIME, data: label.FormatMountLabel("mode=755", mountLabel)},
|
||||||
{source: "shm", path: filepath.Join(rootfs, "dev", "shm"), device: "tmpfs", flags: defaultMountFlags, data: label.FormatMountLabel("mode=1777,size=65536k", mountLabel)},
|
{source: "shm", path: filepath.Join(rootfs, "dev", "shm"), device: "tmpfs", flags: defaultMountFlags, data: label.FormatMountLabel("mode=1777,size=65536k", mountLabel)},
|
||||||
|
{source: "mqueue", path: filepath.Join(rootfs, "dev", "mqueue"), device: "mqueue", flags: defaultMountFlags},
|
||||||
{source: "devpts", path: filepath.Join(rootfs, "dev", "pts"), device: "devpts", flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, data: label.FormatMountLabel("newinstance,ptmxmode=0666,mode=620,gid=5", mountLabel)},
|
{source: "devpts", path: filepath.Join(rootfs, "dev", "pts"), device: "devpts", flags: syscall.MS_NOSUID | syscall.MS_NOEXEC, data: label.FormatMountLabel("newinstance,ptmxmode=0666,mode=620,gid=5", mountLabel)},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
package namespaces
|
package namespaces
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
@ -13,7 +14,6 @@ import (
|
||||||
"github.com/docker/libcontainer/cgroups/fs"
|
"github.com/docker/libcontainer/cgroups/fs"
|
||||||
"github.com/docker/libcontainer/cgroups/systemd"
|
"github.com/docker/libcontainer/cgroups/systemd"
|
||||||
"github.com/docker/libcontainer/network"
|
"github.com/docker/libcontainer/network"
|
||||||
"github.com/docker/libcontainer/syncpipe"
|
|
||||||
"github.com/docker/libcontainer/system"
|
"github.com/docker/libcontainer/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,19 +22,17 @@ import (
|
||||||
// Exec performs setup outside of a namespace so that a container can be
|
// Exec performs setup outside of a namespace so that a container can be
|
||||||
// executed. Exec is a high level function for working with container namespaces.
|
// executed. Exec is a high level function for working with container namespaces.
|
||||||
func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Writer, console, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) {
|
func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Writer, console, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) {
|
||||||
var (
|
var err error
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
// create a pipe so that we can syncronize with the namespaced process and
|
// create a pipe so that we can syncronize with the namespaced process and
|
||||||
// pass the veth name to the child
|
// pass the state and configuration to the child process
|
||||||
syncPipe, err := syncpipe.NewSyncPipe()
|
parent, child, err := newInitPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
defer syncPipe.Close()
|
defer parent.Close()
|
||||||
|
|
||||||
command := createCommand(container, console, dataPath, os.Args[0], syncPipe.Child(), args)
|
command := createCommand(container, console, dataPath, os.Args[0], child, args)
|
||||||
// Note: these are only used in non-tty mode
|
// Note: these are only used in non-tty mode
|
||||||
// if there is a tty for the container it will be opened within the namespace and the
|
// if there is a tty for the container it will be opened within the namespace and the
|
||||||
// fds will be duped to stdin, stdiout, and stderr
|
// fds will be duped to stdin, stdiout, and stderr
|
||||||
|
@ -43,39 +41,47 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri
|
||||||
command.Stderr = stderr
|
command.Stderr = stderr
|
||||||
|
|
||||||
if err := command.Start(); err != nil {
|
if err := command.Start(); err != nil {
|
||||||
|
child.Close()
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
child.Close()
|
||||||
|
|
||||||
// Now we passed the pipe to the child, close our side
|
terminate := func(terr error) (int, error) {
|
||||||
syncPipe.CloseChild()
|
// TODO: log the errors for kill and wait
|
||||||
|
command.Process.Kill()
|
||||||
|
command.Wait()
|
||||||
|
return -1, terr
|
||||||
|
}
|
||||||
|
|
||||||
started, err := system.GetProcessStartTime(command.Process.Pid)
|
started, err := system.GetProcessStartTime(command.Process.Pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return terminate(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do this before syncing with child so that no children
|
// Do this before syncing with child so that no children
|
||||||
// can escape the cgroup
|
// can escape the cgroup
|
||||||
cgroupRef, err := SetupCgroups(container, command.Process.Pid)
|
cgroupRef, err := SetupCgroups(container, command.Process.Pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
command.Process.Kill()
|
return terminate(err)
|
||||||
command.Wait()
|
|
||||||
return -1, err
|
|
||||||
}
|
}
|
||||||
defer cgroupRef.Cleanup()
|
defer cgroupRef.Cleanup()
|
||||||
|
|
||||||
cgroupPaths, err := cgroupRef.Paths()
|
cgroupPaths, err := cgroupRef.Paths()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
command.Process.Kill()
|
return terminate(err)
|
||||||
command.Wait()
|
|
||||||
return -1, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var networkState network.NetworkState
|
var networkState network.NetworkState
|
||||||
if err := InitializeNetworking(container, command.Process.Pid, syncPipe, &networkState); err != nil {
|
if err := InitializeNetworking(container, command.Process.Pid, &networkState); err != nil {
|
||||||
command.Process.Kill()
|
return terminate(err)
|
||||||
command.Wait()
|
}
|
||||||
return -1, err
|
// send the state to the container's init process then shutdown writes for the parent
|
||||||
|
if err := json.NewEncoder(parent).Encode(networkState); err != nil {
|
||||||
|
return terminate(err)
|
||||||
|
}
|
||||||
|
// shutdown writes for the parent side of the pipe
|
||||||
|
if err := syscall.Shutdown(int(parent.Fd()), syscall.SHUT_WR); err != nil {
|
||||||
|
return terminate(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
state := &libcontainer.State{
|
state := &libcontainer.State{
|
||||||
|
@ -86,17 +92,18 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := libcontainer.SaveState(dataPath, state); err != nil {
|
if err := libcontainer.SaveState(dataPath, state); err != nil {
|
||||||
command.Process.Kill()
|
return terminate(err)
|
||||||
command.Wait()
|
|
||||||
return -1, err
|
|
||||||
}
|
}
|
||||||
defer libcontainer.DeleteState(dataPath)
|
defer libcontainer.DeleteState(dataPath)
|
||||||
|
|
||||||
// Sync with child
|
// wait for the child process to fully complete and receive an error message
|
||||||
if err := syncPipe.ReadFromChild(); err != nil {
|
// if one was encoutered
|
||||||
command.Process.Kill()
|
var ierr *initError
|
||||||
command.Wait()
|
if err := json.NewDecoder(parent).Decode(&ierr); err != nil && err != io.EOF {
|
||||||
return -1, err
|
return terminate(err)
|
||||||
|
}
|
||||||
|
if ierr != nil {
|
||||||
|
return terminate(ierr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if startCallback != nil {
|
if startCallback != nil {
|
||||||
|
@ -108,7 +115,6 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
|
return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,16 +135,6 @@ func DefaultCreateCommand(container *libcontainer.Config, console, dataPath, ini
|
||||||
"data_path=" + dataPath,
|
"data_path=" + dataPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: move user and wd into env
|
|
||||||
if user != "" {
|
|
||||||
env = append(env, "user="+user)
|
|
||||||
}
|
|
||||||
if workingDir != "" {
|
|
||||||
env = append(env, "wd="+workingDir)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
command := exec.Command(init, append([]string{"init", "--"}, args...)...)
|
command := exec.Command(init, append([]string{"init", "--"}, args...)...)
|
||||||
// make sure the process is executed inside the context of the rootfs
|
// make sure the process is executed inside the context of the rootfs
|
||||||
command.Dir = container.RootFs
|
command.Dir = container.RootFs
|
||||||
|
@ -173,7 +169,7 @@ func SetupCgroups(container *libcontainer.Config, nspid int) (cgroups.ActiveCgro
|
||||||
|
|
||||||
// InitializeNetworking creates the container's network stack outside of the namespace and moves
|
// InitializeNetworking creates the container's network stack outside of the namespace and moves
|
||||||
// interfaces into the container's net namespaces if necessary
|
// interfaces into the container's net namespaces if necessary
|
||||||
func InitializeNetworking(container *libcontainer.Config, nspid int, pipe *syncpipe.SyncPipe, networkState *network.NetworkState) error {
|
func InitializeNetworking(container *libcontainer.Config, nspid int, networkState *network.NetworkState) error {
|
||||||
for _, config := range container.Networks {
|
for _, config := range container.Networks {
|
||||||
strategy, err := network.GetStrategy(config.Type)
|
strategy, err := network.GetStrategy(config.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -183,18 +179,5 @@ func InitializeNetworking(container *libcontainer.Config, nspid int, pipe *syncp
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pipe.SendToChild(networkState)
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
// GetNamespaceFlags parses the container's Namespaces options to set the correct
|
|
||||||
// flags on clone, unshare, and setns
|
|
||||||
func GetNamespaceFlags(namespaces map[string]bool) (flag int) {
|
|
||||||
for key, enabled := range namespaces {
|
|
||||||
if enabled {
|
|
||||||
if ns := GetNamespace(key); ns != nil {
|
|
||||||
flag |= ns.Value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return flag
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
package namespaces
|
package namespaces
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
@ -15,7 +16,6 @@ import (
|
||||||
"github.com/docker/libcontainer/apparmor"
|
"github.com/docker/libcontainer/apparmor"
|
||||||
"github.com/docker/libcontainer/cgroups"
|
"github.com/docker/libcontainer/cgroups"
|
||||||
"github.com/docker/libcontainer/label"
|
"github.com/docker/libcontainer/label"
|
||||||
"github.com/docker/libcontainer/syncpipe"
|
|
||||||
"github.com/docker/libcontainer/system"
|
"github.com/docker/libcontainer/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -41,11 +41,11 @@ func ExecIn(container *libcontainer.Config, state *libcontainer.State, userArgs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pipe, err := syncpipe.NewSyncPipe()
|
parent, child, err := newInitPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
defer pipe.Close()
|
defer parent.Close()
|
||||||
|
|
||||||
// Note: these are only used in non-tty mode
|
// Note: these are only used in non-tty mode
|
||||||
// if there is a tty for the container it will be opened within the namespace and the
|
// if there is a tty for the container it will be opened within the namespace and the
|
||||||
|
@ -53,23 +53,28 @@ func ExecIn(container *libcontainer.Config, state *libcontainer.State, userArgs
|
||||||
cmd.Stdin = stdin
|
cmd.Stdin = stdin
|
||||||
cmd.Stdout = stdout
|
cmd.Stdout = stdout
|
||||||
cmd.Stderr = stderr
|
cmd.Stderr = stderr
|
||||||
|
cmd.ExtraFiles = []*os.File{child}
|
||||||
cmd.ExtraFiles = []*os.File{pipe.Child()}
|
|
||||||
|
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
|
child.Close()
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
pipe.CloseChild()
|
child.Close()
|
||||||
|
|
||||||
|
terminate := func(terr error) (int, error) {
|
||||||
|
// TODO: log the errors for kill and wait
|
||||||
|
cmd.Process.Kill()
|
||||||
|
cmd.Wait()
|
||||||
|
return -1, terr
|
||||||
|
}
|
||||||
|
|
||||||
// Enter cgroups.
|
// Enter cgroups.
|
||||||
if err := EnterCgroups(state, cmd.Process.Pid); err != nil {
|
if err := EnterCgroups(state, cmd.Process.Pid); err != nil {
|
||||||
return -1, err
|
return terminate(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := pipe.SendToChild(container); err != nil {
|
if err := json.NewEncoder(parent).Encode(container); err != nil {
|
||||||
cmd.Process.Kill()
|
return terminate(err)
|
||||||
cmd.Wait()
|
|
||||||
return -1, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if startCallback != nil {
|
if startCallback != nil {
|
||||||
|
@ -81,7 +86,6 @@ func ExecIn(container *libcontainer.Config, state *libcontainer.State, userArgs
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
|
return cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
package namespaces
|
package namespaces
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
@ -18,7 +20,6 @@ import (
|
||||||
"github.com/docker/libcontainer/network"
|
"github.com/docker/libcontainer/network"
|
||||||
"github.com/docker/libcontainer/security/capabilities"
|
"github.com/docker/libcontainer/security/capabilities"
|
||||||
"github.com/docker/libcontainer/security/restrict"
|
"github.com/docker/libcontainer/security/restrict"
|
||||||
"github.com/docker/libcontainer/syncpipe"
|
|
||||||
"github.com/docker/libcontainer/system"
|
"github.com/docker/libcontainer/system"
|
||||||
"github.com/docker/libcontainer/user"
|
"github.com/docker/libcontainer/user"
|
||||||
"github.com/docker/libcontainer/utils"
|
"github.com/docker/libcontainer/utils"
|
||||||
|
@ -30,11 +31,22 @@ import (
|
||||||
// and other options required for the new container.
|
// and other options required for the new container.
|
||||||
// The caller of Init function has to ensure that the go runtime is locked to an OS thread
|
// The caller of Init function has to ensure that the go runtime is locked to an OS thread
|
||||||
// (using runtime.LockOSThread) else system calls like setns called within Init may not work as intended.
|
// (using runtime.LockOSThread) else system calls like setns called within Init may not work as intended.
|
||||||
func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syncPipe *syncpipe.SyncPipe, args []string) (err error) {
|
func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, pipe *os.File, args []string) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
|
// if we have an error during the initialization of the container's init then send it back to the
|
||||||
|
// parent process in the form of an initError.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
syncPipe.ReportChildError(err)
|
// ensure that any data sent from the parent is consumed so it doesn't
|
||||||
|
// receive ECONNRESET when the child writes to the pipe.
|
||||||
|
ioutil.ReadAll(pipe)
|
||||||
|
if err := json.NewEncoder(pipe).Encode(initError{
|
||||||
|
Message: err.Error(),
|
||||||
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// ensure that this pipe is always closed
|
||||||
|
pipe.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
rootfs, err := utils.ResolveRootfs(uncleanRootfs)
|
rootfs, err := utils.ResolveRootfs(uncleanRootfs)
|
||||||
|
@ -50,7 +62,7 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syn
|
||||||
|
|
||||||
// We always read this as it is a way to sync with the parent as well
|
// We always read this as it is a way to sync with the parent as well
|
||||||
var networkState *network.NetworkState
|
var networkState *network.NetworkState
|
||||||
if err := syncPipe.ReadFromParent(&networkState); err != nil {
|
if err := json.NewDecoder(pipe).Decode(&networkState); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,11 +176,11 @@ func SetupUser(u string) error {
|
||||||
return fmt.Errorf("setgroups %s", err)
|
return fmt.Errorf("setgroups %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := syscall.Setgid(gid); err != nil {
|
if err := system.Setgid(gid); err != nil {
|
||||||
return fmt.Errorf("setgid %s", err)
|
return fmt.Errorf("setgid %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := syscall.Setuid(uid); err != nil {
|
if err := system.Setuid(uid); err != nil {
|
||||||
return fmt.Errorf("setuid %s", err)
|
return fmt.Errorf("setuid %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package namespaces
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
type initError struct {
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i initError) Error() string {
|
||||||
|
return i.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a newly initialized Pipe for communication between processes
|
||||||
|
func newInitPipe() (parent *os.File, child *os.File, err error) {
|
||||||
|
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return os.NewFile(uintptr(fds[1]), "parent"), os.NewFile(uintptr(fds[0]), "child"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNamespaceFlags parses the container's Namespaces options to set the correct
|
||||||
|
// flags on clone, unshare, and setns
|
||||||
|
func GetNamespaceFlags(namespaces map[string]bool) (flag int) {
|
||||||
|
for key, enabled := range namespaces {
|
||||||
|
if enabled {
|
||||||
|
if ns := GetNamespace(key); ns != nil {
|
||||||
|
flag |= ns.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flag
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
@ -1204,6 +1205,28 @@ func SetMacAddress(name, addr string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetHairpinMode(iface *net.Interface, enabled bool) error {
|
||||||
|
sysPath := filepath.Join("/sys/class/net", iface.Name, "brport/hairpin_mode")
|
||||||
|
|
||||||
|
sysFile, err := os.OpenFile(sysPath, os.O_WRONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer sysFile.Close()
|
||||||
|
|
||||||
|
var writeVal []byte
|
||||||
|
if enabled {
|
||||||
|
writeVal = []byte("1")
|
||||||
|
} else {
|
||||||
|
writeVal = []byte("0")
|
||||||
|
}
|
||||||
|
if _, err := sysFile.Write(writeVal); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func ChangeName(iface *net.Interface, newName string) error {
|
func ChangeName(iface *net.Interface, newName string) error {
|
||||||
if len(newName) >= IFNAMSIZ {
|
if len(newName) >= IFNAMSIZ {
|
||||||
return fmt.Errorf("Interface name %s too long", newName)
|
return fmt.Errorf("Interface name %s too long", newName)
|
||||||
|
@ -1224,5 +1247,6 @@ func ChangeName(iface *net.Interface, newName string) error {
|
||||||
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCSIFNAME, uintptr(unsafe.Pointer(&data[0]))); errno != 0 {
|
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCSIFNAME, uintptr(unsafe.Pointer(&data[0]))); errno != 0 {
|
||||||
return errno
|
return errno
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,7 @@ func TestNetworkSetMacAddress(t *testing.T) {
|
||||||
ifcBeforeSet := readLink(t, tl.name)
|
ifcBeforeSet := readLink(t, tl.name)
|
||||||
|
|
||||||
if err := NetworkSetMacAddress(ifcBeforeSet, macaddr); err != nil {
|
if err := NetworkSetMacAddress(ifcBeforeSet, macaddr); err != nil {
|
||||||
t.Fatalf("Could not set %s MAC address on %#v interface: err", macaddr, tl, err)
|
t.Fatalf("Could not set %s MAC address on %#v interface: %s", macaddr, tl, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ifcAfterSet := readLink(t, tl.name)
|
ifcAfterSet := readLink(t, tl.name)
|
||||||
|
@ -140,7 +140,7 @@ func TestNetworkSetMTU(t *testing.T) {
|
||||||
ifcBeforeSet := readLink(t, tl.name)
|
ifcBeforeSet := readLink(t, tl.name)
|
||||||
|
|
||||||
if err := NetworkSetMTU(ifcBeforeSet, mtu); err != nil {
|
if err := NetworkSetMTU(ifcBeforeSet, mtu); err != nil {
|
||||||
t.Fatalf("Could not set %d MTU on %#v interface: err", mtu, tl, err)
|
t.Fatalf("Could not set %d MTU on %#v interface: %s", mtu, tl, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ifcAfterSet := readLink(t, tl.name)
|
ifcAfterSet := readLink(t, tl.name)
|
||||||
|
|
|
@ -95,3 +95,11 @@ func SetMtu(name string, mtu int) error {
|
||||||
}
|
}
|
||||||
return netlink.NetworkSetMTU(iface, mtu)
|
return netlink.NetworkSetMTU(iface, mtu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetHairpinMode(name string, enabled bool) error {
|
||||||
|
iface, err := net.InterfaceByName(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return netlink.SetHairpinMode(iface, enabled)
|
||||||
|
}
|
||||||
|
|
|
@ -39,6 +39,9 @@ func (v *Veth) Create(n *Network, nspid int, networkState *NetworkState) error {
|
||||||
if err := SetMtu(name1, n.Mtu); err != nil {
|
if err := SetMtu(name1, n.Mtu); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := SetHairpinMode(name1, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := InterfaceUp(name1); err != nil {
|
if err := InterfaceUp(name1); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/docker/libcontainer/namespaces"
|
"github.com/docker/libcontainer/namespaces"
|
||||||
"github.com/docker/libcontainer/syncpipe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -41,12 +40,8 @@ func initAction(context *cli.Context) {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
syncPipe, err := syncpipe.NewSyncPipeFromFd(0, uintptr(pipeFd))
|
pipe := os.NewFile(uintptr(pipeFd), "pipe")
|
||||||
if err != nil {
|
if err := namespaces.Init(container, rootfs, console, pipe, []string(context.Args())); err != nil {
|
||||||
log.Fatalf("unable to create sync pipe: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := namespaces.Init(container, rootfs, console, syncPipe, []string(context.Args())); err != nil {
|
|
||||||
log.Fatalf("unable to initialize for container: %s", err)
|
log.Fatalf("unable to initialize for container: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/docker/libcontainer"
|
"github.com/docker/libcontainer"
|
||||||
"github.com/docker/libcontainer/syncpipe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// rFunc is a function registration for calling after an execin
|
// rFunc is a function registration for calling after an execin
|
||||||
|
@ -59,16 +58,13 @@ func findUserArgs() []string {
|
||||||
// loadConfigFromFd loads a container's config from the sync pipe that is provided by
|
// loadConfigFromFd loads a container's config from the sync pipe that is provided by
|
||||||
// fd 3 when running a process
|
// fd 3 when running a process
|
||||||
func loadConfigFromFd() (*libcontainer.Config, error) {
|
func loadConfigFromFd() (*libcontainer.Config, error) {
|
||||||
syncPipe, err := syncpipe.NewSyncPipeFromFd(0, 3)
|
pipe := os.NewFile(3, "pipe")
|
||||||
if err != nil {
|
defer pipe.Close()
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var config *libcontainer.Config
|
var config *libcontainer.Config
|
||||||
if err := syncPipe.ReadFromParent(&config); err != nil {
|
if err := json.NewDecoder(pipe).Decode(&config); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -434,3 +434,28 @@ func Chcon(fpath string, scon string, recurse bool) error {
|
||||||
|
|
||||||
return Setfilecon(fpath, scon)
|
return Setfilecon(fpath, scon)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DupSecOpt takes an SELinux process label and returns security options that
|
||||||
|
// can will set the SELinux Type and Level for future container processes
|
||||||
|
func DupSecOpt(src string) []string {
|
||||||
|
if src == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
con := NewContext(src)
|
||||||
|
if con["user"] == "" ||
|
||||||
|
con["role"] == "" ||
|
||||||
|
con["type"] == "" ||
|
||||||
|
con["level"] == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []string{"label:user:" + con["user"],
|
||||||
|
"label:role:" + con["role"],
|
||||||
|
"label:type:" + con["type"],
|
||||||
|
"label:level:" + con["level"]}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableSecOpt returns a security opt that can be used to disabling SELinux
|
||||||
|
// labeling support for future container processes
|
||||||
|
func DisableSecOpt() []string {
|
||||||
|
return []string{"label:disable"}
|
||||||
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ func TestSELinux(t *testing.T) {
|
||||||
t.Log("getenforce ", selinux.SelinuxGetEnforce())
|
t.Log("getenforce ", selinux.SelinuxGetEnforce())
|
||||||
t.Log("getenforcemode ", selinux.SelinuxGetEnforceMode())
|
t.Log("getenforcemode ", selinux.SelinuxGetEnforceMode())
|
||||||
pid := os.Getpid()
|
pid := os.Getpid()
|
||||||
t.Log("PID:%d MCS:%s\n", pid, selinux.IntToMcs(pid, 1023))
|
t.Logf("PID:%d MCS:%s\n", pid, selinux.IntToMcs(pid, 1023))
|
||||||
err = selinux.Setfscreatecon("unconfined_u:unconfined_r:unconfined_t:s0")
|
err = selinux.Setfscreatecon("unconfined_u:unconfined_r:unconfined_t:s0")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Log(selinux.Getfscreatecon())
|
t.Log(selinux.Getfscreatecon())
|
||||||
|
|
|
@ -1,105 +0,0 @@
|
||||||
package syncpipe
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SyncPipe allows communication to and from the child processes
|
|
||||||
// to it's parent and allows the two independent processes to
|
|
||||||
// syncronize their state.
|
|
||||||
type SyncPipe struct {
|
|
||||||
parent, child *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSyncPipeFromFd(parentFd, childFd uintptr) (*SyncPipe, error) {
|
|
||||||
s := &SyncPipe{}
|
|
||||||
|
|
||||||
if parentFd > 0 {
|
|
||||||
s.parent = os.NewFile(parentFd, "parentPipe")
|
|
||||||
} else if childFd > 0 {
|
|
||||||
s.child = os.NewFile(childFd, "childPipe")
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("no valid sync pipe fd specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SyncPipe) Child() *os.File {
|
|
||||||
return s.child
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SyncPipe) Parent() *os.File {
|
|
||||||
return s.parent
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SyncPipe) SendToChild(v interface{}) error {
|
|
||||||
data, err := json.Marshal(v)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.parent.Write(data)
|
|
||||||
|
|
||||||
return syscall.Shutdown(int(s.parent.Fd()), syscall.SHUT_WR)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SyncPipe) ReadFromChild() error {
|
|
||||||
data, err := ioutil.ReadAll(s.parent)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(data) > 0 {
|
|
||||||
return fmt.Errorf("%s", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SyncPipe) ReadFromParent(v interface{}) error {
|
|
||||||
data, err := ioutil.ReadAll(s.child)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error reading from sync pipe %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(data) > 0 {
|
|
||||||
if err := json.Unmarshal(data, v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SyncPipe) ReportChildError(err error) {
|
|
||||||
// ensure that any data sent from the parent is consumed so it doesn't
|
|
||||||
// receive ECONNRESET when the child writes to the pipe.
|
|
||||||
ioutil.ReadAll(s.child)
|
|
||||||
|
|
||||||
s.child.Write([]byte(err.Error()))
|
|
||||||
s.CloseChild()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SyncPipe) Close() error {
|
|
||||||
if s.parent != nil {
|
|
||||||
s.parent.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.child != nil {
|
|
||||||
s.child.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SyncPipe) CloseChild() {
|
|
||||||
if s.child != nil {
|
|
||||||
s.child.Close()
|
|
||||||
s.child = nil
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
package syncpipe
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewSyncPipe() (s *SyncPipe, err error) {
|
|
||||||
s = &SyncPipe{}
|
|
||||||
|
|
||||||
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.child = os.NewFile(uintptr(fds[0]), "child syncpipe")
|
|
||||||
s.parent = os.NewFile(uintptr(fds[1]), "parent syncpipe")
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
package syncpipe
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"syscall"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type testStruct struct {
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSendErrorFromChild(t *testing.T) {
|
|
||||||
pipe, err := NewSyncPipe()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := pipe.Close(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
childfd, err := syscall.Dup(int(pipe.Child().Fd()))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
childPipe, _ := NewSyncPipeFromFd(0, uintptr(childfd))
|
|
||||||
|
|
||||||
pipe.CloseChild()
|
|
||||||
pipe.SendToChild(nil)
|
|
||||||
|
|
||||||
expected := "something bad happened"
|
|
||||||
childPipe.ReportChildError(fmt.Errorf(expected))
|
|
||||||
|
|
||||||
childError := pipe.ReadFromChild()
|
|
||||||
if childError == nil {
|
|
||||||
t.Fatal("expected an error to be returned but did not receive anything")
|
|
||||||
}
|
|
||||||
|
|
||||||
if childError.Error() != expected {
|
|
||||||
t.Fatalf("expected %q but received error message %q", expected, childError.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSendPayloadToChild(t *testing.T) {
|
|
||||||
pipe, err := NewSyncPipe()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := pipe.Close(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
expected := "libcontainer"
|
|
||||||
|
|
||||||
if err := pipe.SendToChild(testStruct{Name: expected}); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var s *testStruct
|
|
||||||
if err := pipe.ReadFromParent(&s); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Name != expected {
|
|
||||||
t.Fatalf("expected name %q but received %q", expected, s.Name)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +1,5 @@
|
||||||
// +build linux,amd64
|
// +build linux,amd64
|
||||||
|
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
Loading…
Reference in New Issue