Merge pull request #236 from trusch/feature/tls-transport
transport: add possibility to use TLS secured transport layerdocker-18.09
commit
3b9e679ddb
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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...)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue