Skip to content
This repository has been archived by the owner on Apr 3, 2024. It is now read-only.

Commit

Permalink
FIx base config loading and add TLS integration test (#141)
Browse files Browse the repository at this point in the history
  • Loading branch information
cretz committed Sep 23, 2022
1 parent a3d9d73 commit 9a56995
Show file tree
Hide file tree
Showing 13 changed files with 345 additions and 2 deletions.
9 changes: 7 additions & 2 deletions cmd/temporalite/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,13 @@ func buildCLI() *cli.App {

baseConfig := &config.Config{}
if c.IsSet(configFlag) {
baseConfig, err = config.LoadConfig("temporalite", c.String(configFlag), "")
if err != nil {
// Temporal server requires a couple of persistence config values to
// be explicitly set or the config loading fails. While these are the
// same values used internally, they are overridden later anyways,
// they are just here to pass validation.
baseConfig.Persistence.DefaultStore = liteconfig.PersistenceStoreName
baseConfig.Persistence.NumHistoryShards = 1
if err := config.Load("temporalite", c.String(configFlag), "", &baseConfig); err != nil {
return err
}
}
Expand Down
133 changes: 133 additions & 0 deletions cmd/temporalite/mtls_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// MIT License
//
// Copyright (c) 2022 Temporal Technologies Inc. All rights reserved.
//
// Copyright (c) 2021 Datadog, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package main

import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"os"
"path/filepath"
"runtime"
"strconv"
"testing"
"text/template"
"time"

"go.temporal.io/api/enums/v1"
"go.temporal.io/api/workflowservice/v1"
"go.temporal.io/sdk/client"
)

func TestMTLSConfig(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, thisFile, _, _ := runtime.Caller(0)
mtlsDir := filepath.Join(thisFile, "../../../internal/examples/mtls")

// Create temp config dir
confDir, err := os.MkdirTemp("", "temporalite-conf-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(confDir)

// Run templated config and put in temp dir
var buf bytes.Buffer
tmpl, err := template.New("temporalite.yaml.template").
Funcs(template.FuncMap{"qualified": func(s string) string { return strconv.Quote(filepath.Join(mtlsDir, s)) }}).
ParseFiles(filepath.Join(mtlsDir, "temporalite.yaml.template"))
if err != nil {
t.Fatal(err)
} else if err = tmpl.Execute(&buf, nil); err != nil {
t.Fatal(err)
} else if err = os.WriteFile(filepath.Join(confDir, "temporalite.yaml"), buf.Bytes(), 0644); err != nil {
t.Fatal(err)
}

// Run ephemerally using temp config
args := []string{
"temporalite",
"start",
"--ephemeral",
"--config", confDir,
"--namespace", "default",
"--log-format", "pretty",
"--port", "10233",
}
go func() {
if err := buildCLI().RunContext(ctx, args); err != nil {
t.Logf("CLI failed: %v", err)
}
}()

// Load client cert/key for auth
clientCert, err := tls.LoadX509KeyPair(
filepath.Join(mtlsDir, "client-cert.pem"),
filepath.Join(mtlsDir, "client-key.pem"),
)
if err != nil {
t.Fatal(err)
}
// Load server cert for CA check
serverCAPEM, err := os.ReadFile(filepath.Join(mtlsDir, "server-ca-cert.pem"))
if err != nil {
t.Fatal(err)
}
serverCAPool := x509.NewCertPool()
serverCAPool.AppendCertsFromPEM(serverCAPEM)

// Build client options and try to connect client every 100ms for 5s
options := client.Options{
HostPort: "localhost:10233",
ConnectionOptions: client.ConnectionOptions{
TLS: &tls.Config{
Certificates: []tls.Certificate{clientCert},
RootCAs: serverCAPool,
},
},
}
var c client.Client
for i := 0; i < 50; i++ {
if c, err = client.Dial(options); err == nil {
break
}
time.Sleep(100 * time.Millisecond)
}
if err != nil {
t.Fatal(err)
}

// Make a call
resp, err := c.WorkflowService().DescribeNamespace(ctx, &workflowservice.DescribeNamespaceRequest{
Namespace: "default",
})
if err != nil {
t.Fatal(err)
} else if resp.NamespaceInfo.State != enums.NAMESPACE_STATE_REGISTERED {
t.Fatalf("Bad state: %v", resp.NamespaceInfo.State)
}
}
2 changes: 2 additions & 0 deletions internal/examples/mtls/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

To rebuild the certs here, from this dir run `go run .`.
12 changes: 12 additions & 0 deletions internal/examples/mtls/client-ca-cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE-----
MIIBsjCCAVmgAwIBAgIIUwrtoQe2VdMwCgYIKoZIzj0EAwIwETEPMA0GA1UEChMG
TXkgT3JnMB4XDTEyMDkyMzEyNTYxOFoXDTMyMDkyMzEyNTYxOFowETEPMA0GA1UE
ChMGTXkgT3JnMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdbOBnic2aOkqXF17
ST12a55VrMZnQ+ZHQVxJrHFchT3RBNvgevUQEA4KdBM18wuMB8iKKLEBwgkSxiBD
muORUKOBmjCBlzAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwIG
CCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEs40yUgU5cBKYOG
46NWJJWp/iNdMDYGA1UdEQQvMC2CCWxvY2FsaG9zdIIIbXlzZXJ2ZXKHBH8AAAGH
EAAAAAAAAAAAAAAAAAAAAAEwCgYIKoZIzj0EAwIDRwAwRAIgMQhvaGk41tpbgCkK
RtIoiPul56jv0Vc4U43wkVUdoSQCIHUHgI1lHYPkiK0qNLxLtaZCVqdd1Q6glHyc
7wmgLNZD
-----END CERTIFICATE-----
5 changes: 5 additions & 0 deletions internal/examples/mtls/client-ca-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgbtnx7GN/rfVtNyta
jg3TFIt6quwLvykh+gWVE7CnDgqhRANCAAR1s4GeJzZo6SpcXXtJPXZrnlWsxmdD
5kdBXEmscVyFPdEE2+B69RAQDgp0EzXzC4wHyIoosQHCCRLGIEOa45FQ
-----END PRIVATE KEY-----
11 changes: 11 additions & 0 deletions internal/examples/mtls/client-cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBjjCCATWgAwIBAgIINZDGNLwlNKcwCgYIKoZIzj0EAwIwETEPMA0GA1UEChMG
TXkgT3JnMB4XDTEyMDkyMzEyNTYxOFoXDTMyMDkyMzEyNTYxOFowETEPMA0GA1UE
ChMGTXkgT3JnMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiNzKVhkrOvoeuyFF
YUStAb8QNQ17N4/kc0+Bq+CvjAwLvuq+cYEaqRZPxNZwtHR7IwjVjEsUKa57nnl7
UuqjwqN3MHUwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr
BgEFBQcDATAMBgNVHRMBAf8EAjAAMDYGA1UdEQQvMC2CCWxvY2FsaG9zdIIIbXlz
ZXJ2ZXKHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwCgYIKoZIzj0EAwIDRwAwRAIg
DzQSJiiz9W2rWrjv46KWZoxVp+YhRivZCkF1/Z32y3kCIGrAcf822K2CUKmV0ONi
/iExcCNepN+ErnEk8vh0bqYw
-----END CERTIFICATE-----
5 changes: 5 additions & 0 deletions internal/examples/mtls/client-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgE5Hcm8WtfcU43Wlt
QIkN1/TwiX7Vwr2ieBhjDrKGkBKhRANCAASI3MpWGSs6+h67IUVhRK0BvxA1DXs3
j+RzT4Gr4K+MDAu+6r5xgRqpFk/E1nC0dHsjCNWMSxQprnueeXtS6qPC
-----END PRIVATE KEY-----
125 changes: 125 additions & 0 deletions internal/examples/mtls/gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// MIT License
//
// Copyright (c) 2022 Temporal Technologies Inc. All rights reserved.
//
// Copyright (c) 2021 Datadog, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package main

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"log"
"math"
"math/big"
"net"
"os"
"path/filepath"
"runtime"
"time"
)

func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}

func run() error {
_, thisFile, _, _ := runtime.Caller(0)
thisDir := filepath.Dir(thisFile)
// Gen CAs and certs
if err := genCAAndCert(filepath.Join(thisDir, "server")); err != nil {
return err
} else if err = genCAAndCert(filepath.Join(thisDir, "client")); err != nil {
return err
}
return nil
}

func genCAAndCert(filePrefix string) error {
if ca, err := genCert(nil); err != nil {
return err
} else if err := os.WriteFile(filePrefix+"-ca-cert.pem", ca.certPEM, 0644); err != nil {
return err
} else if err := os.WriteFile(filePrefix+"-ca-key.pem", ca.keyPEM, 0600); err != nil {
return err
} else if cert, err := genCert(ca); err != nil {
return err
} else if err := os.WriteFile(filePrefix+"-cert.pem", cert.certPEM, 0644); err != nil {
return err
} else if err := os.WriteFile(filePrefix+"-key.pem", cert.keyPEM, 0600); err != nil {
return err
}
return nil
}

type keyPair struct {
cert *x509.Certificate
certPEM []byte
key *ecdsa.PrivateKey
keyPEM []byte
}

// Without parent this assumes it will be a CA and will self sign
func genCert(parent *keyPair) (*keyPair, error) {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
template := &x509.Certificate{
Subject: pkix.Name{Organization: []string{"My Org"}},
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback},
DNSNames: []string{"localhost", "myserver"},
NotAfter: time.Now().AddDate(10, 0, 0),
NotBefore: time.Now().AddDate(-10, 0, 0),
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
template.SerialNumber, _ = rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
signCert, signKey := template, key
if parent == nil {
template.KeyUsage |= x509.KeyUsageCertSign
template.IsCA = true
} else {
signCert, signKey = parent.cert, parent.key
}
der, err := x509.CreateCertificate(rand.Reader, template, signCert, key.Public(), signKey)
if err != nil {
return nil, err
}
cert, err := x509.ParseCertificate(der)
if err != nil {
return nil, err
}
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
keyPKCS, err := x509.MarshalPKCS8PrivateKey(key)
if err != nil {
return nil, err
}
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyPKCS})
return &keyPair{cert, certPEM, key, keyPEM}, nil
}
12 changes: 12 additions & 0 deletions internal/examples/mtls/server-ca-cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE-----
MIIBszCCAVmgAwIBAgIIaoFVwbB2RDowCgYIKoZIzj0EAwIwETEPMA0GA1UEChMG
TXkgT3JnMB4XDTEyMDkyMzEyNTYxOFoXDTMyMDkyMzEyNTYxOFowETEPMA0GA1UE
ChMGTXkgT3JnMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3ax0vvfXroLlPQjS
AX07tl3bB1Ar/Vityl/4/Hg+FnsviUdktLxw4A6NLjo7FStE5kHrNEVX5fkVTyDQ
KOEU2KOBmjCBlzAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwIG
CCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFB2px9PBdSgbI2oa
xPso8vN8PBdVMDYGA1UdEQQvMC2CCWxvY2FsaG9zdIIIbXlzZXJ2ZXKHBH8AAAGH
EAAAAAAAAAAAAAAAAAAAAAEwCgYIKoZIzj0EAwIDSAAwRQIhAK0VswSyDKG2T3Ns
/ygRVj5ffuJpiLne0Bat1u5sBHyVAiA3ddzGg2sXuNZx+8bZIMLYrwohZsAzlSi0
83csZD+odQ==
-----END CERTIFICATE-----
5 changes: 5 additions & 0 deletions internal/examples/mtls/server-ca-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgBx7Tjg/gEhgzqiMD
yuOLx21+Iv8aro3vlRsVrbZ7KEOhRANCAATdrHS+99euguU9CNIBfTu2XdsHUCv9
WK3KX/j8eD4Wey+JR2S0vHDgDo0uOjsVK0TmQes0RVfl+RVPINAo4RTY
-----END PRIVATE KEY-----
11 changes: 11 additions & 0 deletions internal/examples/mtls/server-cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBjjCCATWgAwIBAgIIbddLEmh9lIkwCgYIKoZIzj0EAwIwETEPMA0GA1UEChMG
TXkgT3JnMB4XDTEyMDkyMzEyNTYxOFoXDTMyMDkyMzEyNTYxOFowETEPMA0GA1UE
ChMGTXkgT3JnMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMFrDy5oyLGjCDxWe
Ijh2fwvI1gb9MuEm75OlyyjmStBMSTiVwljjhEOCd6W3MQQl/jEwXkZopO+OfD7Z
SIO3LKN3MHUwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr
BgEFBQcDATAMBgNVHRMBAf8EAjAAMDYGA1UdEQQvMC2CCWxvY2FsaG9zdIIIbXlz
ZXJ2ZXKHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwCgYIKoZIzj0EAwIDRwAwRAIg
VIguJe1gDHHrLrgEJB01WzXfN95rMGvsu0ZP9E9kgJ8CIDNEHlHeOsqPZlbjgdUB
BH4gzVGlr0v+TnClyFvCMWq4
-----END CERTIFICATE-----
5 changes: 5 additions & 0 deletions internal/examples/mtls/server-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgu85p7nVNMD6FgIbN
3Tdvsp6Qbm20BY/qQOn6v5W7+6OhRANCAAQwWsPLmjIsaMIPFZ4iOHZ/C8jWBv0y
4Sbvk6XLKOZK0ExJOJXCWOOEQ4J3pbcxBCX+MTBeRmik7458PtlIg7cs
-----END PRIVATE KEY-----
12 changes: 12 additions & 0 deletions internal/examples/mtls/temporalite.yaml.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
global:
tls:
frontend:
server:
certFile: {{"server-cert.pem" | qualified}}
keyFile: {{"server-key.pem" | qualified}}
clientCaFiles: [{{"client-ca-cert.pem" | qualified}}]
# Do not require client-auth so that frontend can connect to itself
# without us having to give it client keys
requireClientAuth: false
client:
rootCaFiles: [{{"server-ca-cert.pem" | qualified}}]

0 comments on commit 9a56995

Please sign in to comment.