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 |
+} |