Added notary server to docker base image.
Created trust suite which runs trust server for running trusted commands.
Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
| ... | ... |
@@ -136,6 +136,16 @@ RUN set -x \ |
| 136 | 136 |
go build -o /usr/local/bin/registry-v2 github.com/docker/distribution/cmd/registry \ |
| 137 | 137 |
&& rm -rf "$GOPATH" |
| 138 | 138 |
|
| 139 |
+# Install notary server |
|
| 140 |
+ENV NOTARY_COMMIT 77bced079e83d80f40c1f0a544b1a8a3b97fb052 |
|
| 141 |
+RUN set -x \ |
|
| 142 |
+ && export GOPATH="$(mktemp -d)" \ |
|
| 143 |
+ && git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \ |
|
| 144 |
+ && (cd "$GOPATH/src/github.com/docker/notary" && git checkout -q "$NOTARY_COMMIT") \ |
|
| 145 |
+ && GOPATH="$GOPATH/src/github.com/docker/notary/Godeps/_workspace:$GOPATH" \ |
|
| 146 |
+ go build -o /usr/local/bin/notary-server github.com/docker/notary/cmd/notary-server \ |
|
| 147 |
+ && rm -rf "$GOPATH" |
|
| 148 |
+ |
|
| 139 | 149 |
# Get the "docker-py" source so we can run their integration tests |
| 140 | 150 |
ENV DOCKER_PY_COMMIT 8a87001d09852058f08a807ab6e8491d57ca1e88 |
| 141 | 151 |
RUN git clone https://github.com/docker/docker-py.git /docker-py \ |
| ... | ... |
@@ -61,3 +61,26 @@ func (s *DockerDaemonSuite) TearDownTest(c *check.C) {
|
| 61 | 61 |
s.d.Stop() |
| 62 | 62 |
s.ds.TearDownTest(c) |
| 63 | 63 |
} |
| 64 |
+ |
|
| 65 |
+func init() {
|
|
| 66 |
+ check.Suite(&DockerTrustSuite{
|
|
| 67 |
+ ds: &DockerSuite{},
|
|
| 68 |
+ }) |
|
| 69 |
+} |
|
| 70 |
+ |
|
| 71 |
+type DockerTrustSuite struct {
|
|
| 72 |
+ ds *DockerSuite |
|
| 73 |
+ reg *testRegistryV2 |
|
| 74 |
+ not *testNotary |
|
| 75 |
+} |
|
| 76 |
+ |
|
| 77 |
+func (s *DockerTrustSuite) SetUpTest(c *check.C) {
|
|
| 78 |
+ s.reg = setupRegistry(c) |
|
| 79 |
+ s.not = setupNotary(c) |
|
| 80 |
+} |
|
| 81 |
+ |
|
| 82 |
+func (s *DockerTrustSuite) TearDownTest(c *check.C) {
|
|
| 83 |
+ s.reg.Close() |
|
| 84 |
+ s.not.Close() |
|
| 85 |
+ s.ds.TearDownTest(c) |
|
| 86 |
+} |
| ... | ... |
@@ -2,6 +2,7 @@ package main |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 |
+ "os/exec" |
|
| 5 | 6 |
"strings" |
| 6 | 7 |
|
| 7 | 8 |
"github.com/go-check/check" |
| ... | ... |
@@ -151,3 +152,33 @@ func (s *DockerSuite) TestPullImageWithAllTagFromCentralRegistry(c *check.C) {
|
| 151 | 151 |
c.Fatalf("Pulling with all tags should get more images")
|
| 152 | 152 |
} |
| 153 | 153 |
} |
| 154 |
+ |
|
| 155 |
+func (s *DockerTrustSuite) TestTrustedPull(c *check.C) {
|
|
| 156 |
+ repoName := fmt.Sprintf("%v/dockercli/trusted:latest", privateRegistryURL)
|
|
| 157 |
+ // tag the image and upload it to the private registry |
|
| 158 |
+ dockerCmd(c, "tag", "busybox", repoName) |
|
| 159 |
+ |
|
| 160 |
+ pushCmd := exec.Command(dockerBinary, "push", repoName) |
|
| 161 |
+ s.trustedCmd(pushCmd) |
|
| 162 |
+ out, _, err := runCommandWithOutput(pushCmd) |
|
| 163 |
+ if err != nil {
|
|
| 164 |
+ c.Fatalf("Error running trusted push: %s\n%s", err, out)
|
|
| 165 |
+ } |
|
| 166 |
+ if !strings.Contains(string(out), "Signing and pushing trust metadata") {
|
|
| 167 |
+ c.Fatalf("Missing expected output on trusted push:\n%s", out)
|
|
| 168 |
+ } |
|
| 169 |
+ |
|
| 170 |
+ dockerCmd(c, "rmi", repoName) |
|
| 171 |
+ |
|
| 172 |
+ // Try pull |
|
| 173 |
+ pullCmd := exec.Command(dockerBinary, "pull", repoName) |
|
| 174 |
+ s.trustedCmd(pullCmd) |
|
| 175 |
+ out, _, err = runCommandWithOutput(pullCmd) |
|
| 176 |
+ if err != nil {
|
|
| 177 |
+ c.Fatalf("Error running trusted pull: %s\n%s", err, out)
|
|
| 178 |
+ } |
|
| 179 |
+ |
|
| 180 |
+ if !strings.Contains(string(out), "Tagging") {
|
|
| 181 |
+ c.Fatalf("Missing expected output on trusted push:\n%s", out)
|
|
| 182 |
+ } |
|
| 183 |
+} |
| ... | ... |
@@ -143,3 +143,19 @@ func (s *DockerRegistrySuite) TestPushEmptyLayer(c *check.C) {
|
| 143 | 143 |
c.Fatalf("pushing the image to the private registry has failed: %s, %v", out, err)
|
| 144 | 144 |
} |
| 145 | 145 |
} |
| 146 |
+ |
|
| 147 |
+func (s *DockerTrustSuite) TestTrustedPush(c *check.C) {
|
|
| 148 |
+ repoName := fmt.Sprintf("%v/dockercli/trusted:latest", privateRegistryURL)
|
|
| 149 |
+ // tag the image and upload it to the private registry |
|
| 150 |
+ dockerCmd(c, "tag", "busybox", repoName) |
|
| 151 |
+ |
|
| 152 |
+ pushCmd := exec.Command(dockerBinary, "push", repoName) |
|
| 153 |
+ s.trustedCmd(pushCmd) |
|
| 154 |
+ out, _, err := runCommandWithOutput(pushCmd) |
|
| 155 |
+ if err != nil {
|
|
| 156 |
+ c.Fatalf("Error running trusted push: %s\n%s", err, out)
|
|
| 157 |
+ } |
|
| 158 |
+ if !strings.Contains(string(out), "Signing and pushing trust metadata") {
|
|
| 159 |
+ c.Fatalf("Missing expected output on trusted push:\n%s", out)
|
|
| 160 |
+ } |
|
| 161 |
+} |
| ... | ... |
@@ -1260,6 +1260,27 @@ func setupRegistry(c *check.C) *testRegistryV2 {
|
| 1260 | 1260 |
return reg |
| 1261 | 1261 |
} |
| 1262 | 1262 |
|
| 1263 |
+func setupNotary(c *check.C) *testNotary {
|
|
| 1264 |
+ testRequires(c, NotaryHosting) |
|
| 1265 |
+ ts, err := newTestNotary(c) |
|
| 1266 |
+ if err != nil {
|
|
| 1267 |
+ c.Fatal(err) |
|
| 1268 |
+ } |
|
| 1269 |
+ |
|
| 1270 |
+ // Wait for notary to be ready to serve requests. |
|
| 1271 |
+ for i := 1; i <= 5; i++ {
|
|
| 1272 |
+ if err = ts.Ping(); err == nil {
|
|
| 1273 |
+ break |
|
| 1274 |
+ } |
|
| 1275 |
+ time.Sleep(10 * time.Millisecond * time.Duration(i*i)) |
|
| 1276 |
+ } |
|
| 1277 |
+ |
|
| 1278 |
+ if err != nil {
|
|
| 1279 |
+ c.Fatalf("Timeout waiting for test notary to become available: %s", err)
|
|
| 1280 |
+ } |
|
| 1281 |
+ return ts |
|
| 1282 |
+} |
|
| 1283 |
+ |
|
| 1263 | 1284 |
// appendBaseEnv appends the minimum set of environment variables to exec the |
| 1264 | 1285 |
// docker cli binary for testing with correct configuration to the given env |
| 1265 | 1286 |
// list. |
| 1266 | 1287 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,19 @@ |
| 0 |
+-----BEGIN CERTIFICATE----- |
|
| 1 |
+MIIDCTCCAfOgAwIBAgIQTOoFF+ypXwgdXnXHuCTvYDALBgkqhkiG9w0BAQswJjER |
|
| 2 |
+MA8GA1UEChMIUXVpY2tUTFMxETAPBgNVBAMTCFF1aWNrVExTMB4XDTE1MDcxNzE5 |
|
| 3 |
+NDg1M1oXDTE4MDcwMTE5NDg1M1owJzERMA8GA1UEChMIUXVpY2tUTFMxEjAQBgNV |
|
| 4 |
+BAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMDO |
|
| 5 |
+qvTBAi0ApXLfe90ApJkdkRGwF838Qzt1UFSxomu5fHRV6l3FjX5XCVHiFQ4w3ROh |
|
| 6 |
+dMOu9NahfGLJv9VvWU2MV3YoY9Y7lIXpKwnK1v064wuls4nPh13BUWKQKofcY/e2 |
|
| 7 |
+qaSPd6/qmSRc/kJUvOI9jZMSX6ZRPu9K4PCqm2CivlbLq9UYuo1AbRGfuqHRvTxg |
|
| 8 |
+mQG7WQCzGSvSjuSg5qX3TEh0HckTczJG9ODULNRWNE7ld0W4sfv4VF8R7Uc/G7LO |
|
| 9 |
+8QwLCZ9TIl3gYMPCrhUL3Q6z9Jnn1SQS4mhDnPi6ugRYO1X8k3jjdxV9C2sXwUvN |
|
| 10 |
+OZI1rLEWl9TJNA7ZXtMCAwEAAaM2MDQwDgYDVR0PAQH/BAQDAgCgMAwGA1UdEwEB |
|
| 11 |
+/wQCMAAwFAYDVR0RBA0wC4IJbG9jYWxob3N0MAsGCSqGSIb3DQEBCwOCAQEAH6iq |
|
| 12 |
+kM2+UMukGDLEQKHHiauioWJlHDlLXv76bJiNfjSz94B/2XOQMb9PT04//tnGUyPK |
|
| 13 |
+K8Dx7RoxSodU6T5VRiz/A36mLOvt2t3bcL/1nHf9sAOHcexGtnCbQbW91V7RKfIL |
|
| 14 |
+sjiLNFDkQ9VfVNY+ynQptZoyH1sy07+dplfkIiPzRs5WuVAnEGsX3r6BrhgUITzi |
|
| 15 |
+g1B4kpmGZIohP4m6ZEBY5xuo/NQ0+GhjAENQMU38GpuoMyFS0i0dGcbx8weqnI/B |
|
| 16 |
+Er/qa0+GE/rBnWY8TiRow8dzpneSFQnUZpJ4EwD9IoOIDHo7k2Nbz2P50HMiCXZf |
|
| 17 |
+4RqzctVssRlrRVnO5w== |
|
| 18 |
+-----END CERTIFICATE----- |
| 0 | 19 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,27 @@ |
| 0 |
+-----BEGIN RSA PRIVATE KEY----- |
|
| 1 |
+MIIEogIBAAKCAQEAwM6q9MECLQClct973QCkmR2REbAXzfxDO3VQVLGia7l8dFXq |
|
| 2 |
+XcWNflcJUeIVDjDdE6F0w6701qF8Ysm/1W9ZTYxXdihj1juUhekrCcrW/TrjC6Wz |
|
| 3 |
+ic+HXcFRYpAqh9xj97appI93r+qZJFz+QlS84j2NkxJfplE+70rg8KqbYKK+Vsur |
|
| 4 |
+1Ri6jUBtEZ+6odG9PGCZAbtZALMZK9KO5KDmpfdMSHQdyRNzMkb04NQs1FY0TuV3 |
|
| 5 |
+Rbix+/hUXxHtRz8bss7xDAsJn1MiXeBgw8KuFQvdDrP0mefVJBLiaEOc+Lq6BFg7 |
|
| 6 |
+VfyTeON3FX0LaxfBS805kjWssRaX1Mk0Dtle0wIDAQABAoIBAHbuhNHZROhRn70O |
|
| 7 |
+Ui9vOBki/dt1ThnH5AkHQngb4t6kWjrAzILvW2p1cdBKr0ZDqftz+rzCbVD/5+Rg |
|
| 8 |
+Iq8bsnB9g23lWEBMHD/GJsAxmRA3hNooamk11IBmwTcVSsbnkdq5mEdkICYphjHC |
|
| 9 |
+Ey0DbEf6RBxWlx3WvAWLoNmTw6iFaOCH8IyLavPpe7kLbZc219oNUw2qjCnCXCZE |
|
| 10 |
+/NuViADHJBPN8r7g1gmyclJmTumdUK6oHgXEMMPe43vhReGcgcReK9QZjnTcIXPM |
|
| 11 |
+4oJOraw+BtoZXVvvIPnC+5ntoLFOzjIzM0kaveReZbdgffqF4zy2vRfCHhWssanc |
|
| 12 |
+7a0xR4ECgYEA3Xuvcqy5Xw+v/jVCO0VZj++Z7apA78dY4tWsPx5/0DUTTziTlXkC |
|
| 13 |
+ADduEbwX6HgZ/iLvA9j4C3Z4mO8qByby/6UoBU8NEe+PQt6fT7S+dKSP4uy5ZxVM |
|
| 14 |
+i5opkEyrJsMbve9Jrlj4bk5CICsydrZ+SBFHnpNGjbduGQick5LORWECgYEA3trt |
|
| 15 |
+gepteDGiUYmnnBgjbYtcD11RvpKC8Z/QwGnzN5vk4eBu8r7DkMcLN+SiHjAovlJo |
|
| 16 |
+r5j3EbF8sla1zBf/yySdQZFqUGcwtw7MaAKCLdhQl5WsViNMIx6p2OJapu0dzbv2 |
|
| 17 |
+KTXrnoRCafcH92k0dUX1ahE9eyc8KX6VhbWwXLMCgYATGCCuEDoC+gVAMzM8jOQF |
|
| 18 |
+xrBMjwr+IP+GvskUv/pg5tJ9V/FRR5dmkWDJ4p9lCUWkZTqZ6FCqHFKVTLkg2LjG |
|
| 19 |
+VWS34HLOAwskxrCRXJG22KEW/TWWr31j46yFpjZzJwrzOvftMfpo+BI3V8IH/f+x |
|
| 20 |
+EtxLzYKdoRy6x8VH67YgwQKBgHor2vjV45142FuK83AHa6SqOZXSuvWWrGJ6Ep7p |
|
| 21 |
+doSN2jRaLXi2S9AaznOdy6JxFGUCGJHrcccpXgsGrjNtFLXxJKTFa1sYtwQkALsk |
|
| 22 |
+ZOltJQF09D1krGC0driHntrUMvqOiKye+sS0DRS6cIuaCUAhUiELwoC5SaoV0zKy |
|
| 23 |
+IDUxAoGAOK8Xq+3/sqe79vTpw25RXl+nkAmOAeKjqf3Kh6jbnBhr81rmefyKXB9a |
|
| 24 |
+uj0b980tzUnliwA5cCOsyxfN2vASvMnJxFE721QZI04arlcPFHcFqCtmNnUYTcLp |
|
| 25 |
+0hgn/yLZptcoxpy+eTBu3eNsxz1Bu/Tx/198+2Wr3MbtGpLNIcA= |
|
| 26 |
+-----END RSA PRIVATE KEY----- |
| ... | ... |
@@ -74,6 +74,16 @@ var ( |
| 74 | 74 |
}, |
| 75 | 75 |
fmt.Sprintf("Test requires an environment that can host %s in the same host", v2binary),
|
| 76 | 76 |
} |
| 77 |
+ NotaryHosting = testRequirement{
|
|
| 78 |
+ func() bool {
|
|
| 79 |
+ // for now notary binary is built only if we're running inside |
|
| 80 |
+ // container through `make test`. Figure that out by testing if |
|
| 81 |
+ // notary-server binary is in PATH. |
|
| 82 |
+ _, err := exec.LookPath(notaryBinary) |
|
| 83 |
+ return err == nil |
|
| 84 |
+ }, |
|
| 85 |
+ fmt.Sprintf("Test requires an environment that can host %s in the same host", notaryBinary),
|
|
| 86 |
+ } |
|
| 77 | 87 |
NativeExecDriver = testRequirement{
|
| 78 | 88 |
func() bool {
|
| 79 | 89 |
if daemonExecDriver == "" {
|
| 80 | 90 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,110 @@ |
| 0 |
+package main |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "io/ioutil" |
|
| 5 |
+ "net" |
|
| 6 |
+ "net/http" |
|
| 7 |
+ "os" |
|
| 8 |
+ "os/exec" |
|
| 9 |
+ "path/filepath" |
|
| 10 |
+ "time" |
|
| 11 |
+ |
|
| 12 |
+ "github.com/docker/docker/pkg/tlsconfig" |
|
| 13 |
+ "github.com/go-check/check" |
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+var notaryBinary = "notary-server" |
|
| 17 |
+ |
|
| 18 |
+type testNotary struct {
|
|
| 19 |
+ cmd *exec.Cmd |
|
| 20 |
+ dir string |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+func newTestNotary(c *check.C) (*testNotary, error) {
|
|
| 24 |
+ template := `{
|
|
| 25 |
+ "server": {
|
|
| 26 |
+ "addr": "%s", |
|
| 27 |
+ "tls_key_file": "fixtures/notary/localhost.key", |
|
| 28 |
+ "tls_cert_file": "fixtures/notary/localhost.cert" |
|
| 29 |
+ }, |
|
| 30 |
+ "trust_service": {
|
|
| 31 |
+ "type": "local", |
|
| 32 |
+ "hostname": "", |
|
| 33 |
+ "port": "" |
|
| 34 |
+ }, |
|
| 35 |
+ "logging": {
|
|
| 36 |
+ "level": 5 |
|
| 37 |
+ } |
|
| 38 |
+}` |
|
| 39 |
+ tmp, err := ioutil.TempDir("", "notary-test-")
|
|
| 40 |
+ if err != nil {
|
|
| 41 |
+ return nil, err |
|
| 42 |
+ } |
|
| 43 |
+ confPath := filepath.Join(tmp, "config.json") |
|
| 44 |
+ config, err := os.Create(confPath) |
|
| 45 |
+ if err != nil {
|
|
| 46 |
+ return nil, err |
|
| 47 |
+ } |
|
| 48 |
+ if _, err := fmt.Fprintf(config, template, "localhost:4443"); err != nil {
|
|
| 49 |
+ os.RemoveAll(tmp) |
|
| 50 |
+ return nil, err |
|
| 51 |
+ } |
|
| 52 |
+ |
|
| 53 |
+ cmd := exec.Command(notaryBinary, "-config", confPath) |
|
| 54 |
+ if err := cmd.Start(); err != nil {
|
|
| 55 |
+ os.RemoveAll(tmp) |
|
| 56 |
+ if os.IsNotExist(err) {
|
|
| 57 |
+ c.Skip(err.Error()) |
|
| 58 |
+ } |
|
| 59 |
+ return nil, err |
|
| 60 |
+ } |
|
| 61 |
+ return &testNotary{
|
|
| 62 |
+ cmd: cmd, |
|
| 63 |
+ dir: tmp, |
|
| 64 |
+ }, nil |
|
| 65 |
+} |
|
| 66 |
+ |
|
| 67 |
+func (t *testNotary) address() string {
|
|
| 68 |
+ return "localhost:4443" |
|
| 69 |
+} |
|
| 70 |
+ |
|
| 71 |
+func (t *testNotary) Ping() error {
|
|
| 72 |
+ tlsConfig := tlsconfig.ClientDefault |
|
| 73 |
+ tlsConfig.InsecureSkipVerify = true |
|
| 74 |
+ client := http.Client{
|
|
| 75 |
+ Transport: &http.Transport{
|
|
| 76 |
+ Proxy: http.ProxyFromEnvironment, |
|
| 77 |
+ Dial: (&net.Dialer{
|
|
| 78 |
+ Timeout: 30 * time.Second, |
|
| 79 |
+ KeepAlive: 30 * time.Second, |
|
| 80 |
+ }).Dial, |
|
| 81 |
+ TLSHandshakeTimeout: 10 * time.Second, |
|
| 82 |
+ TLSClientConfig: &tlsConfig, |
|
| 83 |
+ }, |
|
| 84 |
+ } |
|
| 85 |
+ resp, err := client.Get(fmt.Sprintf("https://%s/v2/", t.address()))
|
|
| 86 |
+ if err != nil {
|
|
| 87 |
+ return err |
|
| 88 |
+ } |
|
| 89 |
+ if resp.StatusCode != 200 {
|
|
| 90 |
+ return fmt.Errorf("notary ping replied with an unexpected status code %d", resp.StatusCode)
|
|
| 91 |
+ } |
|
| 92 |
+ return nil |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 95 |
+func (t *testNotary) Close() {
|
|
| 96 |
+ t.cmd.Process.Kill() |
|
| 97 |
+ os.RemoveAll(t.dir) |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+func (s *DockerTrustSuite) trustedCmd(cmd *exec.Cmd) {
|
|
| 101 |
+ env := []string{
|
|
| 102 |
+ "DOCKER_TRUST=1", |
|
| 103 |
+ fmt.Sprintf("DOCKER_TRUST_SERVER=%s", s.not.address()),
|
|
| 104 |
+ "DOCKER_TRUST_ROOT_PASSPHRASE=12345678", |
|
| 105 |
+ "DOCKER_TRUST_TARGET_PASSPHRASE=12345678", |
|
| 106 |
+ "DOCKER_TRUST_SNAPSHOT_PASSPHRASE=12345678", |
|
| 107 |
+ } |
|
| 108 |
+ cmd.Env = append(os.Environ(), env...) |
|
| 109 |
+} |