Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

auth: ENOTDIR not handled when user's HOME is not a directory #10696

Closed
dfinkel opened this issue Aug 16, 2024 · 5 comments · Fixed by #10697
Closed

auth: ENOTDIR not handled when user's HOME is not a directory #10696

dfinkel opened this issue Aug 16, 2024 · 5 comments · Fixed by #10697
Assignees
Labels
triage me I really want to be triaged.

Comments

@dfinkel
Copy link
Contributor

dfinkel commented Aug 16, 2024

Client

trace, but really the auth package.

Environment

Alpine Docker on GKE when running as the guest user which has a home directory set to /dev/null.

Code and Dependencies

Mostly irrelevant, but the code that initializes the opencensus exporter/client is:

import (
	"fmt"

	"cloud.google.com/go/compute/metadata"
	"contrib.go.opencensus.io/exporter/stackdriver"
	"go.opencensus.io/trace"
)

// SetupTracingExporter adds stackdriver to the list of exporters that will receive
// traces. defaultProjID is the GCP project ID that should be used to identify
// the Stackdriver project. defaultTraceAttributes will be appended to every
// span that is exported to Stackdriver Trace
func SetupTracingExporter(defaultProjID string, defaultTraceAttributes map[string]interface{}) (*stackdriver.Exporter, error) {
	sd, err := stackdriver.NewExporter(stackdriver.Options{
		ProjectID:              defaultedGCPProject(defaultProjID),
		DefaultTraceAttributes: defaultTraceAttributes,
	})

	if err != nil {
		return nil, fmt.Errorf("Failed to get stack driver exporter: %w", err)
	}

	trace.RegisterExporter(sd)
	return sd, nil
}

// defaultedGCPProject is a helper function to automatically get the project ID
// when running on GCE. If it's not on GCE, it will default to the default Project
// ID passed to the function
func defaultedGCPProject(defaultProjID string) string {
	if !metadata.OnGCE() {
		return defaultProjID
	}

	projectName, projErr := metadata.ProjectID()
	if projErr != nil {
		return defaultProjID
	}
	return projectName
}

Expected behavior

The trace client (or any other GCP client) initializes without any client certificate. (using the GKE/GCE metadata server oauth credentials)

Actual behavior

Setting up the opencensus stackdriver trace exporter fails with this error:

open /dev/null/.config/gcloud/certificate_config.json: not a directory

It appears that because the user's home directory (as set in /etc/passwd) is explicitly set to /dev/null, and that's not a directory, the explicit path that's constructed here fails with ENOTDIR, which doesn't match os.ErrNotExist, so it falls through and returns ENOTDIR, when this is supposed to be a default code-path. (called from here)

Additional context

We started seeing this following an upgrade to include google.golang.org/api v0.188.0

It appears that #10431 converted a benign fall-through before into a fatal error.

Notably, golang/go#18974 indicates that the Go team doesn't consider it viable to include ENOTDIR in the handling for os.ErrNotExist due to the number of ways that errno can be encountered.

I can send a PR with a fix. (interestingly, ENOTDIR and ENOENT are the only two unix errnos defined for windows (and I'm pretty sure that means all platforms)

cc: @codyoss

@dfinkel dfinkel added the triage me I really want to be triaged. label Aug 16, 2024
dfinkel added a commit to dfinkel/google-cloud-go that referenced this issue Aug 16, 2024
Some users explicitly have their home directories set to `/dev/null`.
When opening a file that's "under that directory", instead of getting
the normal `ENOENT`, one should expect to get `ENOTDIR`.

Unfortunately, due to the variety of cases under which `ENOTDIR` is
returned, the go team has declined to include ENOTDIR in `ErrNotExist`.
(https://golang.org/issues/18974)

This leaves explicitly testing for `ENOTDIR`, which (fortunately)
appears to be one of two errnos that are defined in the `syscall`
package for windows as well as all *nix platforms.

resolves: googleapis#10696
dfinkel added a commit to dfinkel/google-cloud-go that referenced this issue Aug 16, 2024
Some users explicitly have their home directories set to `/dev/null`.
When opening a file that's "under that directory", instead of getting
the normal `ENOENT`, one should expect to get `ENOTDIR`.

Unfortunately, due to the variety of cases under which `ENOTDIR` is
returned, the go team has declined to include ENOTDIR in `ErrNotExist`.
(https://golang.org/issues/18974)

This leaves explicitly testing for `ENOTDIR`, which (fortunately)
appears to be one of two errnos that are defined in the `syscall`
package for windows as well as all *nix platforms.

resolves: googleapis#10696
dfinkel added a commit to dfinkel/google-cloud-go that referenced this issue Aug 16, 2024
Some users explicitly have their home directories set to `/dev/null`.
When opening a file that's "under that directory", instead of getting
the normal `ENOENT`, one should expect to get `ENOTDIR`.

Unfortunately, due to the variety of cases under which `ENOTDIR` is
returned, the go team has declined to include ENOTDIR in `ErrNotExist`.
(https://golang.org/issues/18974)

This leaves explicitly testing for `ENOTDIR`, which (fortunately)
appears to be one of two errnos that are defined in the `syscall`
package for windows as well as all *nix platforms.

Resolves: googleapis#10696
@dfinkel
Copy link
Contributor Author

dfinkel commented Aug 16, 2024

I've opened #10697

@quartzmo
Copy link
Member

@dfinkel Thank you for reporting this. Reviewing your PR now.

@codyoss
Copy link
Member

codyoss commented Aug 21, 2024

I think the correct thing here is to not check for any special errors at all. I have come across this case in the past and discussed it with the Go team. There are legacy syscall reasons why ENOTDIR is not a not exists. Instead we should treat any error as the not exist case here. This was the conclusion reached for https://go-review.git.corp.google.com/c/oauth2/+/493695 as well. Happy to accept a PR for this style of fix.

@dfinkel
Copy link
Contributor Author

dfinkel commented Aug 22, 2024

@codyoss Sure!
I'll rewrite #10697 to return errSourceUnavailable for all errors from os.ReadFile.

dfinkel added a commit to dfinkel/google-cloud-go that referenced this issue Aug 22, 2024
Some users explicitly have their home directories set to `/dev/null`.
When opening a file that's "under that directory", instead of getting
the normal `ENOENT`, one should expect to get `ENOTDIR`.

Unfortunately, due to the variety of cases under which `ENOTDIR` is
returned, the go team has declined to include ENOTDIR in `ErrNotExist`.
(https://golang.org/issues/18974)

The most robust solution in this case (following [1]) is to treat any
read error as `errSourceUnavailable`. This has the mixed-benefit of also
covering EPERM. (hopefully this doesn't make it too hard to track down
permissions problems for secure connect users)

Resolves: googleapis#10696

[1]: https://go-review.googlesource.com/c/oauth2/+/493695
dfinkel added a commit to dfinkel/google-cloud-go that referenced this issue Aug 22, 2024
Some users explicitly have their home directories set to `/dev/null`.
When opening a file that's "under that directory", instead of getting
the normal `ENOENT`, one should expect to get `ENOTDIR`.

Unfortunately, due to the variety of cases under which `ENOTDIR` is
returned, the go team has declined to include ENOTDIR in `ErrNotExist`.
(https://golang.org/issues/18974)

The most robust solution in this case (following [1]) is to treat any
read error as `errSourceUnavailable`. This has the mixed-benefit of also
covering EPERM. (hopefully this doesn't make it too hard to track down
permissions problems for secure connect users)

Resolves: googleapis#10696

[1]: https://go-review.googlesource.com/c/oauth2/+/493695
@dfinkel
Copy link
Contributor Author

dfinkel commented Aug 22, 2024

@codyoss I've updated #10697 to treat all non-nil errors on that that ReadFile call as unavailable. (and added a test that exercises EPERM so it's clear that that error is expected to be swallowed as well)

dfinkel added a commit to dfinkel/google-cloud-go that referenced this issue Aug 22, 2024
Some users explicitly have their home directories set to `/dev/null`.
When opening a file that's "under that directory", instead of getting
the normal `ENOENT`, one should expect to get `ENOTDIR`.

Unfortunately, due to the variety of cases under which `ENOTDIR` is
returned, the go team has declined to include ENOTDIR in `ErrNotExist`.
(https://golang.org/issues/18974)

The most robust solution in this case (following [1]) is to treat any
read error as `errSourceUnavailable`. This has the mixed-benefit of also
covering EPERM. (hopefully this doesn't make it too hard to track down
permissions problems for secure connect users)

Resolves: googleapis#10696

[1]: https://go-review.googlesource.com/c/oauth2/+/493695
dfinkel added a commit to dfinkel/google-cloud-go that referenced this issue Aug 22, 2024
Some users explicitly have their home directories set to `/dev/null`.
When opening a file that's "under that directory", instead of getting
the normal `ENOENT`, one should expect to get `ENOTDIR`.

Unfortunately, due to the variety of cases under which `ENOTDIR` is
returned, the go team has declined to include ENOTDIR in `ErrNotExist`.
(https://golang.org/issues/18974)

The most robust solution in this case (following [1]) is to treat any
read error as `errSourceUnavailable`. This has the mixed-benefit of also
covering EPERM. (hopefully this doesn't make it too hard to track down
permissions problems for secure connect users)

Resolves: googleapis#10696

[1]: https://go-review.googlesource.com/c/oauth2/+/493695
codyoss pushed a commit that referenced this issue Aug 22, 2024
Some users explicitly have their home directories set to /dev/null. When opening a file that's "under that directory", instead of getting the normal ENOENT, one should expect to get ENOTDIR.

Unfortunately, due to the variety of cases under which ENOTDIR is returned, the go team has declined to include ENOTDIR in ErrNotExist. (https://golang.org/issues/18974)

The most robust solution in this case (following 1) is to treat any
read error as errSourceUnavailable. This has the mixed-benefit of also
covering EPERM. (hopefully this doesn't make it too hard to track down
permissions problems for secure connect users)

Resolves: #10696
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
triage me I really want to be triaged.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants