// NewExternalSigningServer creates and runs a new ExternalSigningServer which // uses the given rootCA to sign node certificates. A server key and cert are // generated and saved into the given basedir and then a TLS listener is // started on a random available port. On success, an HTTPS server will be // running in a separate goroutine. The URL of the singing endpoint is // available in the returned *ExternalSignerServer value. Calling the Close() // method will stop the server. func NewExternalSigningServer(rootCA ca.RootCA, basedir string) (*ExternalSigningServer, error) { serverCN := "external-ca-example-server" serverOU := "localhost" // Make a valid server cert for localhost. // Create TLS credentials for the external CA server which we will run. serverPaths := ca.CertPaths{ Cert: filepath.Join(basedir, "server.crt"), Key: filepath.Join(basedir, "server.key"), } serverCert, err := rootCA.IssueAndSaveNewCertificates(ca.NewKeyReadWriter(serverPaths, nil, nil), serverCN, serverOU, "") if err != nil { return nil, errors.Wrap(err, "unable to get TLS server certificate") } serverTLSConfig := &tls.Config{ Certificates: []tls.Certificate{*serverCert}, ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: rootCA.Pool, } tlsListener, err := tls.Listen("tcp", "localhost:0", serverTLSConfig) if err != nil { return nil, errors.Wrap(err, "unable to create TLS connection listener") } assignedPort := tlsListener.Addr().(*net.TCPAddr).Port signURL := url.URL{ Scheme: "https", Host: net.JoinHostPort("localhost", strconv.Itoa(assignedPort)), Path: "/sign", } ess := &ExternalSigningServer{ listener: tlsListener, URL: signURL.String(), } mux := http.NewServeMux() handler := &signHandler{ numIssued: &ess.NumIssued, rootCA: rootCA, flaky: &ess.flaky, } mux.Handle(signURL.Path, handler) server := &http.Server{ Handler: mux, } go server.Serve(tlsListener) return ess, nil }
// newNode creates new node with specific role(manager or agent) and joins to // existing cluster. if joinAddr is empty string, then new cluster will be initialized. // It uses TestExecutor as executor. If lateBind is set, the remote API port is not // bound. If rootCA is set, this root is used to bootstrap the node's TLS certs. func newTestNode(joinAddr, joinToken string, lateBind bool, rootCA *ca.RootCA) (*testNode, error) { tmpDir, err := ioutil.TempDir("", "swarmkit-integration-") if err != nil { return nil, err } cAddr := filepath.Join(tmpDir, "control.sock") cfg := &node.Config{ ListenControlAPI: cAddr, JoinAddr: joinAddr, StateDir: tmpDir, Executor: &TestExecutor{}, JoinToken: joinToken, } if !lateBind { cfg.ListenRemoteAPI = "127.0.0.1:0" } if rootCA != nil { certDir := filepath.Join(tmpDir, "certificates") if err := os.MkdirAll(certDir, 0700); err != nil { return nil, err } certPaths := ca.NewConfigPaths(certDir) if err := ioutil.WriteFile(certPaths.RootCA.Cert, rootCA.Cert, 0644); err != nil { return nil, err } if err := ioutil.WriteFile(certPaths.RootCA.Key, rootCA.Key, 0600); err != nil { return nil, err } // generate TLS certs for this manager for bootstrapping, else the node will generate its own CA _, err := rootCA.IssueAndSaveNewCertificates(ca.NewKeyReadWriter(certPaths.Node, nil, nil), identity.NewID(), ca.ManagerRole, identity.NewID()) if err != nil { return nil, err } } node, err := node.New(cfg) if err != nil { return nil, err } return &testNode{ config: cfg, node: node, stateDir: tmpDir, }, nil }