diff --git a/run_server.sh b/run_server.sh index 1ff4653d..dee76e64 100755 --- a/run_server.sh +++ b/run_server.sh @@ -2,6 +2,6 @@ echo "${@}" /opt/spire/tornjak-agent -c $2 http & /opt/spire/tornjak-agent -c /run/spire/config/server.conf http --tls --cert sample-keys/tls.pem --key sample-keys/key.pem --listen-addr :20000 & -/opt/spire/tornjak-agent -c /run/spire/config/server.conf http --mtls --cert sample-keys/mtls.pem --key sample-keys/key.pem --listen-addr :30000 & +/opt/spire/tornjak-agent -c /run/spire/config/server.conf http --mtls --cert sample-keys/tls.pem --key sample-keys/key.pem --mtls-ca sample-keys/rootCA.pem --listen-addr :30000 & # PORT=3000 npm start --prefix ./tornjak-frontend & /usr/bin/dumb-init /opt/spire/bin/spire-server run "${@}" diff --git a/sample-keys/ca_process/README b/sample-keys/ca_process/README deleted file mode 100644 index 6fd5142b..00000000 --- a/sample-keys/ca_process/README +++ /dev/null @@ -1,25 +0,0 @@ -# Create a CA - -Run `./create-ca.sh` to create a CA. It will put the necessary cert and key files in the `CA/` directory. - -# Signing a cert - -To create and sign a certificate run `./create-cert.sh `, for example `./create-cert.sh *.mydomain.com roks1`. This will create the files `roks1.key` and `roks1.crt` which will represent the key/cert pair to configure the server. - -# The configuration of the server will be - -## For TLS - -Client: -TLS: CA/rootCA.crt - -Server: roks1.key, roks1.crt - -## For mTLS - -Client: -TLS: CA/rootCA.crt, mTLS: client.key, client.crt - -Server: -TLS: roks1.key, roks1.crt -mTLS: CA/rootCA.crt diff --git a/sample-keys/ca_process/README.md b/sample-keys/ca_process/README.md new file mode 100644 index 00000000..172460a4 --- /dev/null +++ b/sample-keys/ca_process/README.md @@ -0,0 +1,71 @@ +# Managing Certificate Authority (CA) for Tornjak + +## Create a CA +Run `./create-ca.sh` to create a CA. It will put the necessary cert and key files in the `CA/` directory. + +## Signing a cert +Certificates are required for TLS and mTLS connections with Tornjak server. + +To create and sign a certificate run `./create-cert.sh ` + +Tornjak server example: + +```console +./create-cert.sh *.mydomain.com roks1 +``` + +This script creates `roks1.key` and `roks1.crt` files that represent +the key/cert pair to configure the server. + +Client example: + +```console +./create-cert.sh localhost client +``` + +This script creates `client.key` and `client.crt` files that represent +the key/cert pair to configure the client. + +## Tornjak Server and Client configuration +* SPIRE with Tornjak represent Server. +* Browser, `curl`, or Tornjak Manager represent clients. + +### For TLS + +``` +Client: +TLS: CA/rootCA.crt + +Server: +TLS: roks1.key, roks1.crt +``` + +### For mTLS +``` +Client: +TLS: CA/rootCA.crt +mTLS: client.key, client.crt + +Server: +TLS: roks1.key, roks1.crt +mTLS: CA/rootCA.crt +``` + +# Examples +Secret to configure the Tornjak Server: +```console +kubectl -n tornjak create secret generic tornjak-certs \ +--from-file=key.pem="roks1.key" \ +--from-file=tls.pem="roks1.crt" \ +--from-file=rootCA.pem="CA/rootCA.crt" +``` + +TLS client request: +```console +curl --cacert CA/rootCA.crt https:// +``` + +mTLS client request: +```console +curl --cacert CA/rootCA.crt --key client.key --cert client.crt https:// +``` diff --git a/tornjak-backend/api/agent/server.go b/tornjak-backend/api/agent/server.go index 1419b641..3c742f69 100644 --- a/tornjak-backend/api/agent/server.go +++ b/tornjak-backend/api/agent/server.go @@ -25,6 +25,7 @@ type Server struct { SpireServerAddr string CertPath string KeyPath string + MTlsCaPath string TlsEnabled bool MTlsEnabled bool @@ -479,6 +480,20 @@ func (s *Server) HandleRequests() { caCertPool := x509.NewCertPool() caCertPool.AppendCertsFromPEM(caCert) + tlsType := "TLS" + // If mTLS is enabled, add mTLS CA path to cert pool as well + if s.MTlsCaPath != "" { + if _, err := os.Stat(s.MTlsCaPath); os.IsNotExist(err) { + log.Fatalf("File does not exist %s", s.MTlsCaPath) + } + mTLSCaCert, err := ioutil.ReadFile(s.MTlsCaPath) + if err != nil { + log.Fatal(err) + } + caCertPool.AppendCertsFromPEM(mTLSCaCert) + tlsType = "mTLS" + } + // Create the TLS Config with the CA pool and enable Client certificate validation tlsConfig := &tls.Config{ @@ -496,7 +511,13 @@ func (s *Server) HandleRequests() { TLSConfig: tlsConfig, } - fmt.Printf("Starting to listen with TLS on %s...\n", s.ListenAddr) + fmt.Printf("Starting to listen with %s on %s...\n", tlsType, s.ListenAddr) + if _, err := os.Stat(s.CertPath); os.IsNotExist(err) { + log.Fatalf("File does not exist %s", s.CertPath) + } + if _, err := os.Stat(s.KeyPath); os.IsNotExist(err) { + log.Fatalf("File does not exist %s", s.KeyPath) + } log.Fatal(server.ListenAndServeTLS(s.CertPath, s.KeyPath)) return } else { diff --git a/tornjak-backend/api/manager/server.go b/tornjak-backend/api/manager/server.go index d36af941..cf33213f 100644 --- a/tornjak-backend/api/manager/server.go +++ b/tornjak-backend/api/manager/server.go @@ -18,6 +18,11 @@ var ( jsonContentType string = "application/json" ) +const ( + keyShowLen int = 40 + certShowLen int = 50 +) + type Server struct { listenAddr string db managerdb.ManagerDB @@ -72,7 +77,33 @@ func (s *Server) apiServerProxyFunc(apiPath string) func(w http.ResponseWriter, retError(w, emsg, http.StatusBadRequest) return } - fmt.Printf("%+v\n", sinfo) + + // Gather the certs and key into a map + cMap := make(map[string]string) + cMap["CA"] = string(sinfo.CA) + cMap["cert"] = string(sinfo.Cert) + cMap["key"] = string(sinfo.Key) + + // Iterate through the map and trim the values for debugging. + // Show the endings only + for k, v := range cMap { + if k == "key" { + if len(v) > keyShowLen { + cMap[k] = "\n..." + v[len(v)-keyShowLen:] + } + } else { + if len(v) > certShowLen { + cMap[k] = "\n..." + v[len(v)-certShowLen:] + } + } + } + fmt.Printf("Name:%s\n Address:%s\n TLS:%t, mTLS:%t\n", sinfo.Name, sinfo.Address, sinfo.TLS, sinfo.MTLS) + if sinfo.TLS { + fmt.Printf("CA:%s\n", cMap["CA"]) + } + if sinfo.MTLS { + fmt.Printf("Cert:%s\n Key:%s\n", cMap["cert"], cMap["key"]) + } client, err := sinfo.HttpClient() if err != nil { diff --git a/tornjak-backend/cmd/agent/agent.go b/tornjak-backend/cmd/agent/agent.go index b04f7b7e..51a8cde7 100644 --- a/tornjak-backend/cmd/agent/agent.go +++ b/tornjak-backend/cmd/agent/agent.go @@ -21,6 +21,7 @@ type cliOptions struct { listenAddr string certPath string keyPath string + mtlsCaPath string tls bool mtls bool } @@ -63,17 +64,24 @@ func main() { &cli.StringFlag{ Name: "cert", Value: "", - Usage: "CA Cert path for TLS/mTLS", + Usage: "CA Cert path for TLS", Destination: &opt.httpOptions.certPath, Required: false, }, &cli.StringFlag{ Name: "key", Value: "", - Usage: "Key path for TLS/mTLS", + Usage: "Key path for TLS", Destination: &opt.httpOptions.keyPath, Required: false, }, + &cli.StringFlag{ + Name: "mtls-ca", + Value: "", + Usage: "CA path for mTLS CA", + Destination: &opt.httpOptions.mtlsCaPath, + Required: false, + }, &cli.BoolFlag{ Name: "tls", Value: false, @@ -140,6 +148,7 @@ func runTornjakCmd(cmd string, opt cliOptions) error { ListenAddr: opt.httpOptions.listenAddr, CertPath: opt.httpOptions.certPath, KeyPath: opt.httpOptions.keyPath, + MTlsCaPath: opt.httpOptions.mtlsCaPath, TlsEnabled: opt.httpOptions.tls, MTlsEnabled: opt.httpOptions.mtls, SpireServerInfo: serverInfo, diff --git a/tornjak-backend/pkg/manager/types/serverinfo.go b/tornjak-backend/pkg/manager/types/serverinfo.go index 066d8f8c..1e33343b 100644 --- a/tornjak-backend/pkg/manager/types/serverinfo.go +++ b/tornjak-backend/pkg/manager/types/serverinfo.go @@ -25,7 +25,6 @@ func (s ServerInfo) HttpClient() (*http.Client, error) { if len(s.Cert) == 0 || len(s.Key) == 0 { return nil, errors.New("Cannot configure MTLS if not key or cert is provided") } - cert, err := tls.X509KeyPair(s.Cert, s.Key) if err != nil { return nil, err