From 45d7cddaa70213d296a4560cf3ace8ea3b40f6ca Mon Sep 17 00:00:00 2001 From: Tamara Boehm <34028368+b1tamara@users.noreply.github.com> Date: Fri, 14 Jul 2023 10:51:37 +0200 Subject: [PATCH] refactor: Reimplement TLS configuration in coding and in jobs config (#79) * wip: refactor: tls configuration * refactor: adapt tls spec config * refactor: Adapt integration tests to run with new TLS structs * refactor: Use client_cas instead of ca * refactor: Adapt acceptance tests to have a correct CN for bosh director cert --------- Co-authored-by: Maximilian Moehl Co-authored-by: Dominik Froehlich --- acceptance-tests/bosh_helpers.go | 24 ++- acceptance-tests/config.go | 61 +++--- jobs/pcap-agent/spec | 2 +- jobs/pcap-agent/templates/client-ca.crt.erb | 2 +- jobs/pcap-agent/templates/pcap-agent.yml.erb | 2 +- jobs/pcap-api/spec | 20 +- .../agents_mtls/pcap-api-client.ca.erb | 14 +- .../agents_mtls/pcap-api-client.crt.erb | 14 +- .../agents_mtls/pcap-api-client.key.erb | 14 +- .../templates/bosh_mtls/pcap-api-bosh.ca.erb | 7 - .../templates/bosh_mtls/pcap-api-bosh.crt.erb | 7 - .../templates/bosh_mtls/pcap-api-bosh.key.erb | 7 - jobs/pcap-api/templates/pcap-api-bosh.ca.erb | 13 ++ jobs/pcap-api/templates/pcap-api.ca.erb | 8 +- jobs/pcap-api/templates/pcap-api.crt.erb | 6 + jobs/pcap-api/templates/pcap-api.key.erb | 6 + jobs/pcap-api/templates/pcap-api.yml.erb | 18 +- manifests/pcap-acceptance-tests.yml | 7 +- manifests/pcap-api.yml | 6 +- spec/agent_spec.rb | 2 +- spec/api_agents_mtls_spec.rb | 4 +- spec/api_bosh_spec.rb | 20 +- spec/api_global_options_spec.rb | 2 +- src/pcap/api.go | 30 ++- src/pcap/bosh.go | 34 +--- src/pcap/client.go | 5 +- src/pcap/cmd/pcap-agent/config_test.go | 8 +- src/pcap/cmd/pcap-agent/main.go | 5 +- src/pcap/cmd/pcap-api/config.go | 2 +- src/pcap/cmd/pcap-api/config_test.go | 32 ++- src/pcap/cmd/pcap-api/main.go | 19 +- src/pcap/config.go | 184 +++++++++++------- src/pcap/config/agent.example.yml | 2 +- src/pcap/config/api.example.yml | 11 +- src/pcap/test/bosh_test.go | 2 +- src/pcap/test/integration/agent_api_client.go | 27 ++- src/pcap/test/integration/bosh_resolver.go | 7 +- src/pcap/test/integration/helpers.go | 11 +- 38 files changed, 343 insertions(+), 302 deletions(-) delete mode 100644 jobs/pcap-api/templates/bosh_mtls/pcap-api-bosh.ca.erb delete mode 100644 jobs/pcap-api/templates/bosh_mtls/pcap-api-bosh.crt.erb delete mode 100644 jobs/pcap-api/templates/bosh_mtls/pcap-api-bosh.key.erb create mode 100644 jobs/pcap-api/templates/pcap-api-bosh.ca.erb diff --git a/acceptance-tests/bosh_helpers.go b/acceptance-tests/bosh_helpers.go index 80318432..5a53ca4b 100644 --- a/acceptance-tests/bosh_helpers.go +++ b/acceptance-tests/bosh_helpers.go @@ -99,20 +99,28 @@ var opsfileStartApache = ` apt-get update && apt-get install apache2 -y && apache2ctl start ` +var opsfileChangeBoshDirectorCN string = `--- +# Replace bosh director cert common name with the right one +- type: replace + path: /instance_groups/name=pcap-api/jobs/name=pcap-api/properties/pcap-api/bosh/tls/common_name + value: ((director_common_name)) +` + // opsfiles that need to be set for all tests -var defaultOpsfiles = []string{opsfileChangeName, opsfileChangeVersion, opsfileAddSSHUser, opsfileStartApache} +var defaultOpsfiles = []string{opsfileChangeName, opsfileChangeVersion, opsfileChangeBoshDirectorCN, opsfileAddSSHUser, opsfileStartApache} var defaultSSHUser string = "ginkgo" // buildManifestVars returns a map of variables needed to deploy pcap. func buildManifestVars(baseManifestVars baseManifestVars, customVars map[string]interface{}) map[string]interface{} { vars := map[string]interface{}{ - "release-version": config.ReleaseVersion, - "director_ssl_ca": config.BoshDirectorCA, - "bosh_director_api": config.BoshDirectorAPI, - "director_ssl_cert": config.BoshDirectorCert, - "director_ssl_key": config.BoshDirectorKey, - "deployment-name": baseManifestVars.deploymentName, - "ssh_user": defaultSSHUser, + "release-version": config.ReleaseVersion, + "director_ssl_ca": config.BoshDirectorCA, + "bosh_director_api": config.BoshDirectorAPI, + "director_ssl_cert": config.BoshDirectorCert, + "director_ssl_key": config.BoshDirectorKey, + "director_common_name": config.BoshDirectorCertCN, + "deployment-name": baseManifestVars.deploymentName, + "ssh_user": defaultSSHUser, } for k, v := range customVars { vars[k] = v diff --git a/acceptance-tests/config.go b/acceptance-tests/config.go index 51cd5ea7..ea3cb1bb 100644 --- a/acceptance-tests/config.go +++ b/acceptance-tests/config.go @@ -1,6 +1,8 @@ package acceptance_tests import ( + "crypto/x509" + "encoding/pem" "fmt" "os" "os/exec" @@ -9,18 +11,19 @@ import ( var config Config type Config struct { - ReleaseRepoPath string `json:"releaseRepoPath"` - ReleaseVersion string `json:"releaseVersion"` - BoshDirectorAPI string `json:"boshDirectorAPI"` - BoshDirectorCert string `json:"boshDirectorCert"` - BoshDirectorKey string `json:"boshDirectorKey"` - BoshDirectorCA string `json:"boshDirectorCA"` - BoshClient string `json:"boshClient"` - BoshClientSecret string `json:"boshClientSecret"` - BoshEnvironment string `json:"boshEnvironment"` - BoshPath string `json:"boshPath"` - BaseManifestPath string `json:"baseManifestPath"` - HomePath string `json:"homePath"` + ReleaseRepoPath string `json:"releaseRepoPath"` + ReleaseVersion string `json:"releaseVersion"` + BoshDirectorAPI string `json:"boshDirectorAPI"` + BoshDirectorCertCN string `json:"boshDirectorCertCN"` + BoshDirectorCert string `json:"boshDirectorCert"` + BoshDirectorKey string `json:"boshDirectorKey"` + BoshDirectorCA string `json:"boshDirectorCA"` + BoshClient string `json:"boshClient"` + BoshClientSecret string `json:"boshClientSecret"` + BoshEnvironment string `json:"boshEnvironment"` + BoshPath string `json:"boshPath"` + BaseManifestPath string `json:"baseManifestPath"` + HomePath string `json:"homePath"` } func loadConfig() (Config, error) { @@ -84,20 +87,30 @@ func loadConfig() (Config, error) { if err != nil { return Config{}, err } + // extract Bosh Director SSL Certificate Common Name + block, _ := pem.Decode([]byte(boshDirectorCert)) + if block == nil { + return Config{}, fmt.Errorf("failed to parse PEM block containing the public key") + } + + cert, _ := x509.ParseCertificate(block.Bytes) // handle error + + boshDirectorCertCN := cert.Subject.CommonName return Config{ - ReleaseRepoPath: releaseRepoPath, - ReleaseVersion: releaseVersion, - BoshDirectorAPI: boshDirectorAPI, - BoshDirectorCert: boshDirectorCert, - BoshDirectorKey: boshDirectorKey, - BoshDirectorCA: boshDirectorCA, - BoshClient: boshClient, - BoshClientSecret: boshClientSecret, - BoshEnvironment: boshEnvironment, - BoshPath: boshPath, - BaseManifestPath: baseManifestPath, - HomePath: homePath, + ReleaseRepoPath: releaseRepoPath, + ReleaseVersion: releaseVersion, + BoshDirectorAPI: boshDirectorAPI, + BoshDirectorCertCN: boshDirectorCertCN, + BoshDirectorCert: boshDirectorCert, + BoshDirectorKey: boshDirectorKey, + BoshDirectorCA: boshDirectorCA, + BoshClient: boshClient, + BoshClientSecret: boshClientSecret, + BoshEnvironment: boshEnvironment, + BoshPath: boshPath, + BaseManifestPath: baseManifestPath, + HomePath: homePath, }, nil } diff --git a/jobs/pcap-agent/spec b/jobs/pcap-agent/spec index ec2d9bda..b89a8fe4 100644 --- a/jobs/pcap-agent/spec +++ b/jobs/pcap-agent/spec @@ -36,5 +36,5 @@ properties: description: "Certificate and chain to talk to pcap-api in PEM format" pcap-agent.listen.tls.private_key: description: "Private key to talk to pcap-api in PEM format" - pcap-agent.listen.tls.ca: + pcap-agent.listen.tls.client_cas: description: "CA bundle which is used to request and verify client certificates" diff --git a/jobs/pcap-agent/templates/client-ca.crt.erb b/jobs/pcap-agent/templates/client-ca.crt.erb index 107b7671..3dbe42a5 100644 --- a/jobs/pcap-agent/templates/client-ca.crt.erb +++ b/jobs/pcap-agent/templates/client-ca.crt.erb @@ -1,3 +1,3 @@ -<%- if_p("pcap-agent.listen.tls.ca") do |client_ca| -%> +<%- if_p("pcap-agent.listen.tls.client_cas") do |client_ca| -%> <%= client_ca -%> <%- end -%> diff --git a/jobs/pcap-agent/templates/pcap-agent.yml.erb b/jobs/pcap-agent/templates/pcap-agent.yml.erb index 07d30b3e..1f9b872d 100644 --- a/jobs/pcap-agent/templates/pcap-agent.yml.erb +++ b/jobs/pcap-agent/templates/pcap-agent.yml.erb @@ -7,7 +7,7 @@ config = { "tls" => { "certificate"=> "/var/vcap/jobs/pcap-agent/config/certs/pcap-agent.crt", "private_key" => "/var/vcap/jobs/pcap-agent/config/certs/pcap-agent.key", - "ca" => "/var/vcap/jobs/pcap-agent/config/certs/client-ca.crt", + "client_cas" => "/var/vcap/jobs/pcap-agent/config/certs/client-ca.crt", }, }, "buffer" => { diff --git a/jobs/pcap-api/spec b/jobs/pcap-api/spec index dbbbfba6..d02e2300 100644 --- a/jobs/pcap-api/spec +++ b/jobs/pcap-api/spec @@ -9,9 +9,7 @@ templates: pcap-api.crt.erb: config/certs/pcap-api.crt pcap-api.key.erb: config/certs/pcap-api.key pcap-api.ca.erb: config/certs/pcap-api-ca.crt - bosh_mtls/pcap-api-bosh.ca.erb: config/certs/bosh/pcap-api-bosh-ca.crt - bosh_mtls/pcap-api-bosh.crt.erb: config/certs/bosh/pcap-api-bosh.crt - bosh_mtls/pcap-api-bosh.key.erb: config/certs/bosh/pcap-api-bosh.key + pcap-api-bosh.ca.erb: config/certs/bosh/pcap-api-bosh-ca.crt agents_mtls/pcap-api-client.crt.erb: config/certs/pcap-api-client.crt agents_mtls/pcap-api-client.key.erb: config/certs/pcap-api-client.key agents_mtls/pcap-api-client.ca.erb: config/certs/pcap-api-client-ca.crt @@ -44,7 +42,7 @@ properties: description: "Certificate chain to talk to gorouter in PEM format" pcap-api.listen.tls.private_key: description: "Private key to talk to gorouter in PEM format" - pcap-api.listen.tls.ca: + pcap-api.listen.tls.client_cas: description: "CA bundle which is used to request and verify client certificates" # platform CA (gorouter CA) pcap-api.agents_mtls.enabled: @@ -70,19 +68,15 @@ properties: description: "Endpoint of the BOSH Director API" pcap-api.bosh.token_scope: description: "Scope of the token" - pcap-api.bosh.mtls.enabled: + pcap-api.bosh.tls.enabled: default: true - pcap-api.bosh.mtls.common_name: + pcap-api.bosh.tls.common_name: description: "Common name of the Bosh Director" - pcap-api.bosh.mtls.skip_verify: + pcap-api.bosh.tls.skip_verify: description: "Skip server verification for connection to Bosh Director" default: false - pcap-api.bosh.mtls.certificate: - description: "Client certificate to talk to Bosh Director in PEM format" - pcap-api.bosh.mtls.private_key: - description: "Private key to talk to Bosh Director in PEM format" - pcap-api.bosh.mtls.ca: - description: "CA bundle which is used to request and verify Bosh Director client certificates" + pcap-api.bosh.tls.ca: + description: "CA bundle which is used to request and verify Bosh Director certificates" pcap-api.cli_download_root: diff --git a/jobs/pcap-api/templates/agents_mtls/pcap-api-client.ca.erb b/jobs/pcap-api/templates/agents_mtls/pcap-api-client.ca.erb index 1809002d..64a582ce 100644 --- a/jobs/pcap-api/templates/agents_mtls/pcap-api-client.ca.erb +++ b/jobs/pcap-api/templates/agents_mtls/pcap-api-client.ca.erb @@ -1,7 +1,9 @@ -<% -if_p("pcap-api.agents_mtls.ca") do |pem| -%> +<%- if p("pcap-api.agents_mtls.enabled").to_s == "true" + if !p("pcap-api.agents_mtls.ca", nil) + raise "Conflicting configuration: pcap-api.agents_mtls.enabled is true, you must provide a valid client CAs" + end + end +-%> +<%- if_p("pcap-api.agents_mtls.ca") do |pem| -%> <%= pem %> -<% -end -%> +<%- end -%> diff --git a/jobs/pcap-api/templates/agents_mtls/pcap-api-client.crt.erb b/jobs/pcap-api/templates/agents_mtls/pcap-api-client.crt.erb index c1c6f5cc..29971946 100644 --- a/jobs/pcap-api/templates/agents_mtls/pcap-api-client.crt.erb +++ b/jobs/pcap-api/templates/agents_mtls/pcap-api-client.crt.erb @@ -1,7 +1,9 @@ -<% -if_p("pcap-api.agents_mtls.certificate") do |pem| -%> +<%- if p("pcap-api.agents_mtls.enabled").to_s == "true" + if !p("pcap-api.agents_mtls.certificate", nil) + raise "Conflicting configuration: pcap-api.agents_mtls.enabled is true, you must provide a valid certificate" + end + end +-%> +<%- if_p("pcap-api.agents_mtls.certificate") do |pem| -%> <%= pem %> -<% -end -%> +<%- end -%> diff --git a/jobs/pcap-api/templates/agents_mtls/pcap-api-client.key.erb b/jobs/pcap-api/templates/agents_mtls/pcap-api-client.key.erb index e22e3941..09f74cc0 100644 --- a/jobs/pcap-api/templates/agents_mtls/pcap-api-client.key.erb +++ b/jobs/pcap-api/templates/agents_mtls/pcap-api-client.key.erb @@ -1,7 +1,9 @@ -<% -if_p("pcap-api.agents_mtls.private_key") do |pem| -%> +<%- if p("pcap-api.agents_mtls.enabled").to_s == "true" + if !p("pcap-api.agents_mtls.private_key", nil) + raise "Conflicting configuration: pcap-api.agents_mtls.enabled is true, you must provide a valid private key" + end + end +-%> +<%- if_p("pcap-api.agents_mtls.private_key") do |pem| -%> <%= pem %> -<% -end -%> +<%- end -%> diff --git a/jobs/pcap-api/templates/bosh_mtls/pcap-api-bosh.ca.erb b/jobs/pcap-api/templates/bosh_mtls/pcap-api-bosh.ca.erb deleted file mode 100644 index fb380303..00000000 --- a/jobs/pcap-api/templates/bosh_mtls/pcap-api-bosh.ca.erb +++ /dev/null @@ -1,7 +0,0 @@ -<% -if_p("pcap-api.bosh.mtls.ca") do |pem| -%> -<%= pem %> -<% -end -%> diff --git a/jobs/pcap-api/templates/bosh_mtls/pcap-api-bosh.crt.erb b/jobs/pcap-api/templates/bosh_mtls/pcap-api-bosh.crt.erb deleted file mode 100644 index c7f48d0b..00000000 --- a/jobs/pcap-api/templates/bosh_mtls/pcap-api-bosh.crt.erb +++ /dev/null @@ -1,7 +0,0 @@ -<% -if_p("pcap-api.bosh.mtls.certificate") do |pem| -%> -<%= pem %> -<% -end -%> diff --git a/jobs/pcap-api/templates/bosh_mtls/pcap-api-bosh.key.erb b/jobs/pcap-api/templates/bosh_mtls/pcap-api-bosh.key.erb deleted file mode 100644 index eba7edab..00000000 --- a/jobs/pcap-api/templates/bosh_mtls/pcap-api-bosh.key.erb +++ /dev/null @@ -1,7 +0,0 @@ -<% -if_p("pcap-api.bosh.mtls.private_key") do |pem| -%> -<%= pem %> -<% -end -%> diff --git a/jobs/pcap-api/templates/pcap-api-bosh.ca.erb b/jobs/pcap-api/templates/pcap-api-bosh.ca.erb new file mode 100644 index 00000000..9ebf7acd --- /dev/null +++ b/jobs/pcap-api/templates/pcap-api-bosh.ca.erb @@ -0,0 +1,13 @@ +<%- if p("pcap-api.bosh.tls.enabled").to_s == "true" + if !p("pcap-api.bosh.tls.ca", nil) + raise "Conflicting configuration: pcap-api.bosh.tls.enabled, you must provide a valid Bosh CAs" + end + end +-%> +<% +if_p("pcap-api.bosh.tls.ca") do |pem| +%> +<%= pem %> +<% +end +%> diff --git a/jobs/pcap-api/templates/pcap-api.ca.erb b/jobs/pcap-api/templates/pcap-api.ca.erb index 8b1c8401..6166693c 100644 --- a/jobs/pcap-api/templates/pcap-api.ca.erb +++ b/jobs/pcap-api/templates/pcap-api.ca.erb @@ -1,3 +1,9 @@ +<%- if p("pcap-api.listen.tls.enabled").to_s == "true" + if !p("pcap-api.listen.tls.client_cas", nil) + raise "Conflicting configuration: pcap-api.listen.tls.enabled is true, you must provide a valid client CA" + end + end +-%> <%- if_p("pcap-api.listen.tls.ca") do |pem| -%> <%= pem %> -<%- end -%> \ No newline at end of file +<%- end -%> diff --git a/jobs/pcap-api/templates/pcap-api.crt.erb b/jobs/pcap-api/templates/pcap-api.crt.erb index a29973e1..87768dcc 100644 --- a/jobs/pcap-api/templates/pcap-api.crt.erb +++ b/jobs/pcap-api/templates/pcap-api.crt.erb @@ -1,3 +1,9 @@ +<%- if p("pcap-api.listen.tls.enabled").to_s == "true" + if !p("pcap-api.listen.tls.certificate", nil) + raise "Conflicting configuration: pcap-api.listen.tls.enabled is true, you must provide a valid certificate" + end + end +-%> <%- if_p("pcap-api.listen.tls.certificate") do |pem| -%> <%= pem %> <%- end -%> diff --git a/jobs/pcap-api/templates/pcap-api.key.erb b/jobs/pcap-api/templates/pcap-api.key.erb index 33803b49..745d838e 100644 --- a/jobs/pcap-api/templates/pcap-api.key.erb +++ b/jobs/pcap-api/templates/pcap-api.key.erb @@ -1,3 +1,9 @@ +<%- if p("pcap-api.listen.tls.enabled").to_s == "true" + if !p("pcap-api.listen.tls.private_key", nil) + raise "Conflicting configuration: pcap-api.listen.tls.enabled is true, you must provide a valid private key" + end + end +-%> <%- if_p("pcap-api.listen.tls.private_key") do |pem| -%> <%= pem %> <%- end -%> diff --git a/jobs/pcap-api/templates/pcap-api.yml.erb b/jobs/pcap-api/templates/pcap-api.yml.erb index bf46ce28..9ed90407 100644 --- a/jobs/pcap-api/templates/pcap-api.yml.erb +++ b/jobs/pcap-api/templates/pcap-api.yml.erb @@ -18,13 +18,13 @@ if p("pcap-api.listen.tls.enabled").to_s == "true" config["listen"]["tls"] = { "certificate"=> "/var/vcap/jobs/pcap-api/config/certs/pcap-api.crt", "private_key" => "/var/vcap/jobs/pcap-api/config/certs/pcap-api.key", - "ca" => "/var/vcap/jobs/pcap-api/config/certs/pcap-api-ca.crt" + "client_cas" => "/var/vcap/jobs/pcap-api/config/certs/pcap-api-ca.crt" } end if p("pcap-api.agents_mtls.enabled").to_s == "true" config["agents_mtls"] = { - "common_name" => p("pcap-api.agents_mtls.common_name"), + "server_name" => p("pcap-api.agents_mtls.common_name"), "skip_verify" => p("pcap-api.agents_mtls.skip_verify"), "certificate" => "/var/vcap/jobs/pcap-api/config/certs/pcap-api-client.crt", "private_key" => '/var/vcap/jobs/pcap-api/config/certs/pcap-api-client.key', @@ -33,13 +33,11 @@ if p("pcap-api.agents_mtls.enabled").to_s == "true" end if_p("pcap-api.bosh.director_url", "pcap-api.bosh.token_scope") do - bosh_mtls = nil - if p("pcap-api.bosh.mtls.enabled").to_s == "true" - bosh_mtls = { - "common_name" => p("pcap-api.bosh.mtls.common_name"), - "skip_verify" => p("pcap-api.bosh.mtls.skip_verify"), - "certificate" => '/var/vcap/jobs/pcap-api/config/certs/bosh/pcap-api-bosh.crt', - "private_key" => '/var/vcap/jobs/pcap-api/config/certs/bosh/pcap-api-bosh.key', + bosh_tls = nil + if p("pcap-api.bosh.tls.enabled").to_s == "true" + bosh_tls = { + "server_name" => p("pcap-api.bosh.tls.common_name"), + "skip_verify" => p("pcap-api.bosh.tls.skip_verify"), "ca" => '/var/vcap/jobs/pcap-api/config/certs/bosh/pcap-api-bosh-ca.crt' } end @@ -47,7 +45,7 @@ if_p("pcap-api.bosh.director_url", "pcap-api.bosh.token_scope") do "agent_port" => p("pcap-api.bosh.agent_port"), "director_url" => p("pcap-api.bosh.director_url"), "token_scope" => p("pcap-api.bosh.token_scope"), - "mtls" => bosh_mtls + "tls" => bosh_tls } end diff --git a/manifests/pcap-acceptance-tests.yml b/manifests/pcap-acceptance-tests.yml index 6eca2e5b..b8c4808c 100644 --- a/manifests/pcap-acceptance-tests.yml +++ b/manifests/pcap-acceptance-tests.yml @@ -26,11 +26,10 @@ instance_groups: director_url: ((bosh_director_api)) token_scope: bosh.admin agent_port: 9494 - mtls: + tls: + enabled: true common_name: bosh.service.cf.internal skip_verify: false - certificate: ((director_ssl_cert)) - private_key: ((director_ssl_key)) ca: ((director_ssl_ca)) agents_mtls: common_name: pcap-agent.service.cf.internal @@ -61,7 +60,7 @@ instance_groups: tls: certificate: ((pcap_agent_tls.certificate)) private_key: ((pcap_agent_tls.private_key)) - ca: ((pcap_agent_tls.ca)) + client_cas: ((pcap_agent_tls.ca)) update: canaries: 1 diff --git a/manifests/pcap-api.yml b/manifests/pcap-api.yml index 5746dfb5..7287f29b 100644 --- a/manifests/pcap-api.yml +++ b/manifests/pcap-api.yml @@ -53,11 +53,9 @@ instance_groups: director_url: ((pcap_api.bosh_director_api)) token_scope: bosh.admin agent_port: 9494 - mtls: + tls: common_name: bosh.service.cf.internal skip_verify: false - certificate: ((pcap_api_bosh_mtls.certificate)) - private_key: ((pcap_api_bosh_mtls.private_key)) ca: ((/bootstrap-bosh/bosh/default_ca.ca)) agents_mtls: common_name: pcap-agent.service.cf.internal @@ -99,7 +97,7 @@ instance_groups: tls: certificate: ((pcap_agent_mtls.certificate)) private_key: ((pcap_agent_mtls.private_key)) - ca: ((pcap_agent_mtls.ca)) + client_cas: ((pcap_agent_mtls.ca)) update: canaries: 1 diff --git a/spec/agent_spec.rb b/spec/agent_spec.rb index 4d6049ca..32721cf9 100644 --- a/spec/agent_spec.rb +++ b/spec/agent_spec.rb @@ -79,7 +79,7 @@ expect(pcap_agent_conf['listen']['port']).to be(9494) expect(pcap_agent_conf['listen']['tls']['certificate']).to include('/var/vcap/jobs/pcap-agent/config/certs/pcap-agent.crt') expect(pcap_agent_conf['listen']['tls']['private_key']).to include('/var/vcap/jobs/pcap-agent/config/certs/pcap-agent.key') - expect(pcap_agent_conf['listen']['tls']['ca']).to include('/var/vcap/jobs/pcap-agent/config/certs/client-ca.crt') + expect(pcap_agent_conf['listen']['tls']['client_cas']).to include('/var/vcap/jobs/pcap-agent/config/certs/client-ca.crt') end end diff --git a/spec/api_agents_mtls_spec.rb b/spec/api_agents_mtls_spec.rb index bf663768..9bd76a96 100644 --- a/spec/api_agents_mtls_spec.rb +++ b/spec/api_agents_mtls_spec.rb @@ -48,7 +48,7 @@ it 'configures correctly' do properties.merge!(agents_mtls_properties) expect(pcap_api_conf['agents_mtls']['skip_verify']).to be(true) - expect(pcap_api_conf['agents_mtls']['common_name']).to include('pcap-agent-test.service.cf.internal') + expect(pcap_api_conf['agents_mtls']['server_name']).to include('pcap-agent-test.service.cf.internal') expect(pcap_api_conf['agents_mtls']['certificate']).to include('/var/vcap/jobs/pcap-api/config/certs/pcap-api-client.crt') expect(pcap_api_conf['agents_mtls']['private_key']).to include('/var/vcap/jobs/pcap-api/config/certs/pcap-api-client.key') expect(pcap_api_conf['agents_mtls']['ca']).to include('/var/vcap/jobs/pcap-api/config/certs/pcap-api-client-ca.crt') @@ -67,7 +67,7 @@ it 'takes defaults correctly' do properties.merge!(agents_mtls_properties) expect(pcap_api_conf['agents_mtls']['skip_verify']).to be(false) - expect(pcap_api_conf['agents_mtls']['common_name']).to include('pcap-agent.service.cf.internal') + expect(pcap_api_conf['agents_mtls']['server_name']).to include('pcap-agent.service.cf.internal') expect(pcap_api_conf['agents_mtls']['certificate']).to include('/var/vcap/jobs/pcap-api/config/certs/pcap-api-client.crt') expect(pcap_api_conf['agents_mtls']['private_key']).to include('/var/vcap/jobs/pcap-api/config/certs/pcap-api-client.key') expect(pcap_api_conf['agents_mtls']['ca']).to include('/var/vcap/jobs/pcap-api/config/certs/pcap-api-client-ca.crt') diff --git a/spec/api_bosh_spec.rb b/spec/api_bosh_spec.rb index 6f7d26ae..4d10a35b 100644 --- a/spec/api_bosh_spec.rb +++ b/spec/api_bosh_spec.rb @@ -27,7 +27,7 @@ 'agent_port' => 9495, 'director_url' => 'https://bosh.service.cf.internal:8080', 'token_scope' => 'bosh.admin', - 'mtls' => + 'tls' => { 'enabled' => false } @@ -40,7 +40,7 @@ expect(pcap_api_conf['bosh']['agent_port']).to be(9495) expect(pcap_api_conf['bosh']['director_url']).to include('https://bosh.service.cf.internal:8080') expect(pcap_api_conf['bosh']['token_scope']).to include('bosh.admin') - expect(pcap_api_conf['bosh']['mtls']).to be_nil + expect(pcap_api_conf['bosh']['tls']).to be_nil end end @@ -51,7 +51,7 @@ { 'director_url' => 'https://bosh.service.cf.internal:8080', 'token_scope' => 'bosh.admin', - 'mtls' => { + 'tls' => { 'enabled' => true, 'common_name' => 'bosh.service.cf.internal', 'skip_verify' => true @@ -63,10 +63,8 @@ it 'configures bosh correctly' do properties.merge!(bosh_properties) expect(pcap_api_conf['bosh']['director_url']).to include('https://bosh.service.cf.internal:8080') - expect(pcap_api_conf['bosh']['mtls']['skip_verify']).to be(true) - expect(pcap_api_conf['bosh']['mtls']['certificate']).to include('/var/vcap/jobs/pcap-api/config/certs/bosh/pcap-api-bosh.crt') - expect(pcap_api_conf['bosh']['mtls']['private_key']).to include('/var/vcap/jobs/pcap-api/config/certs/bosh/pcap-api-bosh.key') - expect(pcap_api_conf['bosh']['mtls']['ca']).to include('/var/vcap/jobs/pcap-api/config/certs/bosh/pcap-api-bosh-ca.crt') + expect(pcap_api_conf['bosh']['tls']['skip_verify']).to be(true) + expect(pcap_api_conf['bosh']['tls']['ca']).to include('/var/vcap/jobs/pcap-api/config/certs/bosh/pcap-api-bosh-ca.crt') end end @@ -77,7 +75,7 @@ { 'director_url' => 'https://bosh.service.cf.internal:8080', 'token_scope' => 'bosh.admin', - 'mtls' => { + 'tls' => { 'enabled' => true, 'common_name' => 'bosh.service.cf.internal', 'skip_verify' => false @@ -89,10 +87,8 @@ it 'configures bosh correctly' do properties.merge!(bosh_properties) expect(pcap_api_conf['bosh']['director_url']).to include('https://bosh.service.cf.internal:8080') - expect(pcap_api_conf['bosh']['mtls']['skip_verify']).to be(false) - expect(pcap_api_conf['bosh']['mtls']['certificate']).to include('/var/vcap/jobs/pcap-api/config/certs/bosh/pcap-api-bosh.crt') - expect(pcap_api_conf['bosh']['mtls']['private_key']).to include('/var/vcap/jobs/pcap-api/config/certs/bosh/pcap-api-bosh.key') - expect(pcap_api_conf['bosh']['mtls']['ca']).to include('/var/vcap/jobs/pcap-api/config/certs/bosh/pcap-api-bosh-ca.crt') + expect(pcap_api_conf['bosh']['tls']['skip_verify']).to be(false) + expect(pcap_api_conf['bosh']['tls']['ca']).to include('/var/vcap/jobs/pcap-api/config/certs/bosh/pcap-api-bosh-ca.crt') end end end diff --git a/spec/api_global_options_spec.rb b/spec/api_global_options_spec.rb index 011009e0..8ed37fc3 100644 --- a/spec/api_global_options_spec.rb +++ b/spec/api_global_options_spec.rb @@ -112,7 +112,7 @@ properties.merge!(listen) expect(pcap_api_conf['listen']['tls']['certificate']).to include('/var/vcap/jobs/pcap-api/config/certs/pcap-api.crt') expect(pcap_api_conf['listen']['tls']['private_key']).to include('/var/vcap/jobs/pcap-api/config/certs/pcap-api.key') - expect(pcap_api_conf['listen']['tls']['ca']).to include('/var/vcap/jobs/pcap-api/config/certs/pcap-api-ca.crt') + expect(pcap_api_conf['listen']['tls']['client_cas']).to include('/var/vcap/jobs/pcap-api/config/certs/pcap-api-ca.crt') end end diff --git a/src/pcap/api.go b/src/pcap/api.go index ca5b39f8..189b3b18 100644 --- a/src/pcap/api.go +++ b/src/pcap/api.go @@ -35,22 +35,24 @@ type API struct { UnimplementedAPIServer } -func NewAPI(bufConf BufferConf, agentmTLS *MutualTLS, id string, maxConcurrentCaptures uint) (*API, error) { - var err error +func NewAPI(bufConf BufferConf, clientTLS *ClientTLS, id string, maxConcurrentCaptures uint) (*API, error) { + clientTLSCreds := insecure.NewCredentials() + if clientTLS != nil { + clientTLSConf, err := clientTLS.Config() + if err != nil { + return nil, fmt.Errorf("create api failed: %w", err) + } + clientTLSCreds = credentials.NewTLS(clientTLSConf) + } - api := &API{ + return &API{ done: make(chan struct{}), bufConf: bufConf, resolvers: make(map[string]AgentResolver), id: id, maxConcurrentCaptures: maxConcurrentCaptures, - } - api.tlsCredentials, err = loadTLSCredentials(agentmTLS) - if err != nil { - return nil, fmt.Errorf("create api failed: %w", err) - } - - return api, nil + tlsCredentials: clientTLSCreds, + }, nil } // AgentEndpoint defines the endpoint for a pcap-agent. @@ -243,14 +245,6 @@ func (api *API) Capture(stream API_CaptureServer) (err error) { return nil } -func loadTLSCredentials(agentMTLS *MutualTLS) (credentials.TransportCredentials, error) { - if agentMTLS == nil || agentMTLS.SkipVerify { - return insecure.NewCredentials(), nil - } - - return LoadTLSCredentials(agentMTLS.Certificate, agentMTLS.PrivateKey, nil, &agentMTLS.CertificateAuthority, &agentMTLS.CommonName) -} - // resolveAgentEndpoints tries all registered api.resolvers until one responds or none can be found that // support this EndpointRequest. The responsible resolver is then queried for the applicable pcap-agent endpoints corresponding to this EndpointRequest. func (api *API) resolveAgentEndpoints(request *EndpointRequest, log *zap.Logger) ([]AgentEndpoint, error) { diff --git a/src/pcap/bosh.go b/src/pcap/bosh.go index 77310ca1..a9d5dd28 100644 --- a/src/pcap/bosh.go +++ b/src/pcap/bosh.go @@ -3,7 +3,6 @@ package pcap import ( "crypto/rsa" "crypto/tls" - "crypto/x509" "encoding/json" "errors" "fmt" @@ -57,7 +56,7 @@ type BoshResolverConfig struct { RawDirectorURL string `yaml:"director_url" validate:"required,url"` AgentPort int `yaml:"agent_port" validate:"required,gt=0,lte=65535"` TokenScope string `yaml:"token_scope" validate:"required"` - MTLS *MutualTLS `yaml:"mtls" validate:"omitempty"` + TLS *ClientTLS `yaml:"tls" validate:"omitempty"` } // BoshResolver uses a BOSH director to resolve AgentEndpoint s. @@ -69,7 +68,7 @@ type BoshResolver struct { Config BoshResolverConfig DirectorURL *url.URL logger *zap.Logger - boshRootCAs *x509.CertPool + tlsConf *tls.Config } // NewBoshResolver creates and initializes a BoshResolver based on the provided config. @@ -88,19 +87,17 @@ func NewBoshResolver(config BoshResolverConfig) (*BoshResolver, error) { directorURL.Path = "/" } - var boshRootCAs *x509.CertPool - if config.MTLS != nil { - boshRootCAs, err = createCAPool(config.MTLS.CertificateAuthority) - if err != nil { - return nil, fmt.Errorf("could not create bosh CA pool: %w", err) - } - } - resolver := &BoshResolver{ logger: zap.L().With(zap.String(LogKeyHandler, BoshResolverName)), Config: config, DirectorURL: directorURL, - boshRootCAs: boshRootCAs, + } + + if config.TLS != nil { + resolver.tlsConf, err = config.TLS.Config() + if err != nil { + return nil, err + } } err = resolver.setup() @@ -224,17 +221,6 @@ func (br *BoshResolver) Validate(endpointRequest *EndpointRequest) error { func (br *BoshResolver) setup() error { br.logger.Debug("setting up BoshResolver", zap.Any("resolver-config", br.Config)) - var tlsConfig *tls.Config - - if br.Config.MTLS != nil { - tlsConfig = &tls.Config{ - MinVersion: tls.VersionTLS12, - MaxVersion: tls.VersionTLS13, - ClientAuth: tls.RequireAndVerifyClientCert, - RootCAs: br.boshRootCAs, - } - } - timeout := 500 * time.Millisecond //nolint:gomnd // reasonable value. br.client = &http.Client{ @@ -247,7 +233,7 @@ func (br *BoshResolver) setup() error { ExpectContinueTimeout: timeout, DisableKeepAlives: true, MaxIdleConnsPerHost: -1, - TLSClientConfig: tlsConfig, + TLSClientConfig: br.tlsConf, }, Timeout: timeout, } diff --git a/src/pcap/client.go b/src/pcap/client.go index 0a5112a8..82c92b64 100644 --- a/src/pcap/client.go +++ b/src/pcap/client.go @@ -109,10 +109,7 @@ func (c *Client) ConnectToAPI(apiURL *url.URL) error { ) if apiURL.Scheme == "https" { - creds, err = LoadTLSCredentials("", "", nil, nil, nil) - if err != nil { - return fmt.Errorf("could not generate TLS credentials: %w", err) - } + creds = credentials.NewTLS(newTLSConfig()) } else { // plain http creds = insecure.NewCredentials() } diff --git a/src/pcap/cmd/pcap-agent/config_test.go b/src/pcap/cmd/pcap-agent/config_test.go index 3b8d073f..dcab97c7 100644 --- a/src/pcap/cmd/pcap-agent/config_test.go +++ b/src/pcap/cmd/pcap-agent/config_test.go @@ -21,10 +21,10 @@ func TestConfig(t *testing.T) { NodeConfig: pcap.NodeConfig{ Listen: pcap.Listen{ Port: 9494, - TLS: &pcap.TLS{ - Certificate: "agent-cert.pem", - PrivateKey: "agent-cert.key", - CertificateAuthority: "pcap-ca.pem", + TLS: &pcap.ServerTLS{ + Certificate: "agent-cert.pem", + PrivateKey: "agent-cert.key", + ClientCas: "pcap-ca.pem", }, }, Buffer: pcap.BufferConf{ diff --git a/src/pcap/cmd/pcap-agent/main.go b/src/pcap/cmd/pcap-agent/main.go index 07cf28a6..62f0e56d 100644 --- a/src/pcap/cmd/pcap-agent/main.go +++ b/src/pcap/cmd/pcap-agent/main.go @@ -15,6 +15,7 @@ import ( "go.uber.org/zap" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" ) func main() { @@ -57,12 +58,14 @@ func main() { return } - tlsCredentials, err := config.TLSCredentials() + tlsConfig, err := config.NodeConfig.Listen.TLS.Config() if err != nil { log.Error("unable to load provided TLS credentials", zap.Error(err)) return } + tlsCredentials := credentials.NewTLS(tlsConfig) + server := grpc.NewServer(grpc.Creds(tlsCredentials)) pcap.RegisterAgentServer(server, agent) diff --git a/src/pcap/cmd/pcap-api/config.go b/src/pcap/cmd/pcap-api/config.go index a206f794..f8067474 100644 --- a/src/pcap/cmd/pcap-api/config.go +++ b/src/pcap/cmd/pcap-api/config.go @@ -29,7 +29,7 @@ var DefaultAPIConfig = APIConfig{ type APIConfig struct { pcap.NodeConfig `yaml:"-,inline"` - AgentsMTLS *pcap.MutualTLS `yaml:"agents_mtls" validate:"omitempty"` + AgentsMTLS *pcap.ClientTLS `yaml:"agents_mtls" validate:"omitempty"` ConcurrentCaptures uint `yaml:"concurrent_captures"` DrainTimeout time.Duration `yaml:"drain_timeout"` diff --git a/src/pcap/cmd/pcap-api/config_test.go b/src/pcap/cmd/pcap-api/config_test.go index 016d5628..b1999784 100644 --- a/src/pcap/cmd/pcap-api/config_test.go +++ b/src/pcap/cmd/pcap-api/config_test.go @@ -22,10 +22,10 @@ func TestAPIConfig(t *testing.T) { NodeConfig: pcap.NodeConfig{ Listen: pcap.Listen{ Port: 8080, - TLS: &pcap.TLS{ - Certificate: "api-cert.pem", - PrivateKey: "api-cert.key", - CertificateAuthority: "pcap-ca.pem", + TLS: &pcap.ServerTLS{ + Certificate: "api-cert.pem", + PrivateKey: "api-cert.key", + ClientCas: "pcap-ca.pem", }, }, Buffer: pcap.BufferConf{ @@ -36,14 +36,12 @@ func TestAPIConfig(t *testing.T) { LogLevel: "debug", ID: "pcap-api/234", }, - AgentsMTLS: &pcap.MutualTLS{ - TLS: pcap.TLS{ - Certificate: "api-client-cert.pem", - PrivateKey: "api-client-cert.key", - CertificateAuthority: "pcap-ca.pem", - }, - SkipVerify: false, - CommonName: "pcap-agent.service.cf.internal", + AgentsMTLS: &pcap.ClientTLS{ + Certificate: "api-client-cert.pem", + PrivateKey: "api-client-cert.key", + RootCas: "pcap-ca.pem", + SkipVerify: false, + ServerName: "pcap-agent.service.cf.internal", }, ConcurrentCaptures: 5, DrainTimeout: time.Second * 10, @@ -51,14 +49,10 @@ func TestAPIConfig(t *testing.T) { RawDirectorURL: "https://bosh.service.cf.internal:8080", AgentPort: 9494, TokenScope: "bosh.admin", - MTLS: &pcap.MutualTLS{ - TLS: pcap.TLS{ - Certificate: "bosh-client-cert.pem", - PrivateKey: "bosh-client-key.key", - CertificateAuthority: "bosh-ca.pem", - }, + TLS: &pcap.ClientTLS{ + RootCas: "bosh-ca.pem", SkipVerify: false, - CommonName: "bosh.service.cf.internal", + ServerName: "bosh.service.cf.internal", }, }, } diff --git a/src/pcap/cmd/pcap-api/main.go b/src/pcap/cmd/pcap-api/main.go index d95e3434..84892267 100644 --- a/src/pcap/cmd/pcap-api/main.go +++ b/src/pcap/cmd/pcap-api/main.go @@ -15,8 +15,11 @@ import ( "go.uber.org/zap" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" ) +//nolint:funlen // the function is good readable func main() { log := zap.L() log.Info("init phase done, starting api") @@ -78,11 +81,19 @@ func main() { return } - tlsCredentials, err := config.TLSCredentials() - if err != nil { - log.Error("unable to load provided TLS credentials", zap.Error(err)) - return + var tlsCredentials credentials.TransportCredentials + if config.NodeConfig.Listen.TLS != nil { + tlsConfig, tlsConfigErr := config.NodeConfig.Listen.TLS.Config() + if tlsConfigErr != nil { + log.Error("unable to load provided TLS credentials", zap.Error(tlsConfigErr)) + return + } + + tlsCredentials = credentials.NewTLS(tlsConfig) + } else { + tlsCredentials = insecure.NewCredentials() } + server := grpc.NewServer(grpc.Creds(tlsCredentials)) pcap.RegisterAPIServer(server, api) diff --git a/src/pcap/config.go b/src/pcap/config.go index 28b44dbf..01e28f74 100644 --- a/src/pcap/config.go +++ b/src/pcap/config.go @@ -7,32 +7,119 @@ import ( "fmt" "os" "regexp" - - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" ) -// TLS configures the server side of (m)TLS. -type TLS struct { - // Certificate holds the path to the PEM encoded certificate (chain). - Certificate string `yaml:"certificate" validate:"file"` - // PrivateKey holds the path to the PEM encoded private key. - PrivateKey string `yaml:"private_key" validate:"file"` - // CertificateAuthority holds the path to the PEM encoded CA bundle which is used - // to request and verify client certificates. - CertificateAuthority string `yaml:"ca" validate:"file"` +// newTLSConfig is used to set common defaults on newly created TLS +// configurations. +func newTLSConfig() *tls.Config { + return &tls.Config{ + MinVersion: tls.VersionTLS12, + MaxVersion: tls.VersionTLS13, + } +} + +type ServerTLS struct { + // Certificate holds the path to the PEM encoded certificate (chain) that + // is presented by the client / server to its peer. + Certificate string `yaml:"certificate" validate:"omitempty,file"` + // PrivateKey is the private key matching the certificate. + PrivateKey string `yaml:"private_key" validate:"omitempty,file"` + ClientCas string `yaml:"client_cas"` + // Verify controls how the peer certificate is verified: + // + // 0: tls.NoClientCert + // + // 1: tls.RequestClientCert + // + // 2: tls.RequireAnyClientCert + // + // 3: tls.VerifyClientCertIfGiven + // + // 4: tls.RequireAndVerifyClientCert + Verify tls.ClientAuthType `yaml:"verify"` } -// MutualTLS defines the client-side configuration for an mTLS connection. -// -// Certificate and PrivateKey are for the mTLS client certificate -// The CertificateAuthority is a file containing the server's CA that should be trusted when connecting to the server. -type MutualTLS struct { - TLS `yaml:"-,inline"` - // SkipVerify can disable the server certificate verification when connecting. +func (c *ServerTLS) Config() (*tls.Config, error) { + if c == nil { + return nil, fmt.Errorf("server TLS config must be non-nil") + } + + tlsConf := newTLSConfig() + + cert, err := tls.LoadX509KeyPair(c.Certificate, c.PrivateKey) + if err != nil { + return nil, fmt.Errorf("load x509 key pair: %w", err) + } + tlsConf.Certificates = []tls.Certificate{cert} + + if c.ClientCas == "" && tlsConf.ClientAuth > 0 { + return nil, fmt.Errorf("tls config: configuered client certificate authentication without client CA list") + } + + if c.ClientCas == "" { + // no mTLS + return tlsConf, nil + } + + // configure mTLS + tlsConf.ClientAuth = c.Verify + + trustedCas, err := createCAPool(c.ClientCas) + if err != nil { + return nil, fmt.Errorf("create CA pool: %w", err) + } + tlsConf.ClientCAs = trustedCas + + return tlsConf, nil +} + +type ClientTLS struct { + // Certificate holds the path to the PEM encoded certificate (chain) that + // is presented by the client / server to its peer. + Certificate string `yaml:"certificate" validate:"omitempty,file"` + // PrivateKey is the private key matching the certificate. + PrivateKey string `yaml:"private_key" validate:"omitempty,file"` + // RootCas holds the path to the PEM encoded CA bundle which + // is used to validate the certificate presented by the server if acting as + // the client. + RootCas string `yaml:"ca" validate:"omitempty,file"` + // SkipVerify can be set to disable verification of the peer certificate if + // acting as the client. SkipVerify bool `yaml:"skip_verify"` - // CommonName is used as part of the certificate verification, together with CertificateAuthority. - CommonName string `yaml:"common_name"` + // ServerName that the certificate presented by the server must be signed + // for. + ServerName string `yaml:"server_name"` +} + +func (c *ClientTLS) Config() (*tls.Config, error) { + tlsConf := newTLSConfig() + if c == nil { + return tlsConf, nil + } + + if c.Certificate != "" || c.PrivateKey != "" { + cert, err := tls.LoadX509KeyPair(c.Certificate, c.PrivateKey) + if err != nil { + return nil, fmt.Errorf("load x509 key pair: %w", err) + } + tlsConf.Certificates = []tls.Certificate{cert} + } + + tlsConf.InsecureSkipVerify = c.SkipVerify + + if c.RootCas != "" { + trustedCas, err := createCAPool(c.RootCas) + if err != nil { + return nil, fmt.Errorf("create CA pool: %w", err) + } + tlsConf.RootCAs = trustedCas + } + + if c.ServerName != "" { + tlsConf.ServerName = c.ServerName + } + + return tlsConf, nil } // BufferConf allows to specify the behaviour of buffers. @@ -58,8 +145,8 @@ type BufferConf struct { // Listen defines the port and optional TLS configuration for the listening socket. type Listen struct { - Port int `yaml:"port" validate:"gt=0,lte=65535"` - TLS *TLS `yaml:"tls,omitempty"` + Port int `yaml:"port" validate:"gt=0,lte=65535"` + TLS *ServerTLS `yaml:"tls,omitempty"` } type NodeConfig struct { @@ -69,57 +156,6 @@ type NodeConfig struct { ID string `yaml:"id" validate:"required"` } -// TLSCredentials creates the necessary credentials from this Config. If NodeConfig.Listen.TLS is -// nil, credentials which disable transport security, will be used. -// -// Note: the TLS version is currently hard-coded to TLSv1.3. -func (c NodeConfig) TLSCredentials() (credentials.TransportCredentials, error) { - if tlsConfig := c.Listen.TLS; tlsConfig != nil { - return LoadTLSCredentials(tlsConfig.Certificate, tlsConfig.PrivateKey, &tlsConfig.CertificateAuthority, nil, nil) - } - return insecure.NewCredentials(), nil -} - -// LoadTLSCredentials creates TLS transport credentials from the given parameters. -func LoadTLSCredentials(certFile, keyFile string, caFile *string, peerCAFile *string, peerCommonName *string) (credentials.TransportCredentials, error) { - tlsConf := &tls.Config{ - MinVersion: tls.VersionTLS12, - MaxVersion: tls.VersionTLS13, - ClientAuth: tls.RequireAndVerifyClientCert, - } - - if certFile != "" && keyFile != "" { - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return nil, fmt.Errorf("load client certificate or private key failed: %w", err) - } - tlsConf.Certificates = []tls.Certificate{cert} - } - - if caFile != nil { - caPool, err := createCAPool(*caFile) - if err != nil { - return nil, fmt.Errorf("load certificate authority file failed: %w", err) - } - tlsConf.ClientCAs = caPool - tlsConf.ClientAuth = tls.RequireAndVerifyClientCert - } - - if peerCAFile != nil { - caPool, err := createCAPool(*peerCAFile) - if err != nil { - return nil, fmt.Errorf("load certificate authority file failed: %w", err) - } - tlsConf.RootCAs = caPool - } - - if peerCommonName != nil { - tlsConf.ServerName = *peerCommonName - } - - return credentials.NewTLS(tlsConf), nil -} - func createCAPool(certificateAuthorityFile string) (*x509.CertPool, error) { caFile, err := os.ReadFile(certificateAuthorityFile) if err != nil { diff --git a/src/pcap/config/agent.example.yml b/src/pcap/config/agent.example.yml index 5730e4f9..f3c84a78 100644 --- a/src/pcap/config/agent.example.yml +++ b/src/pcap/config/agent.example.yml @@ -9,5 +9,5 @@ listen: tls: # omitempty -> nil == tls off certificate: agent-cert.pem private_key: agent-cert.key - ca: pcap-ca.pem + client_cas: pcap-ca.pem diff --git a/src/pcap/config/api.example.yml b/src/pcap/config/api.example.yml index 8b34f7c8..47188c00 100644 --- a/src/pcap/config/api.example.yml +++ b/src/pcap/config/api.example.yml @@ -11,10 +11,10 @@ listen: tls: # omitempty -> nil == tls off certificate: api-cert.pem private_key: api-cert.key - ca: pcap-ca.pem + client_cas: pcap-ca.pem agents_mtls: - common_name: pcap-agent.service.cf.internal + server_name: pcap-agent.service.cf.internal skip_verify: false certificate: api-client-cert.pem @@ -34,10 +34,7 @@ bosh: director_url: https://bosh.service.cf.internal:8080 token_scope: bosh.admin agent_port: 9494 - mtls: - common_name: bosh.service.cf.internal + tls: + server_name: bosh.service.cf.internal skip_verify: false - - certificate: bosh-client-cert.pem - private_key: bosh-client-key.key ca: bosh-ca.pem diff --git a/src/pcap/test/bosh_test.go b/src/pcap/test/bosh_test.go index 736e77a2..6b5d2b06 100644 --- a/src/pcap/test/bosh_test.go +++ b/src/pcap/test/bosh_test.go @@ -284,7 +284,7 @@ func TestAPIRegisterHandler(t *testing.T) { config := pcap.BoshResolverConfig{ RawDirectorURL: boshAPI.URL, - MTLS: nil, + TLS: nil, AgentPort: 8083, } boshResolver, err := pcap.NewBoshResolver(config) diff --git a/src/pcap/test/integration/agent_api_client.go b/src/pcap/test/integration/agent_api_client.go index 2bb425d5..0cfdc400 100644 --- a/src/pcap/test/integration/agent_api_client.go +++ b/src/pcap/test/integration/agent_api_client.go @@ -28,7 +28,7 @@ var port = 9494 var APIPort = 8080 -func createAPIwithLocalResolver(targets []pcap.AgentEndpoint, bufConf pcap.BufferConf, mTLSConfig *pcap.MutualTLS, id string) (pcap.APIClient, *grpc.Server, *pcap.API, net.Addr) { +func createAPIwithLocalResolver(targets []pcap.AgentEndpoint, bufConf pcap.BufferConf, mTLSConfig *pcap.ClientTLS, id string) (pcap.APIClient, *grpc.Server, *pcap.API, net.Addr) { resolver := NewLocalResolver(targets) return createAPI(resolver, bufConf, mTLSConfig, id) } @@ -59,9 +59,8 @@ var _ = Describe("Using LocalResolver", func() { agentServer2, agentTarget2, _ = createAgent(9494, agentID2, nil) targets = append(targets, agentTarget2) - agentTLSConf := &pcap.MutualTLS{SkipVerify: true} apiBuffConf := pcap.BufferConf{Size: 200, UpperLimit: 198, LowerLimit: 180} //nolint:gomnd // Values for a test - apiClient, apiServer, api, _ = createAPIwithLocalResolver(targets, apiBuffConf, agentTLSConf, apiID) + apiClient, apiServer, api, _ = createAPIwithLocalResolver(targets, apiBuffConf, nil, apiID) defaultOptions = &pcap.CaptureOptions{ Device: loopback, @@ -212,9 +211,8 @@ var _ = Describe("Using LocalResolver", func() { agentServer1, agentTarget1, agent1 = createAgent(port, agentID1, nil) targets = append(targets, agentTarget1) - agentTLSConf := &pcap.MutualTLS{SkipVerify: true} apiBuffConf := pcap.BufferConf{Size: 100, UpperLimit: 98, LowerLimit: 90} - apiClient, apiServer, _, _ = createAPIwithLocalResolver(targets, apiBuffConf, agentTLSConf, apiID) + apiClient, apiServer, _, _ = createAPIwithLocalResolver(targets, apiBuffConf, nil, apiID) apiPort++ defaultOptions = &pcap.CaptureOptions{ @@ -256,9 +254,8 @@ var _ = Describe("Using LocalResolver", func() { agentServer2, agentTarget2, _ = createAgent(port, agentID2, nil) targets = append(targets, agentTarget2) - agentTLSConf := &pcap.MutualTLS{SkipVerify: true} apiBuffConf := pcap.BufferConf{Size: 7, UpperLimit: 6, LowerLimit: 4} - apiClient, apiServer, _, _ = createAPIwithLocalResolver(targets, apiBuffConf, agentTLSConf, apiID) + apiClient, apiServer, _, _ = createAPIwithLocalResolver(targets, apiBuffConf, nil, apiID) apiPort++ defaultOptions = &pcap.CaptureOptions{ @@ -306,13 +303,12 @@ var _ = Describe("Using LocalResolver", func() { agentServer1, target, agent1 = createAgent(port, agentID1, mTLSConfig) targets = append(targets, target) - agentTLSConf := &pcap.MutualTLS{ - SkipVerify: false, - CommonName: agentServerCertCN, - TLS: pcap.TLS{ - Certificate: clientCertFile, - PrivateKey: clientKeyFile, CertificateAuthority: caPath, - }, + agentTLSConf := &pcap.ClientTLS{ + Certificate: clientCertFile, + PrivateKey: clientKeyFile, + RootCas: caPath, + SkipVerify: false, + ServerName: agentServerCertCN, } apiBuffConf := pcap.BufferConf{Size: 100, UpperLimit: 98, LowerLimit: 80} apiClient, apiServer, _, _ = createAPIwithLocalResolver(targets, apiBuffConf, agentTLSConf, agentID1) @@ -379,9 +375,8 @@ var _ = Describe("Using LocalResolver", func() { agentServer1, agentTarget1, agent1 = createAgent(port, agentID1, nil) targets = append(targets, agentTarget1) - agentTLSConf := &pcap.MutualTLS{SkipVerify: true} apiBuffConf := pcap.BufferConf{Size: 100000, UpperLimit: 99000, LowerLimit: 90000} - apiClient, apiServer, api, apiAddr = createAPIwithLocalResolver(targets, apiBuffConf, agentTLSConf, apiID) + apiClient, apiServer, api, apiAddr = createAPIwithLocalResolver(targets, apiBuffConf, nil, apiID) }) diff --git a/src/pcap/test/integration/bosh_resolver.go b/src/pcap/test/integration/bosh_resolver.go index 12987bc4..5b6cdaa5 100644 --- a/src/pcap/test/integration/bosh_resolver.go +++ b/src/pcap/test/integration/bosh_resolver.go @@ -64,7 +64,7 @@ var _ = Describe("Client to API with Bosh Resolver", func() { boshConfig := pcap.BoshResolverConfig{ AgentPort: agentPort, TokenScope: "bosh.admin", - MTLS: nil, + TLS: nil, } var err error @@ -73,11 +73,10 @@ var _ = Describe("Client to API with Bosh Resolver", func() { messageWriter = NewMemoryMessageWriter() - agentTLSConf := &pcap.MutualTLS{SkipVerify: true} apiBuffConf := pcap.BufferConf{Size: 200, UpperLimit: 198, LowerLimit: 180} var apiAddr net.Addr - apiClient, apiServer, api, apiAddr = createAPIwithBoshResolver(boshResolver, apiBuffConf, agentTLSConf, apiID) + apiClient, apiServer, api, apiAddr = createAPIwithBoshResolver(boshResolver, apiBuffConf, nil, apiID) apiURL = mock.MustParseURL(fmt.Sprintf("http://%s", apiAddr.String())) }) @@ -323,7 +322,7 @@ var _ = Describe("Client to API with Bosh Resolver", func() { }) }) -func createAPIwithBoshResolver(resolver *pcap.BoshResolver, bufConf pcap.BufferConf, mTLSConfig *pcap.MutualTLS, id string) (pcap.APIClient, *grpc.Server, *pcap.API, net.Addr) { +func createAPIwithBoshResolver(resolver *pcap.BoshResolver, bufConf pcap.BufferConf, mTLSConfig *pcap.ClientTLS, id string) (pcap.APIClient, *grpc.Server, *pcap.API, net.Addr) { return createAPI(resolver, bufConf, mTLSConfig, id) } diff --git a/src/pcap/test/integration/helpers.go b/src/pcap/test/integration/helpers.go index da1b266b..7f7209cb 100644 --- a/src/pcap/test/integration/helpers.go +++ b/src/pcap/test/integration/helpers.go @@ -6,6 +6,7 @@ import ( "context" "crypto/rand" "crypto/rsa" + "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/pem" @@ -231,7 +232,13 @@ func generateCertAndKey(cert *x509.Certificate, ca *x509.Certificate, issuerKey } func configureServer(certFile string, keyFile string, clientCAFile string) (credentials.TransportCredentials, error) { - return pcap.LoadTLSCredentials(certFile, keyFile, &clientCAFile, nil, nil) + tlsConf, err := (&pcap.ServerTLS{ + Certificate: certFile, + PrivateKey: keyFile, + ClientCas: clientCAFile, + Verify: tls.RequireAndVerifyClientCert, + }).Config() + return credentials.NewTLS(tlsConf), err } func createAgent(port int, id string, tlsCreds credentials.TransportCredentials) (*grpc.Server, pcap.AgentEndpoint, *pcap.Agent) { @@ -268,7 +275,7 @@ func createAgent(port int, id string, tlsCreds credentials.TransportCredentials) return server, target, agent } -func createAPI(resolver pcap.AgentResolver, bufConf pcap.BufferConf, mTLSConfig *pcap.MutualTLS, id string) (pcap.APIClient, *grpc.Server, *pcap.API, net.Addr) { +func createAPI(resolver pcap.AgentResolver, bufConf pcap.BufferConf, mTLSConfig *pcap.ClientTLS, id string) (pcap.APIClient, *grpc.Server, *pcap.API, net.Addr) { var server *grpc.Server api, err := pcap.NewAPI(bufConf, mTLSConfig, id, MaxConcurrentCaptures) Expect(err).NotTo(HaveOccurred())