diff --git a/backends/backends.go b/backends/backends.go index 8c2bf45a60..aa42d94137 100644 --- a/backends/backends.go +++ b/backends/backends.go @@ -18,5 +18,6 @@ func New() *beam.Object { backends.Bind("forward", Forward()) backends.Bind("exec", Exec()) backends.Bind("dockerserver", DockerServer()) + backends.Bind("orchard", Orchard()) return beam.Obj(backends) } diff --git a/backends/orchard.go b/backends/orchard.go new file mode 100644 index 0000000000..4fbf5ebe09 --- /dev/null +++ b/backends/orchard.go @@ -0,0 +1,124 @@ +package backends + +import ( + "github.com/docker/libswarm/beam" + "github.com/orchardup/go-orchard/api" + + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "os" +) + +func Orchard() beam.Sender { + backend := beam.NewServer() + backend.OnSpawn(beam.Handler(func(ctx *beam.Message) error { + if len(ctx.Args) != 2 { + return fmt.Errorf("orchard: spawn expects 2 arguments: API token and name of host") + } + apiToken, hostName := ctx.Args[0], ctx.Args[1] + + apiClient := &api.HTTPClient{ + BaseURL: "https://api.orchardup.com/v2", + Token: apiToken, + } + + host, err := apiClient.GetHost(hostName) + if err != nil { + return err + } + + url := fmt.Sprintf("tcp://%s:4243", host.IPAddress) + tlsConfig, err := getTLSConfig([]byte(host.ClientCert), []byte(host.ClientKey)) + if err != nil { + return err + } + + client := newClient() + client.scheme = "https" + client.transport.TLSClientConfig = tlsConfig + client.urlHost = host.IPAddress + + forwardBackend := beam.Obj(ForwardWithClient(client)) + forwardInstance, err := forwardBackend.Spawn(url) + if err != nil { + return err + } + + _, err = ctx.Ret.Send(&beam.Message{Verb: beam.Ack, Ret: forwardInstance}) + return err + })) + return backend +} + +func getTLSConfig(clientCertPEMData, clientKeyPEMData []byte) (*tls.Config, error) { + certPool := x509.NewCertPool() + + certChainPath := os.Getenv("ORCHARD_HOST_CA") + if certChainPath != "" { + certChainData, err := ioutil.ReadFile(certChainPath) + if err != nil { + return nil, err + } + certPool.AppendCertsFromPEM(certChainData) + } else { + certPool.AppendCertsFromPEM([]byte(orchardCerts)) + } + + clientCert, err := tls.X509KeyPair(clientCertPEMData, clientKeyPEMData) + if err != nil { + return nil, err + } + + config := new(tls.Config) + config.RootCAs = certPool + config.Certificates = []tls.Certificate{clientCert} + config.BuildNameToCertificate() + + return config, nil +} + +var orchardCerts string = `-----BEGIN CERTIFICATE----- +MIIDizCCAnOgAwIBAgIJANOkcdAljaXsMA0GCSqGSIb3DQEBBQUAMFwxCzAJBgNV +BAYTAkdCMQ8wDQYDVQQIDAZMb25kb24xIjAgBgNVBAoMGU9yY2hhcmQgTGFib3Jh +dG9yaWVzIEx0ZC4xGDAWBgNVBAMMD09yY2hhcmQgUm9vdCBDQTAeFw0xMzA5MTAx +OTU4MDZaFw0xODA5MDkxOTU4MDZaMFwxCzAJBgNVBAYTAkdCMQ8wDQYDVQQIDAZM +b25kb24xIjAgBgNVBAoMGU9yY2hhcmQgTGFib3JhdG9yaWVzIEx0ZC4xGDAWBgNV +BAMMD09yY2hhcmQgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAKE2uHYhJyUxTa/DgXE2Ru85RJptv7YypqL6/hEthhSb30e8c/UZ2RGAupgr +2KhP2B/c78d7hMIX09rbc/Z7TpnV4T3ERuguDJ6jz6NjLKDsW8VVBMx4imcE0hHB +ZrhO+cEuBBufw+iW83uNIkzzIVVlgZ6o9jGXEY61D+SNNxtsMEIkjh+5/JxdeRvK +PSHhBJ2VJpCRWpvuhEuCc5Qlz6PkWEbCchEe8Mzy6Zy8FzT4q4t+ztryWTUavR9s +8lv3N7vHMo4R/r1M+VtjlXzutV8S83avrUQ48woTGBULyfXisbSN6snCXf4VJx8E +UU48FjQBZbKfgV+2ut4f2oUdkNsCAwEAAaNQME4wHQYDVR0OBBYEFBM7bwyZ7n42 +9q3EmgaOU7PGa4xtMB8GA1UdIwQYMBaAFBM7bwyZ7n429q3EmgaOU7PGa4xtMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADZ5YUYc3WfiKWg1VqmBj0EE +wVhy43rId0ruMwcNgyDesCvIDJy5Y9XsIRnRIIeM3tm0MF+fGlmOiN1AvX0KeiTM +7RtPYFawl62aGrDvo0CdZTxCYSRcLvUhgGiftEnqRijawecnk5BhcP+g5Zxe8b+L +DzqbCwG9AQ9M2NAxWdbaBJwAL8qceKklVGWOEpjEYiF4zdNQoLfW+lygVBvKZEwF +By4x8aTPlf8MMMn2ogk4Js8ZcjmvP9fBlA09ecD1DO9lNWMXpDyoB504qTPGoLoj +u6XKHvAXEXvMzLHp7qABqZXDgKIcr8wAqnmqlnFHpLjd+bV7bGMHjGM8EeIdt8I= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDjTCCAnWgAwIBAgIJANOkcdAljaXwMA0GCSqGSIb3DQEBBQUAMFwxCzAJBgNV +BAYTAkdCMQ8wDQYDVQQIDAZMb25kb24xIjAgBgNVBAoMGU9yY2hhcmQgTGFib3Jh +dG9yaWVzIEx0ZC4xGDAWBgNVBAMMD09yY2hhcmQgUm9vdCBDQTAeFw0xMzA5MTIx +ODE5MTRaFw0xODA5MTExODE5MTRaMF4xCzAJBgNVBAYTAkdCMQ8wDQYDVQQIDAZM +b25kb24xIjAgBgNVBAoMGU9yY2hhcmQgTGFib3JhdG9yaWVzIEx0ZC4xGjAYBgNV +BAMMEU9yY2hhcmQgRG9ja2VyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA1T5SrhAZI9N3k8lESi9mYJ9aAqaNESF1qGqWh4Vk2ek0J9X76tPXTK0/ +mn9kVMhHFusSHw6EV9imORIdJd9ivdqfMpEPeBuYNuZTNQYsVPP//ZwBPA5+dVOK +dBH+OjgLne8oHIgNM1lRZQlTWrE9FrD11VVnTNcI3VfuDjPD7z5FeYb+gRQx5/u4 +gp+xLfglquCzbaPRqQ9FhPB7MFkiQDfZuZieAWZ4QOLJb4za582OX2Gl6mUbIOc7 +TQcxeifIwUnSBunq8ER6donjfLy/vJUMBITw4LfzVFpuDnki5FI1DzY+GaVzRSOn +JiGT+WxPH2ydgDieKL1cqB7i+/6o4QIDAQABo1AwTjAdBgNVHQ4EFgQUp/8F8VUv +P+hIDEOf/fzXSujHjYAwHwYDVR0jBBgwFoAUEztvDJnufjb2rcSaBo5Ts8ZrjG0w +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAcbK4gNKRipaDBUwUWU7Z +YXP+npiGEaKxEgBf2J0FFi0yfKRTdACj42vWTZI2m26A1be04xrXZhBKPm4+zN6U +SjEjl+jIKSR7E0QvFv9fFJ0hWl5CDcg8EJdFckgawH2MfSBb4qjN4MDygtOet35Z +VmA7V3AaHa7d2xP+dyER+qP5/ysWzHqliephEpDj4QQIK+bQWwBXj91LhRsyHdn7 +VBHS10FcJegbD86SLb85U7zFPZ+vWClDLWwh2hN7ApAGjJgQrfHFhanLi51MIkM2 +FHytSi4zdRd+9nEbSVc4t1CL/llaSFk7W77hMFZpq+J+ih6aC0echGQIoXfdHfSM +Qg== +-----END CERTIFICATE-----`