mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
1716d497a4
This change will allow us to run SELinux in a container with BTRFS back end. We continue to work on fixing the kernel/BTRFS but this change will allow SELinux Security separation on BTRFS. It basically relabels the content on container creation. Just relabling -init directory in BTRFS use case. Everything looks like it works. I don't believe tar/achive stores the SELinux labels, so we are good as far as docker commit. Tested Speed on startup with BTRFS on top of loopback directory. BTRFS not on loopback should get even better perfomance on startup time. The more inodes inside of the container image will increase the relabel time. This patch will give people who care more about security the option of runnin BTRFS with SELinux. Those who don't want to take the slow down can disable SELinux either in individual containers or for all containers by continuing to disable SELinux in the daemon. Without relabel: > time docker run --security-opt label:disable fedora echo test test real 0m0.918s user 0m0.009s sys 0m0.026s With Relabel test real 0m1.942s user 0m0.007s sys 0m0.030s Signed-off-by: Dan Walsh <dwalsh@redhat.com> Signed-off-by: Dan Walsh <dwalsh@redhat.com>
353 lines
9 KiB
Go
353 lines
9 KiB
Go
// +build experimental
|
|
// +build !windows
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/docker/docker/daemon/graphdriver"
|
|
"github.com/docker/docker/daemon/graphdriver/vfs"
|
|
"github.com/docker/docker/pkg/archive"
|
|
"github.com/go-check/check"
|
|
)
|
|
|
|
func init() {
|
|
check.Suite(&DockerExternalGraphdriverSuite{
|
|
ds: &DockerSuite{},
|
|
})
|
|
}
|
|
|
|
type DockerExternalGraphdriverSuite struct {
|
|
server *httptest.Server
|
|
ds *DockerSuite
|
|
d *Daemon
|
|
ec *graphEventsCounter
|
|
}
|
|
|
|
type graphEventsCounter struct {
|
|
activations int
|
|
creations int
|
|
removals int
|
|
gets int
|
|
puts int
|
|
stats int
|
|
cleanups int
|
|
exists int
|
|
init int
|
|
metadata int
|
|
diff int
|
|
applydiff int
|
|
changes int
|
|
diffsize int
|
|
}
|
|
|
|
func (s *DockerExternalGraphdriverSuite) SetUpTest(c *check.C) {
|
|
s.d = NewDaemon(c)
|
|
s.ec = &graphEventsCounter{}
|
|
}
|
|
|
|
func (s *DockerExternalGraphdriverSuite) TearDownTest(c *check.C) {
|
|
s.d.Stop()
|
|
s.ds.TearDownTest(c)
|
|
}
|
|
|
|
func (s *DockerExternalGraphdriverSuite) SetUpSuite(c *check.C) {
|
|
mux := http.NewServeMux()
|
|
s.server = httptest.NewServer(mux)
|
|
|
|
type graphDriverRequest struct {
|
|
ID string `json:",omitempty"`
|
|
Parent string `json:",omitempty"`
|
|
MountLabel string `json:",omitempty"`
|
|
}
|
|
|
|
type graphDriverResponse struct {
|
|
Err error `json:",omitempty"`
|
|
Dir string `json:",omitempty"`
|
|
Exists bool `json:",omitempty"`
|
|
Status [][2]string `json:",omitempty"`
|
|
Metadata map[string]string `json:",omitempty"`
|
|
Changes []archive.Change `json:",omitempty"`
|
|
Size int64 `json:",omitempty"`
|
|
}
|
|
|
|
respond := func(w http.ResponseWriter, data interface{}) {
|
|
w.Header().Set("Content-Type", "appplication/vnd.docker.plugins.v1+json")
|
|
switch t := data.(type) {
|
|
case error:
|
|
fmt.Fprintln(w, fmt.Sprintf(`{"Err": %q}`, t.Error()))
|
|
case string:
|
|
fmt.Fprintln(w, t)
|
|
default:
|
|
json.NewEncoder(w).Encode(&data)
|
|
}
|
|
}
|
|
|
|
decReq := func(b io.ReadCloser, out interface{}, w http.ResponseWriter) error {
|
|
defer b.Close()
|
|
if err := json.NewDecoder(b).Decode(&out); err != nil {
|
|
http.Error(w, fmt.Sprintf("error decoding json: %s", err.Error()), 500)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
base, err := ioutil.TempDir("", "external-graph-test")
|
|
c.Assert(err, check.IsNil)
|
|
vfsProto, err := vfs.Init(base, []string{}, nil, nil)
|
|
c.Assert(err, check.IsNil, check.Commentf("error initializing graph driver"))
|
|
driver := graphdriver.NewNaiveDiffDriver(vfsProto, nil, nil)
|
|
|
|
mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
|
|
s.ec.activations++
|
|
respond(w, `{"Implements": ["GraphDriver"]}`)
|
|
})
|
|
|
|
mux.HandleFunc("/GraphDriver.Init", func(w http.ResponseWriter, r *http.Request) {
|
|
s.ec.init++
|
|
respond(w, "{}")
|
|
})
|
|
|
|
mux.HandleFunc("/GraphDriver.Create", func(w http.ResponseWriter, r *http.Request) {
|
|
s.ec.creations++
|
|
|
|
var req graphDriverRequest
|
|
if err := decReq(r.Body, &req, w); err != nil {
|
|
return
|
|
}
|
|
if err := driver.Create(req.ID, req.Parent, ""); err != nil {
|
|
respond(w, err)
|
|
return
|
|
}
|
|
respond(w, "{}")
|
|
})
|
|
|
|
mux.HandleFunc("/GraphDriver.Remove", func(w http.ResponseWriter, r *http.Request) {
|
|
s.ec.removals++
|
|
|
|
var req graphDriverRequest
|
|
if err := decReq(r.Body, &req, w); err != nil {
|
|
return
|
|
}
|
|
|
|
if err := driver.Remove(req.ID); err != nil {
|
|
respond(w, err)
|
|
return
|
|
}
|
|
respond(w, "{}")
|
|
})
|
|
|
|
mux.HandleFunc("/GraphDriver.Get", func(w http.ResponseWriter, r *http.Request) {
|
|
s.ec.gets++
|
|
|
|
var req graphDriverRequest
|
|
if err := decReq(r.Body, &req, w); err != nil {
|
|
return
|
|
}
|
|
|
|
dir, err := driver.Get(req.ID, req.MountLabel)
|
|
if err != nil {
|
|
respond(w, err)
|
|
return
|
|
}
|
|
respond(w, &graphDriverResponse{Dir: dir})
|
|
})
|
|
|
|
mux.HandleFunc("/GraphDriver.Put", func(w http.ResponseWriter, r *http.Request) {
|
|
s.ec.puts++
|
|
|
|
var req graphDriverRequest
|
|
if err := decReq(r.Body, &req, w); err != nil {
|
|
return
|
|
}
|
|
|
|
if err := driver.Put(req.ID); err != nil {
|
|
respond(w, err)
|
|
return
|
|
}
|
|
respond(w, "{}")
|
|
})
|
|
|
|
mux.HandleFunc("/GraphDriver.Exists", func(w http.ResponseWriter, r *http.Request) {
|
|
s.ec.exists++
|
|
|
|
var req graphDriverRequest
|
|
if err := decReq(r.Body, &req, w); err != nil {
|
|
return
|
|
}
|
|
respond(w, &graphDriverResponse{Exists: driver.Exists(req.ID)})
|
|
})
|
|
|
|
mux.HandleFunc("/GraphDriver.Status", func(w http.ResponseWriter, r *http.Request) {
|
|
s.ec.stats++
|
|
respond(w, &graphDriverResponse{Status: driver.Status()})
|
|
})
|
|
|
|
mux.HandleFunc("/GraphDriver.Cleanup", func(w http.ResponseWriter, r *http.Request) {
|
|
s.ec.cleanups++
|
|
err := driver.Cleanup()
|
|
if err != nil {
|
|
respond(w, err)
|
|
return
|
|
}
|
|
respond(w, `{}`)
|
|
})
|
|
|
|
mux.HandleFunc("/GraphDriver.GetMetadata", func(w http.ResponseWriter, r *http.Request) {
|
|
s.ec.metadata++
|
|
|
|
var req graphDriverRequest
|
|
if err := decReq(r.Body, &req, w); err != nil {
|
|
return
|
|
}
|
|
|
|
data, err := driver.GetMetadata(req.ID)
|
|
if err != nil {
|
|
respond(w, err)
|
|
return
|
|
}
|
|
respond(w, &graphDriverResponse{Metadata: data})
|
|
})
|
|
|
|
mux.HandleFunc("/GraphDriver.Diff", func(w http.ResponseWriter, r *http.Request) {
|
|
s.ec.diff++
|
|
|
|
var req graphDriverRequest
|
|
if err := decReq(r.Body, &req, w); err != nil {
|
|
return
|
|
}
|
|
|
|
diff, err := driver.Diff(req.ID, req.Parent)
|
|
if err != nil {
|
|
respond(w, err)
|
|
return
|
|
}
|
|
io.Copy(w, diff)
|
|
})
|
|
|
|
mux.HandleFunc("/GraphDriver.Changes", func(w http.ResponseWriter, r *http.Request) {
|
|
s.ec.changes++
|
|
var req graphDriverRequest
|
|
if err := decReq(r.Body, &req, w); err != nil {
|
|
return
|
|
}
|
|
|
|
changes, err := driver.Changes(req.ID, req.Parent)
|
|
if err != nil {
|
|
respond(w, err)
|
|
return
|
|
}
|
|
respond(w, &graphDriverResponse{Changes: changes})
|
|
})
|
|
|
|
mux.HandleFunc("/GraphDriver.ApplyDiff", func(w http.ResponseWriter, r *http.Request) {
|
|
s.ec.applydiff++
|
|
var diff archive.Reader = r.Body
|
|
defer r.Body.Close()
|
|
|
|
id := r.URL.Query().Get("id")
|
|
parent := r.URL.Query().Get("parent")
|
|
|
|
if id == "" {
|
|
http.Error(w, fmt.Sprintf("missing id"), 409)
|
|
}
|
|
|
|
size, err := driver.ApplyDiff(id, parent, diff)
|
|
if err != nil {
|
|
respond(w, err)
|
|
return
|
|
}
|
|
respond(w, &graphDriverResponse{Size: size})
|
|
})
|
|
|
|
mux.HandleFunc("/GraphDriver.DiffSize", func(w http.ResponseWriter, r *http.Request) {
|
|
s.ec.diffsize++
|
|
|
|
var req graphDriverRequest
|
|
if err := decReq(r.Body, &req, w); err != nil {
|
|
return
|
|
}
|
|
|
|
size, err := driver.DiffSize(req.ID, req.Parent)
|
|
if err != nil {
|
|
respond(w, err)
|
|
return
|
|
}
|
|
respond(w, &graphDriverResponse{Size: size})
|
|
})
|
|
|
|
err = os.MkdirAll("/etc/docker/plugins", 0755)
|
|
c.Assert(err, check.IsNil, check.Commentf("error creating /etc/docker/plugins"))
|
|
|
|
err = ioutil.WriteFile("/etc/docker/plugins/test-external-graph-driver.spec", []byte(s.server.URL), 0644)
|
|
c.Assert(err, check.IsNil, check.Commentf("error writing to /etc/docker/plugins/test-external-graph-driver.spec"))
|
|
}
|
|
|
|
func (s *DockerExternalGraphdriverSuite) TearDownSuite(c *check.C) {
|
|
s.server.Close()
|
|
|
|
err := os.RemoveAll("/etc/docker/plugins")
|
|
c.Assert(err, check.IsNil, check.Commentf("error removing /etc/docker/plugins"))
|
|
}
|
|
|
|
func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriver(c *check.C) {
|
|
if err := s.d.StartWithBusybox("-s", "test-external-graph-driver"); err != nil {
|
|
b, _ := ioutil.ReadFile(s.d.LogfileName())
|
|
c.Assert(err, check.IsNil, check.Commentf("\n%s", string(b)))
|
|
}
|
|
|
|
out, err := s.d.Cmd("run", "-d", "--name=graphtest", "busybox", "sh", "-c", "echo hello > /hello")
|
|
c.Assert(err, check.IsNil, check.Commentf(out))
|
|
|
|
err = s.d.Restart("-s", "test-external-graph-driver")
|
|
|
|
out, err = s.d.Cmd("inspect", "--format='{{.GraphDriver.Name}}'", "graphtest")
|
|
c.Assert(err, check.IsNil, check.Commentf(out))
|
|
c.Assert(strings.TrimSpace(out), check.Equals, "test-external-graph-driver")
|
|
|
|
out, err = s.d.Cmd("diff", "graphtest")
|
|
c.Assert(err, check.IsNil, check.Commentf(out))
|
|
c.Assert(strings.Contains(out, "A /hello"), check.Equals, true)
|
|
|
|
out, err = s.d.Cmd("rm", "-f", "graphtest")
|
|
c.Assert(err, check.IsNil, check.Commentf(out))
|
|
|
|
out, err = s.d.Cmd("info")
|
|
c.Assert(err, check.IsNil, check.Commentf(out))
|
|
|
|
err = s.d.Stop()
|
|
c.Assert(err, check.IsNil)
|
|
|
|
c.Assert(s.ec.activations, check.Equals, 2)
|
|
c.Assert(s.ec.init, check.Equals, 2)
|
|
c.Assert(s.ec.creations >= 1, check.Equals, true)
|
|
c.Assert(s.ec.removals >= 1, check.Equals, true)
|
|
c.Assert(s.ec.gets >= 1, check.Equals, true)
|
|
c.Assert(s.ec.puts >= 1, check.Equals, true)
|
|
c.Assert(s.ec.stats, check.Equals, 3)
|
|
c.Assert(s.ec.cleanups, check.Equals, 2)
|
|
c.Assert(s.ec.exists >= 1, check.Equals, true)
|
|
c.Assert(s.ec.applydiff >= 1, check.Equals, true)
|
|
c.Assert(s.ec.changes, check.Equals, 1)
|
|
c.Assert(s.ec.diffsize, check.Equals, 0)
|
|
c.Assert(s.ec.diff, check.Equals, 0)
|
|
c.Assert(s.ec.metadata, check.Equals, 1)
|
|
}
|
|
|
|
func (s *DockerExternalGraphdriverSuite) TestExternalGraphDriverPull(c *check.C) {
|
|
testRequires(c, Network)
|
|
c.Assert(s.d.Start(), check.IsNil)
|
|
|
|
out, err := s.d.Cmd("pull", "busybox:latest")
|
|
c.Assert(err, check.IsNil, check.Commentf(out))
|
|
|
|
out, err = s.d.Cmd("run", "-d", "busybox", "top")
|
|
c.Assert(err, check.IsNil, check.Commentf(out))
|
|
}
|