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

Do not fail on $HOME not containing .config directory #2147

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

rhatdan
Copy link
Member

@rhatdan rhatdan commented Sep 3, 2024

Copy link
Contributor

openshift-ci bot commented Sep 3, 2024

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: rhatdan

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci bot added the approved label Sep 3, 2024
@rhatdan
Copy link
Member Author

rhatdan commented Sep 3, 2024

@Luap99 PTAL

@rhatdan rhatdan changed the title Do not fail on /home/dwalsh not containing .config directory Do not fail on $HOME not containing .config directory Sep 3, 2024
// NOTE: For now we want Windows to use system locations.
// GetRootlessUID == -1 on Windows, so exclude negative range
if unshare.GetRootlessUID() > 0 {
configHome, err := homedir.GetConfigHome()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see how this fixes containers/podman#23818
Without an extensive code analysis I would assume there are more code paths calling into this

I would think the proper fix is to make homedir.GetConfigHome() not error on ENOENT IMO

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree, Callers might want to make different decisions on no ConfigHome and having to look at "", nil versus "", ENOENT allows them to fail if they expect to have a ConfigHome.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should fix the specific error, and seems like a good change in general, since it is expected that the SigPath does not exist in the users HomeDir is common.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would a caller expect this this path to exists? For pretty much all other tools this path is used to read option config files. I do not even understand why thins function tries to create the given path.
IMO this should return the path,nil even if the underlying path does not actually exists.

If you call something like os.UserHomeDir() you also juts the the path but no guarantee that it exists.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok you could change the function, but this would be potentially breaking change for callers of the function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without an extensive code analysis I would assume there are more code paths calling into this

There are several, incl. things like pkg/machine/env.GetConfDir which uses the return value to maintain state. We can’t just silently ignore failures without inventing some other place where to store data. And if we were to invent some other place, in a situation where ~/.config both doesn’t exist and can’t be created … I can’t imagine what that would be.

We must fail in this situation, ultimately.


Callers might want to make different decisions on no ConfigHome and having to look at "", nil versus "", ENOENT allows them to fail if they expect to have a ConfigHome.

(I can’t parse this, but, anyway):

Please make a table of:

  • on-filesystem situation
  • environment setting
  • expected behavior

After we have the expected behavior, we can discuss what the API should look like.

I generally think that it is undesirable for callers to make any kinds of individual decisions based on the situation. All callers who wanted “$XDG_CONFIG_HOME or a fallback” should get exactly the same “$XDG_CONFIG_HOME or a fallback” and generally behave the same in the fallback situation. It seems to me, as a first approximation, undesirable to expose the details of the fallback to the callers; instead, the fallback logic should all be inside GetConfigHome.

[Or, to put it another way, if some callers don’t want the standard $XDG_CONFIG_HOME behavior, they shouldn’t be using the $XDG_CONFIG_HOME variable and name at all; they should define some other variable and name for that custom behavior.]


… personally, I suggest that we don’t want all of this complexity, and “each Linux user must have a modifiable $ConfigHome” is a reasonable thing which we just assume, and fail otherwise.

Especially consider the case where GetConfigHome is intentionally returning an error, where $XDG_CONFIG_HOME is set to a value but that value is not writeable by the current user (typically using su without --login). That’s an unreasonable, potentially dangerous, setup, and we should not be triggering any fallback behavior; we should fail at the first opportunity.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

containers/podman#23818 (comment)

Especially consider the case where GetConfigHome is intentionally returning an error, where $XDG_CONFIG_HOME is set to a value but that value is not writeable by the current user (typically using su without --login). That’s an unreasonable, potentially dangerous, setup, and we should not be triggering any fallback behavior; we should fail at the first opportunity.

Well yeah but do we have to fail when the files are never needed in the first place? I would assume code fails anyway if we get EACCESS when reading the config files as we only ignore ENOENT AFAIK.

The doc comments on GetConfigHome() make no such claim that they check for the dir existence, in fact when XDG_CONFIG_HOME is set it doesn't try to create/stat it there so your described scenario isn't failing there.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added containers/storage#2084 to handle XDG_CONFIG_HOME versus $HOME/.config

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well yeah but do we have to fail when the files are never needed in the first place?

I think that’s an very reasonable point when looking for general config files.

When looking for security-relevant configuration like policy.json, it gets… complicated, because it’s possible the user caused this situation by a mistaken chmod, and falling back to the system-wide config might be a much more permissive policy and not be what the user wanted. Now, to an extent, that’s just inherent in the “check if a per-user config exists, otherwise fall back” idea — we can protect the user against some mistakes but we can’t protect against e.g. a typo in the config file name.

return sigPath, nil
}
} else {
if !errors.Is(err, unix.ENOENT) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should use fs.ErrNotExist

Copy link
Contributor

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICS:

  • This is not the right fix for the motivating issue. That is really a Podman dependency aspect
  • Even unrelated from the motivating issue, this is not at all sufficient to handle GetConfigHome failures throughout Podman, and I don’t know what that would look like.

Comment on lines 850 to 874
It("HomeDirTest", func() {
oldHOMEDIR, set := os.LookupEnv("HOME")
dir, err := os.MkdirTemp("", "configTest")
gomega.Expect(err).ToNot(gomega.HaveOccurred())
defer os.RemoveAll(dir)

os.Setenv("HOME", dir)
_, err = defaultConfig()
gomega.Expect(err).ToNot(gomega.HaveOccurred())

if set {
os.Setenv("HOME", oldHOMEDIR)
} else {
os.Unsetenv("HOME")
}
})

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is completely unrepresentative of containers/podman#23818 .

Ordinarily, if $HOME is an empty but writable directory, GetConfigHome transparently creates ~/.config, and nothing fails.

The reported failure is that .config does not exist and the user does not have the permissions to create it. In that case GetConfigHome fails.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. Will work to fix.

@@ -188,6 +189,25 @@ const (
DefaultVolumePluginTimeout = 5
)

func defaultSigPath() (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this should exist at all:

Suggested change
func defaultSigPath() (string, error) {
// defaultSigPath is an internal implementation detail of defaultConfig. Do not add any other callers.
func defaultSigPath() (string, error) {

Copy link
Contributor

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.

// NOTE: For now we want Windows to use system locations.
// GetRootlessUID == -1 on Windows, so exclude negative range
if unshare.GetRootlessUID() > 0 {
configHome, err := homedir.GetConfigHome()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without an extensive code analysis I would assume there are more code paths calling into this

There are several, incl. things like pkg/machine/env.GetConfDir which uses the return value to maintain state. We can’t just silently ignore failures without inventing some other place where to store data. And if we were to invent some other place, in a situation where ~/.config both doesn’t exist and can’t be created … I can’t imagine what that would be.

We must fail in this situation, ultimately.


Callers might want to make different decisions on no ConfigHome and having to look at "", nil versus "", ENOENT allows them to fail if they expect to have a ConfigHome.

(I can’t parse this, but, anyway):

Please make a table of:

  • on-filesystem situation
  • environment setting
  • expected behavior

After we have the expected behavior, we can discuss what the API should look like.

I generally think that it is undesirable for callers to make any kinds of individual decisions based on the situation. All callers who wanted “$XDG_CONFIG_HOME or a fallback” should get exactly the same “$XDG_CONFIG_HOME or a fallback” and generally behave the same in the fallback situation. It seems to me, as a first approximation, undesirable to expose the details of the fallback to the callers; instead, the fallback logic should all be inside GetConfigHome.

[Or, to put it another way, if some callers don’t want the standard $XDG_CONFIG_HOME behavior, they shouldn’t be using the $XDG_CONFIG_HOME variable and name at all; they should define some other variable and name for that custom behavior.]


… personally, I suggest that we don’t want all of this complexity, and “each Linux user must have a modifiable $ConfigHome” is a reasonable thing which we just assume, and fail otherwise.

Especially consider the case where GetConfigHome is intentionally returning an error, where $XDG_CONFIG_HOME is set to a value but that value is not writeable by the current user (typically using su without --login). That’s an unreasonable, potentially dangerous, setup, and we should not be triggering any fallback behavior; we should fail at the first opportunity.

}
}
}
return DefaultSignaturePolicyPath, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s, also… a whole another discussion topic in this problem space: In general, c/image callers should not have their own default path logic at all because they are all going to diverge over time.

Compare containers/image#1726 .

All of this code really should not exist in the first place.

… but, a reminder, removing this code would not really be a fix for containers/podman#23818 .


… now, c/image does look for a policy.json in the home directory, but it currently uses a hard-coded .config/containers/policy.json and it does not interpret $XDG_CONFIG_HOME ( containers/image#2297 ). So just removing this code right now could break things.

@rhatdan rhatdan force-pushed the config branch 2 times, most recently from a90e70e to 7d96c3f Compare September 4, 2024 10:48
Copy link

Ephemeral COPR build failed. @containers/packit-build please check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Importing Go bindings calls XDG runtime checks, causing application to exit unexpectedly
3 participants