diff --git a/web/testdata/tls-ca-chain-invalid.pem b/web/testdata/tls-ca-chain-invalid.pem new file mode 100644 index 00000000..fb1a75b9 --- /dev/null +++ b/web/testdata/tls-ca-chain-invalid.pem @@ -0,0 +1,2 @@ +This file is intentionally not a PEM encoded certificate. It stands in for a +truncated, DER encoded or otherwise unusable CA bundle. diff --git a/web/testdata/web_config_auth_clientCAsText_nocerts.bad.yml b/web/testdata/web_config_auth_clientCAsText_nocerts.bad.yml new file mode 100644 index 00000000..b003b94b --- /dev/null +++ b/web/testdata/web_config_auth_clientCAsText_nocerts.bad.yml @@ -0,0 +1,5 @@ +tls_server_config: + cert_file: "server.crt" + key_file: "server.key" + client_auth_type: "RequireAndVerifyClientCert" + client_ca: "this is not a PEM encoded certificate" diff --git a/web/testdata/web_config_auth_clientCAs_nocerts.bad.yml b/web/testdata/web_config_auth_clientCAs_nocerts.bad.yml new file mode 100644 index 00000000..e7b76b7f --- /dev/null +++ b/web/testdata/web_config_auth_clientCAs_nocerts.bad.yml @@ -0,0 +1,5 @@ +tls_server_config: + cert_file: "server.crt" + key_file: "server.key" + client_auth_type: "RequireAndVerifyClientCert" + client_ca_file: "tls-ca-chain-invalid.pem" diff --git a/web/tls_config.go b/web/tls_config.go index b40be6bb..4aed6881 100644 --- a/web/tls_config.go +++ b/web/tls_config.go @@ -258,17 +258,22 @@ func ConfigToTLSConfig(c *TLSConfig) (*tls.Config, error) { cfg.CurvePreferences = cp } + // clientCAsEmpty records that a client CA was configured but held no + // usable certificate. It is reported after the client auth policy has been + // validated, so that a configuration carrying both faults keeps reporting + // the policy one. + clientCAsEmpty := false if c.ClientCAs != "" { clientCAPool := x509.NewCertPool() clientCAFile, err := os.ReadFile(c.ClientCAs) if err != nil { return nil, err } - clientCAPool.AppendCertsFromPEM(clientCAFile) + clientCAsEmpty = !clientCAPool.AppendCertsFromPEM(clientCAFile) cfg.ClientCAs = clientCAPool } else if c.ClientCAsText != "" { clientCAPool := x509.NewCertPool() - clientCAPool.AppendCertsFromPEM([]byte(c.ClientCAsText)) + clientCAsEmpty = !clientCAPool.AppendCertsFromPEM([]byte(c.ClientCAsText)) cfg.ClientCAs = clientCAPool } @@ -296,6 +301,13 @@ func ConfigToTLSConfig(c *TLSConfig) (*tls.Config, error) { return nil, errors.New("client CA's have been configured without a Client Auth Policy") } + if clientCAsEmpty { + if c.ClientCAs != "" { + return nil, fmt.Errorf("no client CA certificates found in client_ca_file (%s)", c.ClientCAs) + } + return nil, errors.New("no client CA certificates found in client_ca") + } + return cfg, nil } diff --git a/web/tls_config_test.go b/web/tls_config_test.go index b8b3a966..6498e14c 100644 --- a/web/tls_config_test.go +++ b/web/tls_config_test.go @@ -52,6 +52,7 @@ var ( "Invalid Cert or CertPath": regexp.MustCompile(`missing one of cert or cert_file`), "Invalid Key or KeyPath": regexp.MustCompile(`missing one of key or key_file`), "ClientCA set without policy": regexp.MustCompile(`client CA's have been configured without a Client Auth Policy`), + "No client CA certificates": regexp.MustCompile(`no client CA certificates found in client_ca`), "Bad password": regexp.MustCompile(`hashedSecret too short to be a bcrypted password`), "Unauthorized": regexp.MustCompile(`Unauthorized`), "Forbidden": regexp.MustCompile(`Forbidden`), @@ -171,6 +172,16 @@ func TestYAMLFiles(t *testing.T) { YAMLConfigPath: "testdata/web_config_auth_clientCAs_invalid.bad.yml", ExpectedError: ErrorMap["No such file"], }, + { + Name: `invalid config yml (client CA file without certificates)`, + YAMLConfigPath: "testdata/web_config_auth_clientCAs_nocerts.bad.yml", + ExpectedError: ErrorMap["No client CA certificates"], + }, + { + Name: `invalid config yml (inline client CA without certificates)`, + YAMLConfigPath: "testdata/web_config_auth_clientCAsText_nocerts.bad.yml", + ExpectedError: ErrorMap["No client CA certificates"], + }, { Name: `invalid config yml (invalid user list)`, YAMLConfigPath: "testdata/web_config_auth_user_list_invalid.bad.yml",