Skip to content

Commit

Permalink
Merge pull request #20 from fate0/custom-tls
Browse files Browse the repository at this point in the history
add support for custom tls config
  • Loading branch information
rogerwelin authored May 31, 2020
2 parents 3661474 + 2d75e69 commit cff7785
Show file tree
Hide file tree
Showing 17 changed files with 360 additions and 0 deletions.
20 changes: 20 additions & 0 deletions README-ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,26 @@ Starting Load Test with 1000 requests using 10 concurrent users

```

示例:指定自定义 ca 证书
```bash
$ ./cassowary run -u http://localhost:8000 -c 10 -n 1000 --ca /path/to/ca.pem

Starting Load Test with 1000 requests using 10 concurrent users

[ omitted for brevity ]

```

示例:指定客户端证书信息
```bash
$ ./cassowary run -u http://localhost:8000 -c 10 -n 1000 --cert /path/to/client.pem --key /path/to/client-key.pem

Starting Load Test with 1000 requests using 10 concurrent users

[ omitted for brevity ]

```


**以模块或者library导入Cassowary**

Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,30 @@ Starting Load Test with 1000 requests using 10 concurrent users

```

### Specifying ca certificate
Example specifying ca certificate

```bash
$ ./cassowary run -u http://localhost:8000 -c 10 -n 1000 --ca /path/to/ca.pem

Starting Load Test with 1000 requests using 10 concurrent users

[ omitted for brevity ]

```

### Specifying client authentication certificate
Example specifying client authentication certificate

```bash
$ ./cassowary run -u http://localhost:8000 -c 10 -n 1000 --cert /path/to/client.pem --key /path/to/client-key.pem

Starting Load Test with 1000 requests using 10 concurrent users

[ omitted for brevity ]

```

Importing cassowary as a module/library
--------

Expand Down
49 changes: 49 additions & 0 deletions cmd/cassowary/cli.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package main

import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"strconv"

Expand Down Expand Up @@ -151,6 +154,27 @@ func validateCLI(c *cli.Context) error {
httpMethod = "GET"
}

tlsConfig := new(tls.Config)
if c.String("ca") != "" {
pemCerts, err := ioutil.ReadFile(c.String("ca"))
if err != nil {
return err
}
ca := x509.NewCertPool()
if !ca.AppendCertsFromPEM(pemCerts) {
return fmt.Errorf("failed to read CA from PEM")
}
tlsConfig.RootCAs = ca
}

if c.String("cert") != "" && c.String("key") != "" {
cert, err := tls.LoadX509KeyPair(c.String("cert"), c.String("key"))
if err != nil {
return err
}
tlsConfig.Certificates = []tls.Certificate{cert}
}

cass := &client.Cassowary{
FileMode: false,
BaseURL: c.String("url"),
Expand All @@ -159,6 +183,7 @@ func validateCLI(c *cli.Context) error {
RequestHeader: header,
Duration: duration,
PromExport: prometheusEnabled,
TLSConfig: tlsConfig,
PromURL: c.String("prompushgwurl"),
Cloudwatch: c.Bool("cloudwatch"),
ExportMetrics: c.Bool("json-metrics"),
Expand Down Expand Up @@ -282,6 +307,18 @@ func runCLI(args []string) {
Name: "disable-keep-alive",
Usage: "use this flag to disable http keep-alive",
},
cli.StringFlag{
Name: "ca",
Usage: "ca certificate to verify peer against",
},
cli.StringFlag{
Name: "cert",
Usage: "client authentication certificate",
},
cli.StringFlag{
Name: "key",
Usage: "client authentication key",
},
},
Action: validateCLIFile,
},
Expand Down Expand Up @@ -345,6 +382,18 @@ func runCLI(args []string) {
Name: "disable-keep-alive",
Usage: "use this flag to disable http keep-alive",
},
cli.StringFlag{
Name: "ca",
Usage: "ca certificate to verify peer against",
},
cli.StringFlag{
Name: "cert",
Usage: "client authentication certificate",
},
cli.StringFlag{
Name: "key",
Usage: "client authentication key",
},
},
Action: validateCLI,
},
Expand Down
62 changes: 62 additions & 0 deletions docs/LIBRARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,65 @@ func main() {
fmt.Println(string(jsonMetrics))
}
```

**Example 3: Custom TLS config**

```go
package main

import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io/ioutil"

cassowary "github.com/rogerwelin/cassowary/pkg/client"
)

func main() {
pemCerts, err := ioutil.ReadFile("testdata/ca.pem")
if err != nil {
panic("Invalid ca.pem path")
}

ca := x509.NewCertPool()
if !ca.AppendCertsFromPEM(pemCerts) {
panic("Failed to read CA from PEM")
}

cert, err := tls.LoadX509KeyPair("testdata/client.pem", "testdata/client-key.pem")
if err != nil {
panic("Invalid client.pem/client-key.pem path")
}

clientTLSConfig := &tls.Config{
RootCAs: ca,
Certificates: []tls.Certificate{cert},
}

cass := &cassowary.Cassowary{
BaseURL: "http://www.example.com",
ConcurrencyLevel: 1,
Requests: 10,
TLSConfig: clientTLSConfig,
DisableTerminalOutput: true,
}
metrics, err := cass.Coordinate()
if err != nil {
panic(err)
}

// print results
fmt.Printf("%+v\n", metrics)

// or print as json
jsonMetrics, err := json.Marshal(metrics)
if err != nil {
panic(err)
}

fmt.Println(string(jsonMetrics))
}

```
1 change: 1 addition & 0 deletions pkg/client/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ func (c *Cassowary) Coordinate() (ResultMetrics, error) {
c.Client = &http.Client{
Timeout: time.Second * time.Duration(c.Timeout),
Transport: &http.Transport{
TLSClientConfig: c.TLSConfig,
MaxIdleConnsPerHost: 10000,
DisableCompression: false,
DisableKeepAlives: c.DisableKeepAlive,
Expand Down
66 changes: 66 additions & 0 deletions pkg/client/load_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package client

import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
Expand Down Expand Up @@ -73,3 +76,66 @@ func TestLoadCoordinateURLPaths(t *testing.T) {
t.Errorf("Wanted %d but got %d", 30, len(cass.URLPaths))
}
}

func TestCoordinateTLSConfig(t *testing.T) {
srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Write([]byte("ok"))
}))

pemCerts, err := ioutil.ReadFile("testdata/ca.pem")
if err != nil {
t.Fatal("Invalid ca.pem path")
}

ca := x509.NewCertPool()
if !ca.AppendCertsFromPEM(pemCerts) {
t.Fatal("Failed to read CA from PEM")
}

cert, err := tls.LoadX509KeyPair("testdata/server.pem", "testdata/server-key.pem")
if err != nil {
t.Fatal("Invalid server.pem/server-key.pem path")
}

srv.TLS = &tls.Config{
ClientCAs: ca,
ClientAuth: tls.RequireAndVerifyClientCert,
Certificates: []tls.Certificate{cert},
}
srv.StartTLS()

cert, err = tls.LoadX509KeyPair("testdata/client.pem", "testdata/client-key.pem")
if err != nil {
t.Fatal("Invalid client.pem/client-key.pem path")
}
clientTLSConfig := &tls.Config{
RootCAs: ca,
Certificates: []tls.Certificate{cert},
}

cass := Cassowary{
BaseURL: srv.URL,
ConcurrencyLevel: 1,
Requests: 10,
TLSConfig: clientTLSConfig,
DisableTerminalOutput: true,
}

metrics, err := cass.Coordinate()
if err != nil {
t.Error(err)
}

if metrics.BaseURL != srv.URL {
t.Errorf("Wanted %s but got %s", srv.URL, metrics.BaseURL)
}

if metrics.TotalRequests != 10 {
t.Errorf("Wanted %d but got %d", 1, metrics.TotalRequests)
}

if metrics.FailedRequests != 0 {
t.Errorf("Wanted %d but got %d", 0, metrics.FailedRequests)
}
}
6 changes: 6 additions & 0 deletions pkg/client/testdata/ca-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDCKZiW/gDKygWST6H2EWbSnln/DeaTA9tZzExaPjhGLeluQAY+2k24c
qo4fM4LouV+gBwYFK4EEACKhZANiAAQkLBpmhd9cRaPGV5PXca0MZTb9u98KJusf
NRElfAyKDLrQ8jPEaN7i0uVHeK7gwAGEWsSUhmUC+0YF78jZosaecZiNZ71AgJZ7
2DKij0x9PLrGfQmSOPtjzxqWbKmgzt8=
-----END EC PRIVATE KEY-----
15 changes: 15 additions & 0 deletions pkg/client/testdata/ca.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICaDCCAe6gAwIBAgIUeox07oT2mMosFiT0SIpi5G+wcngwCgYIKoZIzj0EAwMw
ajELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh
biBGcmFuY2lzY28xEjAQBgNVBAoTCWNhc3Nvd2FyeTEaMBgGA1UECxMRQ2Fzc293
YXJ5IFRlc3RpbmcwIBcNMjAwNTMxMDUxNDAwWhgPMjEyMDA1MDcwNTE0MDBaMGox
CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4g
RnJhbmNpc2NvMRIwEAYDVQQKEwljYXNzb3dhcnkxGjAYBgNVBAsTEUNhc3Nvd2Fy
eSBUZXN0aW5nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEJCwaZoXfXEWjxleT13Gt
DGU2/bvfCibrHzURJXwMigy60PIzxGje4tLlR3iu4MABhFrElIZlAvtGBe/I2aLG
nnGYjWe9QICWe9gyoo9MfTy6xn0Jkjj7Y88almypoM7fo1MwUTAOBgNVHQ8BAf8E
BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUIccCwpEWbeI0v2vEhPf4
ONBdr68wDwYDVR0RBAgwBocEfwAAATAKBggqhkjOPQQDAwNoADBlAjEA5xrv47yu
jUHorUWBJVMhDnUyav55W052ho0fNGB4cRgoFAXUX4teVgvLzrnVGRZUAjBaHyhK
ojG8Pg/iJnJNzmCgyJW1dKZTyskifuW28JRyM6LgnOYT5tGT9yH6g10UjvA=
-----END CERTIFICATE-----
19 changes: 19 additions & 0 deletions pkg/client/testdata/ca_csr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"hosts" : ["127.0.0.1"],
"key": {
"algo": "ecdsa",
"size": 384
},
"ca": {
"expiry": "876000h"
},
"names": [
{
"C": "US",
"L": "San Francisco",
"O": "cassowary",
"OU": "Cassowary Testing",
"ST": "California"
}
]
}
6 changes: 6 additions & 0 deletions pkg/client/testdata/client-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDAc90Blw/NfCnWMXTC4Dbo5+LaA4evCIIfZDbPaI7UVz6UwTBsEFGK/
/rWIEskJOO+gBwYFK4EEACKhZANiAARTfENUulB4IBZPQFORPmyErND774l+jH4V
JiKPLENCCUKHUDNih+skoincGS2fLjTplrC/ZLACeuvsevr0czcaH0MTkL3aA5FT
9RNbgBMcKKkqC/DtJZ+imr6RwGu3iow=
-----END EC PRIVATE KEY-----
16 changes: 16 additions & 0 deletions pkg/client/testdata/client.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE-----
MIICejCCAgCgAwIBAgIUWpA0KuqhONq0Q7xJwp/vXdMdnC4wCgYIKoZIzj0EAwMw
ajELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh
biBGcmFuY2lzY28xEjAQBgNVBAoTCWNhc3Nvd2FyeTEaMBgGA1UECxMRQ2Fzc293
YXJ5IFRlc3RpbmcwIBcNMjAwNTMxMDUxNDAwWhgPMjEyMDA1MDcwNTE0MDBaMGox
CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4g
RnJhbmNpc2NvMRIwEAYDVQQKEwljYXNzb3dhcnkxGjAYBgNVBAsTEUNhc3Nvd2Fy
eSBUZXN0aW5nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEU3xDVLpQeCAWT0BTkT5s
hKzQ+++Jfox+FSYijyxDQglCh1AzYofrJKIp3Bktny406Zawv2SwAnrr7Hr69HM3
Gh9DE5C92gORU/UTW4ATHCipKgvw7SWfopq+kcBrt4qMo2UwYzAOBgNVHQ8BAf8E
BAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4E
FgQUu8dTMk2IWBGGnTG6KWvVnPzFEHUwDwYDVR0RBAgwBocEfwAAATAKBggqhkjO
PQQDAwNoADBlAjBfb1Ts88t8kd8nU4/eSVeKRR91VQI/uOIxNVHDuOsyBinx0jgh
eZ+MfZZ/exv319ICMQC0plJI7JSB+lDHbmIEYOiXZUXbONZkbO9ALRRMTqxzh00S
vAju6beNGIFpwQ/q7dg=
-----END CERTIFICATE-----
16 changes: 16 additions & 0 deletions pkg/client/testdata/csr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"hosts" : ["127.0.0.1"],
"key": {
"algo": "ecdsa",
"size": 384
},
"names": [
{
"C": "US",
"L": "San Francisco",
"O": "cassowary",
"OU": "Cassowary Testing",
"ST": "California"
}
]
}
4 changes: 4 additions & 0 deletions pkg/client/testdata/gencert.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
cfssl gencert -initca ca_csr.json |cfssljson -bare ca
cfssl gencert -ca ca.pem -ca-key ca-key.pem -config signing.json -profile client csr.json |cfssljson -bare client
cfssl gencert -ca ca.pem -ca-key ca-key.pem -config signing.json -profile server csr.json |cfssljson -bare server
rm *.csr
6 changes: 6 additions & 0 deletions pkg/client/testdata/server-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDAN6AKzYbaqN9dm5YsDP4qFF0m+jM2lbodOsvmwpYryEhtoq9NuWBKr
s7ycpCBd/N+gBwYFK4EEACKhZANiAAQ1+v02VGuU/Ld5sRQG/vVf9hcAUUA+dqoe
zooZ3Af4yJagE8jCtfG/yXZTmJpYmAKGI8USRdVGG5EL1bS6DULhItaLOVFTlLvC
QJjaZWTI1YGVZgboH0dS+i6XB4O0H70=
-----END EC PRIVATE KEY-----
Loading

0 comments on commit cff7785

Please sign in to comment.