{"record":{"id":"d19ffc9848e361ed","repo":"cloudflare/cloudflared","slug":"read-ca-certificate-s-w","errorCode":null,"errorMessage":"read CA certificate %s: %w","messagePattern":"read CA certificate (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tlsconfig/origin_ca.go","lineNumber":81,"sourceCode":"\n\t// nolint: gosec\n\tcustomOriginCA, err := os.ReadFile(originCAFilename)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, fmt.Sprintf(\"unable to read the file %s\", originCAFilename))\n\t}\n\n\tif !certPool.AppendCertsFromPEM(customOriginCA) {\n\t\treturn nil, fmt.Errorf(\"error appending custom CA to cert pool\")\n\t}\n\treturn certPool, nil\n}\n\nfunc CreateTunnelConfig(caCert string, serverName string) (*tls.Config, error) {\n\ttlsConfig := &tls.Config{ServerName: serverName}\n\tif caCert != \"\" {\n\t\tcaCertPEM, err := os.ReadFile(caCert) //nolint:gosec\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"read CA certificate %s: %w\", caCert, err)\n\t\t}\n\n\t\trootCAPool := x509.NewCertPool()\n\t\tif !rootCAPool.AppendCertsFromPEM(caCertPEM) {\n\t\t\treturn nil, fmt.Errorf(\"parse CA certificate %s\", caCert)\n\t\t}\n\t\ttlsConfig.RootCAs = rootCAPool\n\t}\n\n\tif tlsConfig.RootCAs == nil {\n\t\trootCAPool, err := x509.SystemCertPool()\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrap(err, \"unable to get x509 system cert pool\")\n\t\t}\n\t\tcfRootCA, err := GetCloudflareRootCA()\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrap(err, \"could not append Cloudflare Root CAs to cloudflared certificate pool\")\n\t\t}","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/tlsconfig/origin_ca.go#L63-L99","documentation":"CreateTunnelConfig in tlsconfig builds the *tls.Config used to verify the edge/origin server. When a CA certificate path is supplied (caCert != \"\"), it reads that file from disk with os.ReadFile; if the read fails (missing file, bad permissions, is a directory), it wraps the OS error as \"read CA certificate <path>: <underlying os error>\" and aborts config creation. The wrapped error (from errors.Unwrap / %w) always carries the exact OS reason.","triggerScenarios":"Calling CreateTunnelConfig (directly or via prepareTunnelConfig/probeTLSConfig) with a non-empty caCert path that cannot be read: the file does not exist, the path points to a directory, the process lacks read permission, or the path is misspelled/relative to the wrong working directory.","commonSituations":"Users pass --origin-ca-pool or equivalent with a typo'd or relative path; the cert file was deleted/moved after config was written; running cloudflared in a container where the CA file was not volume-mounted; running as a non-root service account that cannot read the file; SELinux/AppArmor denies read access.","solutions":["Verify the file exists and is readable: run `ls -l <path>` and `cat <path> > /dev/null` as the same user that runs cloudflared.","Use an absolute path in the configuration/flag instead of a path relative to the current working directory of the daemon.","Fix filesystem permissions (chown/chmod) or mount the CA file into the container/pod if running containerized.","If the file is genuinely absent, obtain the correct CA bundle (e.g. Cloudflare origin CA or system bundle) and place it at the configured path, or pass an empty caCert to fall back to the system pool plus Cloudflare roots."],"exampleFix":"// before\ntlsCfg, err := tlsconfig.CreateTunnelConfig(\"certs/origin-ca.pem\", \"example.com\")\n// after — check the path up front\nconst caPath = \"/etc/cloudflared/origin-ca.pem\"\nif _, err := os.Stat(caPath); err != nil {\n\tlog.Fatal().Err(err).Msgf(\"CA certificate not readable at %s\", caPath)\n}\ntlsCfg, err := tlsconfig.CreateTunnelConfig(caPath, \"example.com\")","handlingStrategy":"validation","validationCode":"func caCertReadable(path string) error {\n\tif path == \"\" {\n\t\treturn nil\n\t}\n\tfi, err := os.Stat(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"CA cert %s: %w\", path, err)\n\t}\n\tif fi.IsDir() {\n\t\treturn fmt.Errorf(\"CA cert %s is a directory\", path)\n\t}\n\tf, err := os.Open(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"CA cert %s not readable: %w\", path, err)\n\t}\n\treturn f.Close()\n}","typeGuard":"func caCertPathSet(cfg struct{ CACert string }) bool { return cfg.CACert != \"\" }","tryCatchPattern":"tlsCfg, err := tlsconfig.CreateTunnelConfig(caPath, serverName)\nif err != nil {\n\tvar perr *fs.PathError\n\tif errors.As(err, &perr) {\n\t\tlog.Fatal().Err(perr).Msgf(\"cannot read CA cert %s\", perr.Path)\n\t}\n\tlog.Fatal().Err(err).Msg(\"failed to build tunnel TLS config\")\n}","preventionTips":["Always use absolute paths for cert files in daemon configs.","Check file readability as the service user during startup/health checks.","Mount CA files explicitly into containers and verify at image build time.","Keep the underlying error (%w) in logs to distinguish ENOENT vs EACCES."],"tags":["tls","certificate","file-io","configuration"],"backgroundTag":"file-read-failed","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}