Skip to content

Commit

Permalink
Extra validation, tests for --cert/--key flags
Browse files Browse the repository at this point in the history
  • Loading branch information
csstaub committed Feb 20, 2019
1 parent 20bc341 commit 4df57d6
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 22 deletions.
4 changes: 2 additions & 2 deletions docs/HSM-PKCS11.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ To import the server test key into SoftHSM, for example:
To launch ghostunnel with the SoftHSM-backed PKCS11 key (on macOS):

ghostunnel server \
--keystore test-keys/server-cert.pem \
--cert test-keys/server-cert.pem \
--pkcs11-module /usr/local/Cellar/softhsm/2.4.0/lib/softhsm/libsofthsm2.so \
--pkcs11-token-label ghostunnel-server \
--pkcs11-pin 1234 \
Expand All @@ -43,7 +43,7 @@ to use environment variables to set PKCS11 options instead of flags (via
`PKCS11_MODULE`, `PKCS11_TOKEN_LABEL` and `PKCS11_PIN`), useful if you don't
want to show the PIN on the command line.

Note that `--keystore` needs to point to the certificate chain that corresponds
Note that `--cert` needs to point to the certificate chain that corresponds
to the private key in the PKCS#11 module, with the leaf certificate being the
first certificate in the chain. Ghostunnel doesn't have the ability to read
the certificate chain directly from the module at this point in time.
Expand Down
36 changes: 18 additions & 18 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,18 @@ import (
"strings"
"time"

"github.com/cyberdelia/go-metrics-graphite"
"github.com/hashicorp/go-syslog"
"github.com/kavu/go_reuseport"
"github.com/mwitkow/go-http-dialer"
graphite "github.com/cyberdelia/go-metrics-graphite"
gsyslog "github.com/hashicorp/go-syslog"
reuseport "github.com/kavu/go_reuseport"
http_dialer "github.com/mwitkow/go-http-dialer"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rcrowley/go-metrics"
metrics "github.com/rcrowley/go-metrics"
"github.com/square/ghostunnel/auth"
"github.com/square/ghostunnel/certloader"
"github.com/square/ghostunnel/proxy"
"github.com/square/ghostunnel/wildcard"
"github.com/square/go-sq-metrics"
"gopkg.in/alecthomas/kingpin.v2"
sqmetrics "github.com/square/go-sq-metrics"
kingpin "gopkg.in/alecthomas/kingpin.v2"

prometheusmetrics "github.com/deathowl/go-metrics-prometheus"
"github.com/prometheus/client_golang/prometheus"
Expand Down Expand Up @@ -90,10 +90,10 @@ var (
clientDisableAuth = clientCommand.Flag("disable-authentication", "Disable client authentication, no certificate will be provided to the server.").Default("false").Bool()

// TLS options
keystorePath = app.Flag("keystore", "Path to certificate and keystore (PEM with certificate/key, or PKCS12).").PlaceHolder("PATH").String()
cert = app.Flag("cert", "Path to certificate (PEM without key).").PlaceHolder("PATH").String()
key = app.Flag("key", "Path to certificate private key (PEM key).").PlaceHolder("PATH").String()
keystorePass = app.Flag("storepass", "Password for certificate and keystore (optional).").PlaceHolder("PASS").String()
keystorePath = app.Flag("keystore", "Path to keystore (combined PEM with cert/key, or PKCS12 keystore).").PlaceHolder("PATH").String()
certPath = app.Flag("cert", "Path to certificate (PEM with certificate chain).").PlaceHolder("PATH").String()
keyPath = app.Flag("key", "Path to certificate private key (PEM with private key).").PlaceHolder("PATH").String()
keystorePass = app.Flag("storepass", "Password for keystore (if using PKCS keystore, optional).").PlaceHolder("PASS").String()
caBundlePath = app.Flag("cacert", "Path to CA bundle file (PEM/X509). Uses system trust store by default.").String()
enabledCipherSuites = app.Flag("cipher-suites", "Set of cipher suites to enable, comma-separated, in order of preference (AES, CHACHA).").Default("AES,CHACHA").String()

Expand Down Expand Up @@ -217,14 +217,14 @@ func serverValidateFlags() error {
len(*serverAllowedIPs) > 0 ||
len(*serverAllowedURIs) > 0

if (*key != "" && *cert == "") || (*key == "" && *cert != "") {
return errors.New("both key and cert are required")
if ((*keyPath != "" && *certPath == "") || (*keyPath == "" && *certPath != "")) && !hasPKCS11() {
return errors.New("when using --cert, must also specify --key")
}
if *key != "" && *cert != "" && *keystorePath != "" {
return errors.New("Cannot specificy both key/cert and keystorePath")
if *keyPath != "" && *certPath != "" && *keystorePath != "" {
return errors.New("--key/--cert and --keystore are mutually exclusive")
}
if *keystorePath == "" && !hasKeychainIdentity() {
return errors.New("at least one of --keystore/cert/key or --keychain-identity (if supported) flags is required")
if *keystorePath == "" && !hasKeychainIdentity() && *certPath == "" {
return errors.New("at least one of --keystore, --cert/--key or --keychain-identity (if supported) flags is required")
}
if *keystorePath != "" && hasKeychainIdentity() {
return errors.New("--keystore and --keychain-identity flags are mutually exclusive")
Expand Down Expand Up @@ -333,7 +333,7 @@ func run(args []string) error {
}
metrics := sqmetrics.NewMetrics(*metricsURL, *metricsPrefix, client, *metricsInterval, metrics.DefaultRegistry, logger)

cert, err := buildCertificate(*keystorePath, *cert, *key, *keystorePass)
cert, err := buildCertificate(*keystorePath, *certPath, *keyPath, *keystorePass)
if err != nil {
fmt.Fprintf(os.Stderr, "error: unable to load certificates: %s\n", err)
return err
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion tests/test-server-pkcs11-module.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
'--listen={0}:13001'.format(LOCALHOST),
'--target={0}:{1}'.format(LOCALHOST,
STATUS_PORT),
'--keystore=../test-keys/server-cert.pem',
'--cert=../test-keys/server-cert.pem',
'--pkcs11-module={0}'.format(os.environ['GHOSTUNNEL_TEST_PKCS11_MODULE']),
'--pkcs11-token-label={0}'.format(os.environ['GHOSTUNNEL_TEST_PKCS11_LABEL']),
'--pkcs11-pin={0}'.format(os.environ['GHOSTUNNEL_TEST_PKCS11_PIN']),
Expand Down
File renamed without changes.
59 changes: 59 additions & 0 deletions tests/test-server-reloads-split-cert-key.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env python3

"""
Ensures that tunnel sees & reloads a certificate
change.
"""

from common import LOCALHOST, RootCert, STATUS_PORT, SocketPair, TcpServer, TlsClient, print_ok, run_ghostunnel, terminate
import os
import signal

if __name__ == "__main__":
ghostunnel = None
try:
# create certs
root = RootCert('root')
root.create_signed_cert('server')
root.create_signed_cert('new_server')
root.create_signed_cert('client')

# start ghostunnel
ghostunnel = run_ghostunnel(['server',
'--listen={0}:13001'.format(LOCALHOST),
'--target={0}:13002'.format(LOCALHOST),
'--key=server.key',
'--cert=server.crt',
'--cacert=root.crt',
'--allow-ou=client',
'--status={0}:{1}'.format(LOCALHOST,
STATUS_PORT)])

# create connections with client
pair1 = SocketPair(
TlsClient('client', 'root', 13001), TcpServer(13002))
pair1.validate_can_send_from_client("toto", "pair1 works")
pair1.validate_tunnel_ou("server", "pair1 -> ou=server")

# Replace keystore and trigger reload
os.rename('new_server.crt', 'server.crt')
os.rename('new_server.key', 'server.key')
ghostunnel.send_signal(signal.SIGUSR1)

TlsClient(None, 'root', STATUS_PORT).connect(20, 'new_server')
print_ok("reload done")

# create connections with client
pair2 = SocketPair(
TlsClient('client', 'root', 13001), TcpServer(13002))
pair2.validate_can_send_from_client("toto", "pair2 works")
pair2.validate_tunnel_ou("new_server", "pair2 -> ou=new_server")
pair2.cleanup()

# ensure that pair1 is still alive
pair1.validate_can_send_from_client("toto", "pair1 still works")
pair1.cleanup()

print_ok("OK")
finally:
terminate(ghostunnel)
46 changes: 46 additions & 0 deletions tests/test-server-split-cert-key.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env python3

"""
Test that ensures that we can use the --cert/--key flags.
"""

from common import LOCALHOST, STATUS_PORT, TcpClient, print_ok, run_ghostunnel, terminate, RootCert, SocketPair, TlsClient, TcpServer
import urllib.request
import urllib.error
import urllib.parse
import os
import signal
import json
import sys

if __name__ == "__main__":
ghostunnel = None
try:
root = RootCert('root')
root.create_signed_cert('server')
root.create_signed_cert('client')

# start ghostunnel
ghostunnel = run_ghostunnel(['server',
'--listen={0}:13001'.format(LOCALHOST),
'--target={0}:13002'.format(LOCALHOST),
'--cert=server.crt',
'--key=server.key',
'--cacert=root.crt',
'--status={0}:{1}'.format(LOCALHOST,
STATUS_PORT),
'--allow-ou=client'])

# connect with client, confirm that the tunnel is up
pair = SocketPair(
TlsClient('client', 'root', 13001), TcpServer(13002))
pair.validate_can_send_from_client(
"hello world", "1: client -> server")
pair.validate_can_send_from_server(
"hello world", "1: server -> client")
pair.validate_closing_client_closes_server(
"1: client closed -> server closed")

print_ok("OK")
finally:
terminate(ghostunnel)
6 changes: 5 additions & 1 deletion tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ var cipherSuites = map[string][]uint16{
// Build reloadable certificate
func buildCertificate(keystorePath, certPath, keyPath, keystorePass string) (certloader.Certificate, error) {
if hasPKCS11() {
return buildCertificateFromPKCS11(keystorePath)
if keystorePath != "" {
return buildCertificateFromPKCS11(keystorePath)
} else {
return buildCertificateFromPKCS11(certPath)
}
}
if hasKeychainIdentity() {
return buildCertificateFromCertstore()
Expand Down

0 comments on commit 4df57d6

Please sign in to comment.