Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TLS certs support for mutual authentication #83

Merged
merged 8 commits into from
Oct 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ ddosify [FLAG]
| `-l` | [Type](#load-types) of the load test. Ddosify supports 3 load types. | `string` | `linear` | No |
| <span style="white-space: nowrap;">`--config`</span> | [Config File](#config-file) of the load test. | `string` | - | No |
| <span style="white-space: nowrap;">`--version`</span> | Prints version, git commit, built date (utc), go information and quit | - | - | No |

| <span style="white-space: nowrap;">`--cert_path`</span> | A path to a certificate file (usually called 'cert.pem') | - | - | No |
| <span style="white-space: nowrap;">`--cert_key_path`</span> | A path to a certificate key file (usually called 'key.pem') | - | - | No |

### Load Types

Expand Down
19 changes: 17 additions & 2 deletions config/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ type step struct {
Timeout int `json:"timeout"`
Sleep string `json:"sleep"`
Others map[string]interface{} `json:"others"`
CertPath string `json:"cert_path"`
CertKeyPath string `json:"cert_key_path"`
}

func (s *step) UnmarshalJSON(data []byte) error {
Expand Down Expand Up @@ -220,7 +222,8 @@ func stepToScenarioItem(s step) (types.ScenarioItem, error) {
}

s.Protocol = strings.ToUpper(s.Protocol)
return types.ScenarioItem{

item := types.ScenarioItem{
ID: s.Id,
Name: s.Name,
URL: s.Url,
Expand All @@ -232,7 +235,19 @@ func stepToScenarioItem(s step) (types.ScenarioItem, error) {
Timeout: s.Timeout,
Sleep: strings.ReplaceAll(s.Sleep, " ", ""),
Custom: s.Others,
}, nil
}

if s.CertPath != "" && s.CertKeyPath != "" {
cert, pool, err := types.ParseTLS(s.CertPath, s.CertKeyPath)
if err != nil {
return item, err
}

item.Cert = cert
item.CertPool = pool
}

return item, nil
}

func prepareMultipartPayload(parts []multipartFormData) (body string, contentType string, err error) {
Expand Down
261 changes: 261 additions & 0 deletions config/json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@
package config

import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"reflect"
"regexp"
"strings"
Expand Down Expand Up @@ -303,3 +306,261 @@ func TestCreateHammerInvalidTarget(t *testing.T) {
t.Errorf("TestCreateHammerProtocol error occurred")
}
}

func TestCreateHammerTLS(t *testing.T) {
t.Parallel()

// prepare TLS files
cert, certKey := generateCerts()
certFile, keyFile, err := createCertPairFiles(cert, certKey)
if err != nil {
t.Fatalf("Failed to prepare certs %v", err)
}
defer os.Remove(certFile.Name())
defer os.Remove(keyFile.Name())

config := buildJSONTLSConfig(certFile.Name(), keyFile.Name())

jsonReader, _ := NewConfigReader(config, ConfigTypeJson)

h, err := jsonReader.CreateHammer()

if err != nil {
t.Errorf("TestCreateHammerDefaultValues error occurred: %v", err)
}

certVal, _, err := types.ParseTLS(certFile.Name(), keyFile.Name())
if err != nil {
t.Fatalf("Failed to gen certs %v", err)
}

// We compare only Certificte because CertPool has pointers inside and it's hard to compare it
if !reflect.DeepEqual(certVal, h.Scenario.Scenario[0].Cert) {
t.Errorf("\nExpected: %#v, \nFound: %#v", certVal, h.Scenario.Scenario[0].Cert)
}
}

func TestCreateHammerTLSWithOnlyCertPath(t *testing.T) {
t.Parallel()

// prepare TLS files
cert, certKey := generateCerts()
certFile, keyFile, err := createCertPairFiles(cert, certKey)
if err != nil {
t.Fatalf("Failed to prepare certs %v", err)
}
defer os.Remove(certFile.Name())
defer os.Remove(keyFile.Name())

config := buildJSONTLSConfig(certFile.Name(), "")

jsonReader, _ := NewConfigReader(config, ConfigTypeJson)
expectedHammer := types.Hammer{
TotalReqCount: types.DefaultReqCount,
LoadType: types.DefaultLoadType,
TestDuration: types.DefaultDuration,
ReportDestination: types.DefaultOutputType,
Scenario: types.Scenario{
Scenario: []types.ScenarioItem{{
ID: 1,
URL: strings.ToLower(types.DefaultProtocol) + "://test.com",
Protocol: types.DefaultProtocol,
Method: types.DefaultMethod,
Timeout: types.DefaultTimeout,
}},
},
Proxy: proxy.Proxy{
Strategy: proxy.ProxyTypeSingle,
},
}

h, err := jsonReader.CreateHammer()

if err != nil {
t.Errorf("TestCreateHammerDefaultValues error occurred: %v", err)
}

if !reflect.DeepEqual(expectedHammer, h) {
t.Errorf("\nExpected: %#v, \nFound: %#v", expectedHammer, h)
}
}

func TestCreateHammerTLSWithOnlyKeyPath(t *testing.T) {
t.Parallel()

// prepare TLS files
cert, certKey := generateCerts()
certFile, keyFile, err := createCertPairFiles(cert, certKey)
if err != nil {
t.Fatalf("Failed to prepare certs %v", err)
}
defer os.Remove(certFile.Name())
defer os.Remove(keyFile.Name())

config := buildJSONTLSConfig("", keyFile.Name())

jsonReader, _ := NewConfigReader(config, ConfigTypeJson)
expectedHammer := types.Hammer{
TotalReqCount: types.DefaultReqCount,
LoadType: types.DefaultLoadType,
TestDuration: types.DefaultDuration,
ReportDestination: types.DefaultOutputType,
Scenario: types.Scenario{
Scenario: []types.ScenarioItem{{
ID: 1,
URL: strings.ToLower(types.DefaultProtocol) + "://test.com",
Protocol: types.DefaultProtocol,
Method: types.DefaultMethod,
Timeout: types.DefaultTimeout,
}},
},
Proxy: proxy.Proxy{
Strategy: proxy.ProxyTypeSingle,
},
}

h, err := jsonReader.CreateHammer()

if err != nil {
t.Errorf("TestCreateHammerDefaultValues error occurred: %v", err)
}

if !reflect.DeepEqual(expectedHammer, h) {
t.Errorf("\nExpected: %#v, \nFound: %#v", expectedHammer, h)
}
}

func TestCreateHammerTLSWithWithEmptyPath(t *testing.T) {
t.Parallel()

config := buildJSONTLSConfig("", "")

jsonReader, _ := NewConfigReader(config, ConfigTypeJson)
expectedHammer := types.Hammer{
TotalReqCount: types.DefaultReqCount,
LoadType: types.DefaultLoadType,
TestDuration: types.DefaultDuration,
ReportDestination: types.DefaultOutputType,
Scenario: types.Scenario{
Scenario: []types.ScenarioItem{{
ID: 1,
URL: strings.ToLower(types.DefaultProtocol) + "://test.com",
Protocol: types.DefaultProtocol,
Method: types.DefaultMethod,
Timeout: types.DefaultTimeout,
}},
},
Proxy: proxy.Proxy{
Strategy: proxy.ProxyTypeSingle,
},
}

h, err := jsonReader.CreateHammer()

if err != nil {
t.Errorf("TestCreateHammerDefaultValues error occurred: %v", err)
}

if !reflect.DeepEqual(expectedHammer, h) {
t.Errorf("\nExpected: %#v, \nFound: %#v", expectedHammer, h)
}
}

func buildJSONTLSConfig(certPath, keyPath string) []byte {
format := `
{
"steps": [
{
"id": 1,
"url": "test.com",
"cert_path": %q,
"cert_key_path": %q
}
]
}`

config := fmt.Sprintf(format, certPath, keyPath)

fmt.Println(config)

return []byte(config)
}

func createCertPairFiles(cert string, certKey string) (*os.File, *os.File, error) {
certFile, err := os.CreateTemp("", ".pem")
if err != nil {
return nil, nil, err
}

_, err = io.WriteString(certFile, cert)
if err != nil {
return nil, nil, err
}

keyFile, err := os.CreateTemp("", ".pem")
if err != nil {
return nil, nil, err
}

_, err = io.WriteString(keyFile, certKey)
if err != nil {
return nil, nil, err
}

return certFile, keyFile, nil
}

func generateCerts() (string, string) {
cert := `-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUS4UhTks8aRCQ1k9IGn437ZyP3MgwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMjEwMDUyMjM5MDVaFw0zMjEw
MDIyMjM5MDVaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDMbZctKXBx8v63TXIhM/OB7S6VfPqpzfHufhs6kAHu
jfC2ooCUqzqdg0T8bM1bjahYuAbQA1cWKYBsqfd01Po1ltWmbMf7ZvmSB6VN7kC2
Y670zee91dGDQ2yzmorJuIZAtOBVZesYLg8UHSGzSC/smJOrjYidtlbvzOcX0pv3
RCIUrNMed60EpSch/rzAJLzJmwNSQZ4vJHNlNetSkvTi7cxMWfwpcM/rN1hEmP1X
J43hJp/TNRZVnEsvs/yggP/FwUjG74mU3KfnWiv91AkkarNTNquEMJ+f4OFqMcnF
p0wqg47JTqcAAT0n1B0VB+z0hGXEFMN+IJXsHETZNG+JAgMBAAGjUzBRMB0GA1Ud
DgQWBBSIw+qUKQJjXWti5x/Cnn2GueuX5zAfBgNVHSMEGDAWgBSIw+qUKQJjXWti
5x/Cnn2GueuX5zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAA
DXzf8VXi4s2GScNfHf0BzMjpyrtRZ0Wbp2Vfh7OwVR6xcx+pqXNjlydM/vu2LvOK
hh7Jbo+JS+o7O24UJ9lLFkCRsZVF+NFqJf+2rdHCaOiZSdZmtjBU0dFuAGS7+lU3
M8P7WCNOm6NAKbs7VZHVcZPzp81SCPQgQIS19xRf4Irbvsijv4YdyL4Qv7aWcclb
MdZX9AH9Fx8tJq4VKvUYsCXAD0kuywMLjh+yj5O/2hMvs5rvaQvm2daQNRDNp884
uTLrNF7W7QaKEL06ZpXJoBqdKsiwn577XTDKvzN0XxQrT+xV9VHO7OXblF+Od3/Y
SzBR+QiQKy3x+LkOxhkk
-----END CERTIFICATE-----`

certKey := `-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDMbZctKXBx8v63
TXIhM/OB7S6VfPqpzfHufhs6kAHujfC2ooCUqzqdg0T8bM1bjahYuAbQA1cWKYBs
qfd01Po1ltWmbMf7ZvmSB6VN7kC2Y670zee91dGDQ2yzmorJuIZAtOBVZesYLg8U
HSGzSC/smJOrjYidtlbvzOcX0pv3RCIUrNMed60EpSch/rzAJLzJmwNSQZ4vJHNl
NetSkvTi7cxMWfwpcM/rN1hEmP1XJ43hJp/TNRZVnEsvs/yggP/FwUjG74mU3Kfn
Wiv91AkkarNTNquEMJ+f4OFqMcnFp0wqg47JTqcAAT0n1B0VB+z0hGXEFMN+IJXs
HETZNG+JAgMBAAECggEAM+U6NHfJmNPD/8qER5OFpJ0Ob1qL06F5Yj7XMLWwF9wm
mGaGV7dkKOpTD/Wa6Dv82ZDWAeZnLDQa6vr228zZO9Nvp1EEL3kDsCOKvk7WVLbX
ikPfKZznE/iA1tNLmkvioPiJ3oQB+2Bt6YA/tuCDcf+FtU43uTm5tiSBIdYQS+Om
xN9OEXihk1svxHXQKa/a3nKPVLvdp3P90hDJ0PcRslXSy1V8az+A94JFEnCvnKsK
nF2rItCcXkInL0lYHZKgLHQMXGWkNl8e3PA1GZk3yF6LPNtPI1T5Ek9GwkHNw4JZ
BL/xEWLKB1qR2Z4I3UbWGVyi418kANv1eISb+49egQKBgQDraSRWB8nM5O3Zl9kT
8S5K924o1oXrO17eqQlVtQVmtUdoVvIBc6uHQZOmV1eHYpr6c95h8apNLexI22AY
SWkq9smpCnxLUsdkplwzie0F4bAzD6MCR8WIJxapUSPlyCA+8st1hquYBchKGQhd
6mMY1gzMDacYV/WhtG4E5d0nMQKBgQDeTr793n00VtpKuquFJe6Stu7Ujf64dL0s
3opLovyI0TmtMz5oCqIezwrjqc0Vy0UksWXaz0AboinDP+5n60cTEIt/6H0kryDc
dxfSHEA9BBDoQtxOFi3QGcxXbwu0i9QSoexrKY7FhA2xPji6bCcPycthhIrCpUiZ
s5gVkjHn2QKBgQCGklxLMbiSgGvXb46Qb9be1AMNJVT427+n2UmUzR6BUC+53boK
Sm1LrJkTBerrYdrmQUZnBxcrd40TORT9zTlpbhppn6zeAjwptVAPxlDQg+uNxOqS
ayToaC/0KoYy3OxSD8lvLcT56pRMh3LY/RwZHoPCQiu7Js0r21DpS93YgQKBgAuc
c09RMprsOmSS0WiX7ZkOIvVJIVfDCSpxySlgLu56dxe7yHOosoUHbVsswEB2KHtd
JKPEFWYcFzBSg4I8AK9XOuIIY5jp6L57Hexke1p0fumSrG0LrYLkBg8/Bo58iywZ
9v414nYgipKKXG4oPfYOJShHwvOdrGgSwEvIIgEpAoGAZz0yC9+x+JaoTnyUIRyI
+Aj5a4KhYjFtsZhcn/yCZHDqzJNDz6gAu579ey+J2CVOhjtgB5lowsDrHu32Hqnn
SEfyTru/ynQ8obwaRzdDYml+On86YWOw+brpMXkN+KB6bs2okE2N68v0qGPakxjt
OLDW6kKz5pI4T8lQJhdqjCU=
-----END PRIVATE KEY-----`

return cert, certKey
}
Loading