{"record":{"id":"8304ddfb2698389d","repo":"JuliusBrussee/caveman","slug":"postgres-read-s-w","errorCode":null,"errorMessage":"postgres: read %s: %w","messagePattern":"postgres: read (.+?): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/postgresconfig/postgresconfig.go","lineNumber":97,"sourceCode":"func caPEMFromEnvironment() (string, error) {\n\tfilePath := strings.TrimSpace(os.Getenv(caFileEnvironment))\n\tdirect := strings.TrimSpace(os.Getenv(caEnvironment))\n\tif filePath != \"\" && direct != \"\" {\n\t\treturn \"\", fmt.Errorf(\"postgres: set only %s or %s, not both\", caFileEnvironment, caEnvironment)\n\t}\n\tif filePath == \"\" {\n\t\treturn direct, nil\n\t}\n\tinfo, err := os.Stat(filePath)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"postgres: %s: %w\", caFileEnvironment, err)\n\t}\n\tif !info.Mode().IsRegular() {\n\t\treturn \"\", fmt.Errorf(\"postgres: %s must point to a regular file\", caFileEnvironment)\n\t}\n\tcontents, err := os.ReadFile(filePath)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"postgres: read %s: %w\", caFileEnvironment, err)\n\t}\n\treturn strings.TrimSpace(string(contents)), nil\n}\n\n// NewPool constructs a pool from the hardened configuration.\nfunc NewPool(ctx context.Context, databaseURL string) (*pgxpool.Pool, error) {\n\tconfig, err := ParsePoolConfig(databaseURL)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn pgxpool.NewWithConfig(ctx, config)\n}\n\n// NewRuntimePool constructs a pool and proves the connected login is a\n// least-privilege member of expectedRole. Runtime services must never connect\n// as a superuser, BYPASSRLS role, or owner of a tenant table; this remains true\n// for local, on-prem, staging, and production deployments alike.\nfunc NewRuntimePool(ctx context.Context, databaseURL, expectedRole string) (*pgxpool.Pool, error) {","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/postgresconfig/postgresconfig.go#L79-L115","documentation":"os.ReadFile on the validated CA file path failed (e.g. permission denied, I/O error, file removed between stat and read). The error is wrapped with 'postgres: read <ENVVAR>:' so the failing variable is identifiable.","triggerScenarios":"ParsePoolConfig/NewPool with the CA-file env var set; file exists and is regular but cannot be read by the process (permissions changed, file deleted in a race, filesystem error).","commonSituations":"Secret mounted as root-only while the app runs as non-root; NFS/overlay filesystem errors; Kubernetes secret volume not yet mounted at read time.","solutions":["Fix file permissions so the process user can read the CA file (chmod/chown or mount with correct mode).","Ensure the secret/volume is mounted before the application starts.","Retest the exact read with `sudo -u <appuser> cat \"$POSTGRES_CA_FILE\"`.","If the CA is available inline, use the direct PEM env var instead of the file path."],"exampleFix":"// before\n// k8s secret volume defaultMode: 0400 owned by root, app runs as uid 1000\n// after\n// defaultMode: 0440 with fsGroup: 1000 (or chmod o+r the file)","handlingStrategy":"validation","validationCode":"func canReadCA(path string) error {\n\tf, err := os.Open(path)\n\tif err != nil { return err }\n\tdefer f.Close()\n\t_, err = f.Read(make([]byte, 1))\n\treturn err\n}","typeGuard":null,"tryCatchPattern":"if err := canReadCA(os.Getenv(\"POSTGRES_CA_FILE\")); err != nil {\n\tlog.Fatalf(\"CA file unreadable: %v\", err)\n}","preventionTips":["Match secret mount permissions to the app's run-as UID/GID (k8s defaultMode/fsGroup).","Avoid opening the CA from network filesystems in latency/error-prone setups.","Read the CA in the entrypoint to fail fast with a clear message."],"tags":["postgres","tls","file","permissions"],"backgroundTag":"file-read-failed","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}