diff --git a/.gitignore b/.gitignore index 33bb9632..822af2a6 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,7 @@ vendor/ # Executables produced by cadence-samples repo bin/ docker-compose.yml + +# Credentials +new_samples/client_samples/helloworld_tls/credentials/ +credentials/ \ No newline at end of file diff --git a/new_samples/client_samples/helloworld_tls/README.md b/new_samples/client_samples/helloworld_tls/README.md new file mode 100644 index 00000000..12f3a246 --- /dev/null +++ b/new_samples/client_samples/helloworld_tls/README.md @@ -0,0 +1,48 @@ +## Running the Sample + +### Step 1: Download Certificates +```bash +cd new_samples/client_samples/helloworld_tls/credentials +chmod +x download-certs.sh +./download-certs.sh +cd .. +``` + +### Step 2: Register the Domain +Before running workflows, you must register the "default" domain: + +```bash +cd new_samples/client_samples/helloworld_tls +go run register_domain.go +``` + +Expected output: +``` +Successfully registered domain {"domain": "default"} +``` + +If the domain already exists, you'll see: +``` +Domain already exists {"domain": "default"} +``` + +### Step 3: Start the Worker +In one terminal: +```bash +cd new_samples +go run main.go +``` + +### Step 4: Run the Sample +In another terminal: +```bash +cd new_samples/client_samples/helloworld_tls +go run hello_world_tls.go +``` + +## References + +- [Cadence Official Certificates](https://github.com/cadence-workflow/cadence/tree/master/config/credentials) +- [Cadence Documentation](https://cadenceworkflow.io/) +- [Go TLS Package](https://pkg.go.dev/crypto/tls) + diff --git a/new_samples/client_samples/helloworld_tls/credentials/download-certs.sh b/new_samples/client_samples/helloworld_tls/credentials/download-certs.sh new file mode 100644 index 00000000..ed779858 --- /dev/null +++ b/new_samples/client_samples/helloworld_tls/credentials/download-certs.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# Script to download certificates from Cadence repository +# Based on: https://github.com/cadence-workflow/cadence/tree/master/config/credentials + +set -e + +echo "Downloading certificates from Cadence repository..." + +BASE_URL="https://raw.githubusercontent.com/cadence-workflow/cadence/master/config/credentials" + +# Download all certificate files +echo "Downloading ca.cert..." +curl -L -o ca.cert "${BASE_URL}/ca.cert" + +echo "Downloading server.cert..." +curl -L -o server.cert "${BASE_URL}/server.cert" + +echo "Downloading server.key..." +curl -L -o server.key "${BASE_URL}/server.key" + +echo "Downloading client.cert..." +curl -L -o client.cert "${BASE_URL}/client.cert" + +echo "Downloading client.key..." +curl -L -o client.key "${BASE_URL}/client.key" + +# Also try to get .pem versions if they exist +echo "Trying to download .pem versions..." +curl -L -o ca.pem "${BASE_URL}/ca.pem" 2>/dev/null || echo "ca.pem not found, skipping" +curl -L -o server.pem "${BASE_URL}/server.pem" 2>/dev/null || echo "server.pem not found, skipping" +curl -L -o client.pem "${BASE_URL}/client.pem" 2>/dev/null || echo "client.pem not found, skipping" + +# Create symlinks for compatibility with existing code +if [ -f "client.cert" ]; then + ln -sf client.cert client.crt 2>/dev/null || cp client.cert client.crt +fi + +if [ -f "ca.cert" ]; then + ln -sf ca.cert keytest.crt 2>/dev/null || cp ca.cert keytest.crt +fi + +echo "" +echo "✓ Certificates downloaded successfully from Cadence repository!" +echo "" +echo "Downloaded files:" +ls -lh *.cert *.key 2>/dev/null || true +ls -lh *.pem 2>/dev/null || true +ls -lh *.crt 2>/dev/null || true +echo "" +echo "These are the official Cadence test certificates." +echo "Reference: https://github.com/cadence-workflow/cadence/tree/master/config/credentials" + diff --git a/new_samples/client_samples/helloworld_tls/hello_world_tls.go b/new_samples/client_samples/helloworld_tls/hello_world_tls.go new file mode 100644 index 00000000..fc16e56b --- /dev/null +++ b/new_samples/client_samples/helloworld_tls/hello_world_tls.go @@ -0,0 +1,85 @@ +package main + +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "os" + "time" + + "github.com/google/uuid" + "github.com/uber-common/cadence-samples/new_samples/worker" + "go.uber.org/cadence/.gen/go/shared" + "go.uber.org/yarpc/transport/grpc" + "go.uber.org/zap" + "google.golang.org/grpc/credentials" +) + +func main() { + withTLSDialOption, err := withTLSDialOption() + if err != nil { + panic(err) + } + cadenceClient := worker.BuildCadenceClient(withTLSDialOption) + logger := worker.BuildLogger() + + domain := "default" + tasklist := "default-tasklist" + workflowID := uuid.New().String() + requestID := uuid.New().String() + executionTimeout := int32(60) + closeTimeout := int32(60) + + workflowType := "cadence_samples.TLSWorkflow" + input := []byte(`{"message": "Uber"}`) + + req := shared.StartWorkflowExecutionRequest{ + Domain: &domain, + WorkflowId: &workflowID, + WorkflowType: &shared.WorkflowType{ + Name: &workflowType, + }, + TaskList: &shared.TaskList{ + Name: &tasklist, + }, + Input: input, + ExecutionStartToCloseTimeoutSeconds: &executionTimeout, + TaskStartToCloseTimeoutSeconds: &closeTimeout, + RequestId: &requestID, + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + resp, err := cadenceClient.StartWorkflowExecution(ctx, &req) + if err != nil { + logger.Error("Failed to create workflow", zap.Error(err)) + panic("Failed to create workflow.") + } + + logger.Info("successfully started HelloWorld workflow", zap.String("runID", resp.GetRunId())) +} + +func withTLSDialOption() (grpc.DialOption, error) { + // Present client cert for mutual TLS (if enabled on server) + clientCert, err := tls.LoadX509KeyPair("credentials/client.crt", "credentials/client.key") + if err != nil { + return nil, fmt.Errorf("Failed to load client certificate: %v", zap.Error(err)) + } + + // Load server CA + caCert, err := os.ReadFile("credentials/keytest.crt") + if err != nil { + return nil, fmt.Errorf("Failed to load server CA certificate: %v", zap.Error(err)) + } + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + tlsConfig := tls.Config{ + InsecureSkipVerify: true, + RootCAs: caCertPool, + Certificates: []tls.Certificate{clientCert}, + } + creds := credentials.NewTLS(&tlsConfig) + grpc.DialerCredentials(creds) + return grpc.DialerCredentials(creds), nil +} diff --git a/new_samples/client_samples/helloworld_tls/register_domain.go b/new_samples/client_samples/helloworld_tls/register_domain.go new file mode 100644 index 00000000..8ab832b2 --- /dev/null +++ b/new_samples/client_samples/helloworld_tls/register_domain.go @@ -0,0 +1,88 @@ +package main + +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "os" + "time" + + "github.com/uber-common/cadence-samples/new_samples/worker" + "go.uber.org/cadence/.gen/go/shared" + "go.uber.org/yarpc/transport/grpc" + "go.uber.org/zap" + "google.golang.org/grpc/credentials" +) + +func main() { + logger := worker.BuildLogger() + logger.Info("Registering default domain for cadence-vishwa with TLS...") + + withTLSDialOption, err := buildTLSDialOption() + if err != nil { + logger.Fatal("Failed to build TLS dial option", zap.Error(err)) + } + + cadenceClient := worker.BuildCadenceClient(withTLSDialOption) + + // Register the domain + domain := "default" + retentionDays := int32(7) + emitMetric := true + + req := &shared.RegisterDomainRequest{ + Name: &domain, + Description: stringPtr("Default domain for cadence samples"), + WorkflowExecutionRetentionPeriodInDays: &retentionDays, + EmitMetric: &emitMetric, + } + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + err = cadenceClient.RegisterDomain(ctx, req) + if err != nil { + // Check if domain already exists + if _, ok := err.(*shared.DomainAlreadyExistsError); ok { + logger.Info("Domain already exists", zap.String("domain", domain)) + return + } + logger.Fatal("Failed to register domain", zap.Error(err)) + } + + logger.Info("Successfully registered domain", zap.String("domain", domain)) +} + +func buildTLSDialOption() (grpc.DialOption, error) { + // Load client certificate + clientCert, err := tls.LoadX509KeyPair("credentials/client.crt", "credentials/client.key") + if err != nil { + return nil, fmt.Errorf("failed to load client certificate: %w", err) + } + + // Load server CA + caCert, err := os.ReadFile("credentials/keytest.crt") + if err != nil { + return nil, fmt.Errorf("failed to load server CA certificate: %w", err) + } + + caCertPool := x509.NewCertPool() + if !caCertPool.AppendCertsFromPEM(caCert) { + return nil, fmt.Errorf("failed to append CA certificate") + } + + tlsConfig := &tls.Config{ + InsecureSkipVerify: true, + RootCAs: caCertPool, + Certificates: []tls.Certificate{clientCert}, + MinVersion: tls.VersionTLS12, + } + + creds := credentials.NewTLS(tlsConfig) + return grpc.DialerCredentials(creds), nil +} + +func stringPtr(s string) *string { + return &s +}