Merge pull request #236 from trusch/feature/tls-transport

transport: add possibility to use TLS secured transport layer
docker-18.09
Tõnis Tiigi 2017-12-21 08:18:33 -08:00 committed by GitHub
commit 3b9e679ddb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 164 additions and 5 deletions

View File

@ -1,12 +1,16 @@
package client
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"time"
controlapi "github.com/moby/buildkit/api/services/control"
"github.com/moby/buildkit/util/appdefaults"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
type Client struct {
@ -18,15 +22,26 @@ type ClientOpt interface{}
// New returns a new buildkit client. Address can be empty for the system-default address.
func New(address string, opts ...ClientOpt) (*Client, error) {
gopts := []grpc.DialOption{
grpc.WithInsecure(),
grpc.WithTimeout(30 * time.Second),
grpc.WithDialer(dialer),
grpc.FailOnNonTempDialError(true),
}
needWithInsecure := true
for _, o := range opts {
if _, ok := o.(*withBlockOpt); ok {
gopts = append(gopts, grpc.WithBlock(), grpc.FailOnNonTempDialError(true))
}
if credInfo, ok := o.(*withCredentials); ok {
opt, err := loadCredentials(credInfo)
if err != nil {
return nil, err
}
gopts = append(gopts, opt)
needWithInsecure = false
}
}
if needWithInsecure {
gopts = append(gopts, grpc.WithInsecure())
}
if address == "" {
address = appdefaults.Address
@ -54,3 +69,49 @@ type withBlockOpt struct{}
func WithBlock() ClientOpt {
return &withBlockOpt{}
}
type withCredentials struct {
ServerName string
CACert string
Cert string
Key string
}
// WithCredentials configures the TLS parameters of the client.
// Arguments:
// * serverName: specifies the name of the target server
// * ca: specifies the filepath of the CA certificate to use for verification
// * cert: specifies the filepath of the client certificate
// * key: specifies the filepath of the client key
func WithCredentials(serverName, ca, cert, key string) ClientOpt {
return &withCredentials{serverName, ca, cert, key}
}
func loadCredentials(opts *withCredentials) (grpc.DialOption, error) {
ca, err := ioutil.ReadFile(opts.CACert)
if err != nil {
return nil, errors.Wrap(err, "could not read ca certificate")
}
certPool := x509.NewCertPool()
if ok := certPool.AppendCertsFromPEM(ca); !ok {
return nil, errors.New("failed to append ca certs")
}
cfg := &tls.Config{
ServerName: opts.ServerName,
RootCAs: certPool,
}
// we will produce an error if the user forgot about either cert or key if at least one is specified
if opts.Cert != "" || opts.Key != "" {
cert, err := tls.LoadX509KeyPair(opts.Cert, opts.Key)
if err != nil {
return nil, errors.Wrap(err, "could not read certificate/key")
}
cfg.Certificates = []tls.Certificate{cert}
cfg.BuildNameToCertificate()
}
return grpc.WithTransportCredentials(credentials.NewTLS(cfg)), nil
}

View File

@ -2,6 +2,7 @@ package main
import (
"fmt"
"net/url"
"os"
"github.com/moby/buildkit/client"
@ -28,9 +29,29 @@ func main() {
},
cli.StringFlag{
Name: "addr",
Usage: "listening address",
Usage: "buildkitd address",
Value: defaultAddress,
},
cli.StringFlag{
Name: "tlsservername",
Usage: "buildkitd server name for certificate validation",
Value: "",
},
cli.StringFlag{
Name: "tlscacert",
Usage: "CA certificate for validation",
Value: "",
},
cli.StringFlag{
Name: "tlscert",
Usage: "client certificate",
Value: "",
},
cli.StringFlag{
Name: "tlskey",
Usage: "client key",
Value: "",
},
}
app.Commands = []cli.Command{
@ -62,5 +83,21 @@ func main() {
}
func resolveClient(c *cli.Context) (*client.Client, error) {
return client.New(c.GlobalString("addr"), client.WithBlock())
serverName := c.GlobalString("tlsservername")
if serverName == "" {
// guess servername as hostname of target address
uri, err := url.Parse(c.GlobalString("addr"))
if err != nil {
return nil, err
}
serverName = uri.Hostname()
}
caCert := c.GlobalString("tlscacert")
cert := c.GlobalString("tlscert")
key := c.GlobalString("tlskey")
opts := []client.ClientOpt{client.WithBlock()}
if caCert != "" || cert != "" || key != "" {
opts = append(opts, client.WithCredentials(serverName, caCert, cert, key))
}
return client.New(c.GlobalString("addr"), opts...)
}

View File

@ -1,7 +1,10 @@
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
@ -27,6 +30,7 @@ import (
"golang.org/x/net/context"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
type workerInitializerOpt struct {
@ -79,6 +83,18 @@ func main() {
Usage: "debugging address (eg. 0.0.0.0:6060)",
Value: "",
},
cli.StringFlag{
Name: "tlscert",
Usage: "certificate file to use",
},
cli.StringFlag{
Name: "tlskey",
Usage: "key file to use",
},
cli.StringFlag{
Name: "tlscacert",
Usage: "ca certificate to verify clients",
},
}
app.Flags = append(app.Flags, appFlags...)
@ -91,8 +107,15 @@ func main() {
return err
}
}
server := grpc.NewServer(unaryInterceptor(ctx))
opts := []grpc.ServerOption{unaryInterceptor(ctx)}
creds, err := serverCredentials(c)
if err != nil {
return err
}
if creds != nil {
opts = append(opts, creds)
}
server := grpc.NewServer(opts...)
// relative path does not work with nightlyone/lockfile
root, err := filepath.Abs(c.GlobalString("root"))
@ -214,6 +237,44 @@ func unaryInterceptor(globalCtx context.Context) grpc.ServerOption {
})
}
func serverCredentials(c *cli.Context) (grpc.ServerOption, error) {
certFile := c.GlobalString("tlscert")
keyFile := c.GlobalString("tlskey")
caFile := c.GlobalString("tlscacert")
if certFile == "" && keyFile == "" {
return nil, nil
}
err := errors.New("you must specify key and cert file if one is specified")
if certFile == "" {
return nil, err
}
if keyFile == "" {
return nil, err
}
certificate, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, errors.Wrap(err, "could not load server key pair")
}
tlsConf := &tls.Config{
Certificates: []tls.Certificate{certificate},
}
if caFile != "" {
certPool := x509.NewCertPool()
ca, err := ioutil.ReadFile(caFile)
if err != nil {
return nil, errors.Wrap(err, "could not read ca certificate")
}
// Append the client certificates from the CA
if ok := certPool.AppendCertsFromPEM(ca); !ok {
return nil, errors.New("failed to append ca cert")
}
tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
tlsConf.ClientCAs = certPool
}
creds := grpc.Creds(credentials.NewTLS(tlsConf))
return creds, nil
}
func newController(c *cli.Context, root string) (*control.Controller, error) {
sessionManager, err := session.NewManager()
if err != nil {