1
1
package image
2
2
3
3
import (
4
+ "bytes"
4
5
"context"
6
+ "crypto/ecdsa"
7
+ "crypto/elliptic"
8
+ "crypto/rand"
9
+ "crypto/tls"
10
+ "crypto/x509"
11
+ "crypto/x509/pkix"
12
+ "encoding/pem"
5
13
"fmt"
14
+ "io"
15
+ "io/ioutil"
16
+ "k8s.io/apimachinery/pkg/util/wait"
17
+ "math/big"
18
+ "net"
19
+ "net/http"
20
+ "os"
6
21
"time"
7
22
8
23
"github.com/docker/distribution/configuration"
@@ -15,14 +30,45 @@ import (
15
30
// RunDockerRegistry runs a docker registry on an available port and returns its host string if successful, otherwise it returns an error.
16
31
// If the rootDir argument isn't empty, the registry is configured to use this as the root directory for persisting image data to the filesystem.
17
32
// If the rootDir argument is empty, the registry is configured to keep image data in memory.
18
- func RunDockerRegistry (ctx context.Context , rootDir string ) (string , error ) {
33
+ func RunDockerRegistry (ctx context.Context , rootDir string ) (string , string , error ) {
19
34
dockerPort , err := freeport .GetFreePort ()
20
35
if err != nil {
21
- return "" , err
36
+ return "" , "" , err
37
+ }
38
+ host := fmt .Sprintf ("localhost:%d" , dockerPort )
39
+ certPool := x509 .NewCertPool ()
40
+
41
+ cafile , err := ioutil .TempFile ("" , "ca" )
42
+ if err != nil {
43
+ return "" , "" , err
44
+ }
45
+ certfile , err := ioutil .TempFile ("" , "cert" )
46
+ if err != nil {
47
+ return "" , "" , err
48
+ }
49
+ keyfile , err := ioutil .TempFile ("" , "key" )
50
+ if err != nil {
51
+ return "" , "" , err
52
+ }
53
+ if err := GenerateCerts (cafile , certfile , keyfile , certPool ); err != nil {
54
+ return "" , "" , err
55
+ }
56
+ if err := cafile .Close (); err != nil {
57
+ return "" , "" , err
58
+ }
59
+ if err := certfile .Close (); err != nil {
60
+ return "" , "" , err
61
+ }
62
+ if err := keyfile .Close (); err != nil {
63
+ return "" , "" , err
22
64
}
23
65
24
66
config := & configuration.Configuration {}
25
- config .HTTP .Addr = fmt .Sprintf (":%d" , dockerPort )
67
+ config .HTTP .Addr = host
68
+ config .HTTP .TLS .Certificate = certfile .Name ()
69
+ config .HTTP .TLS .Key = keyfile .Name ()
70
+ config .Log .Level = "debug"
71
+
26
72
if rootDir != "" {
27
73
config .Storage = map [string ]configuration.Parameters {"filesystem" : map [string ]interface {}{
28
74
"rootdirectory" : rootDir ,
@@ -34,15 +80,131 @@ func RunDockerRegistry(ctx context.Context, rootDir string) (string, error) {
34
80
35
81
dockerRegistry , err := registry .NewRegistry (ctx , config )
36
82
if err != nil {
37
- return "" , err
83
+ return "" , "" , err
38
84
}
39
85
40
86
go func () {
87
+ defer func () {
88
+ os .Remove (cafile .Name ())
89
+ os .Remove (certfile .Name ())
90
+ os .Remove (keyfile .Name ())
91
+ }()
41
92
if err := dockerRegistry .ListenAndServe (); err != nil {
42
93
panic (fmt .Errorf ("docker registry stopped listening: %v" , err ))
43
94
}
44
95
}()
45
96
97
+ err = wait .Poll (100 * time .Millisecond , 10 * time .Second , func () (done bool , err error ) {
98
+ tr := & http.Transport {TLSClientConfig : & tls.Config {
99
+ InsecureSkipVerify : false ,
100
+ RootCAs : certPool ,
101
+ }}
102
+ client := & http.Client {Transport : tr }
103
+ r , err := client .Get ("https://" + host + "/v2/" )
104
+ if err != nil {
105
+ return false , nil
106
+ }
107
+ if r .StatusCode == http .StatusOK {
108
+ return true , nil
109
+ }
110
+ return false , nil
111
+ })
112
+ if err != nil {
113
+ return "" , "" , err
114
+ }
115
+
46
116
// Return the registry host string
47
- return fmt .Sprintf ("localhost:%d" , dockerPort ), nil
117
+ return host , cafile .Name (), nil
118
+ }
119
+
120
+ func certToPem (der []byte ) ([]byte , error ) {
121
+ out := & bytes.Buffer {}
122
+ if err := pem .Encode (out , & pem.Block {Type : "CERTIFICATE" , Bytes : der }); err != nil {
123
+ return nil , err
124
+ }
125
+ return out .Bytes (), nil
126
+ }
127
+
128
+ func keyToPem (key * ecdsa.PrivateKey ) ([]byte , error ) {
129
+ b , err := x509 .MarshalECPrivateKey (key )
130
+ if err != nil {
131
+ return nil , fmt .Errorf ( "unable to marshal private key: %v" , err )
132
+ }
133
+ out := & bytes.Buffer {}
134
+ if err := pem .Encode (out , & pem.Block {Type : "EC PRIVATE KEY" , Bytes : b }); err != nil {
135
+ return nil , err
136
+ }
137
+ return out .Bytes (), nil
138
+ }
139
+
140
+ func GenerateCerts (caWriter , certWriter , keyWriter io.Writer , pool * x509.CertPool ) error {
141
+ priv , err := ecdsa .GenerateKey (elliptic .P521 (), rand .Reader )
142
+ if err != nil {
143
+ return err
144
+ }
145
+ ca := x509.Certificate {
146
+ SerialNumber : big .NewInt (1 ),
147
+ Subject : pkix.Name {
148
+ Organization : []string {"test ca" },
149
+ },
150
+ NotBefore : time .Now (),
151
+ NotAfter : time .Now ().Add (time .Hour ),
152
+
153
+ KeyUsage : x509 .KeyUsageKeyEncipherment | x509 .KeyUsageDigitalSignature | x509 .KeyUsageCertSign ,
154
+ ExtKeyUsage : []x509.ExtKeyUsage {x509 .ExtKeyUsageServerAuth },
155
+ BasicConstraintsValid : true ,
156
+ IsCA : true ,
157
+ }
158
+ cert := x509.Certificate {
159
+ SerialNumber : big .NewInt (2 ),
160
+ Subject : pkix.Name {
161
+ Organization : []string {"test cert" },
162
+ },
163
+ NotBefore : time .Now (),
164
+ NotAfter : time .Now ().Add (time .Hour ),
165
+
166
+ KeyUsage : x509 .KeyUsageKeyEncipherment | x509 .KeyUsageDigitalSignature ,
167
+ ExtKeyUsage : []x509.ExtKeyUsage {x509 .ExtKeyUsageServerAuth },
168
+ BasicConstraintsValid : true ,
169
+ IPAddresses : []net.IP {
170
+ net .ParseIP ("127.0.0.1" ),
171
+ net .ParseIP ("::1" ),
172
+ },
173
+ DNSNames : []string {"localhost" },
174
+ }
175
+
176
+ caBytes , err := x509 .CreateCertificate (rand .Reader , & ca , & ca , & priv .PublicKey , priv )
177
+ if err != nil {
178
+ return err
179
+ }
180
+
181
+ caFile , err := certToPem (caBytes )
182
+ if err != nil {
183
+ return err
184
+ }
185
+ if _ , err := caWriter .Write (caFile ); err != nil {
186
+ return err
187
+ }
188
+ pool .AppendCertsFromPEM (caFile )
189
+
190
+ certBytes , err := x509 .CreateCertificate (rand .Reader , & cert , & ca , & priv .PublicKey , priv )
191
+ if err != nil {
192
+ return err
193
+ }
194
+ certFile , err := certToPem (certBytes )
195
+ if err != nil {
196
+ return err
197
+ }
198
+ if _ , err := certWriter .Write (certFile ); err != nil {
199
+ return err
200
+ }
201
+
202
+ keyFile , err := keyToPem (priv )
203
+ if err != nil {
204
+ return err
205
+ }
206
+ if _ , err := keyWriter .Write (keyFile ); err != nil {
207
+ return err
208
+ }
209
+ return nil
48
210
}
0 commit comments