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