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

pkg/docker: lock auth.json before reading/writing #1506

Closed
wants to merge 1 commit into from

Conversation

vrothberg
Copy link
Member

Read/Write lock each auth.json file before reading/writing
it to prevent race coditions.

Fixes: #1365
Signed-off-by: Valentin Rothberg vrothberg@redhat.com

@mtrmac @giuseppe @rhatdan PTAL

@rhatdan
Copy link
Member

rhatdan commented Mar 24, 2022

Where is the lock for writing?

@vrothberg
Copy link
Member Author

Where is the lock for writing?

Each auth.json file is used as a lock. This way, we don't have to worry about finding a path for a lockfile for (root)less on Mac, Win, and Linux.

@vrothberg
Copy link
Member Author

Doesn't seem to work yet as I want to though.

@rhatdan
Copy link
Member

rhatdan commented Mar 24, 2022

Not what I meant. I would think there is a code path when writing the auth.json file that should take the lock, but this PR only handles the read paths. But I am not overly familiar with this code.

@vrothberg
Copy link
Member Author

Not what I meant. I would think there is a code path when writing the auth.json file that should take the lock, but this PR only handles the read paths. But I am not overly familiar with this code.

The writing part is buried at the bottom: https://github.com/containers/image/pull/1506/files#diff-893e8c3da19873ab36cfae5eb73229819aed89b5115aca90e1aa7960ab661454R616-R624

@rhatdan
Copy link
Member

rhatdan commented Mar 24, 2022

Ok I missed the change in function name.

LGTM

@vrothberg vrothberg force-pushed the fix-1365 branch 2 times, most recently from 604dc1c to be483ce Compare March 24, 2022 13:55
@vrothberg
Copy link
Member Author

Here we go ... the CI jobs are back

Copy link
Collaborator

@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.

Thanks! Using c/storage/pkg/lockfile, when we already depend on it, is a great idea.

pkg/docker/config/config.go Outdated Show resolved Hide resolved
}

// Make sure to read-lock the file before reading it.
lock, err := lockfile.GetLockfile(path)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Isn’t the path parameter a path of the lock? Which contains a random value to support .Modified? How can auth.json be a JSON and a random string at the same time?

Copy link
Member Author

Choose a reason for hiding this comment

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

Touch() and Modified() don't work in this scenario but I think that's OK in this scenario since we're controlling it here.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, I have not been reading that carefully enough; the contents are not actually used if we call neither Touch() nor Modified().

Still, that feels like an implementation detail that could change any time. I suppose our tests would catch it…

@@ -552,6 +575,14 @@ func readJSONFile(path string, legacyFormat bool) (dockerConfigFile, error) {
return dockerConfigFile{}, err
}

// When creating a file for the first time, it may be empty at this
// point as it was created by the write lock. Hence, return early here
Copy link
Collaborator

Choose a reason for hiding this comment

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

That’s surprising to me (and incompatible with older versions of consumers, though that’s a small concern — they could easily race with WriteFile and see an empty file, and fail, for that reason).

See elsewhere about sharing the lock with JSON.

Copy link
Member Author

@vrothberg vrothberg Mar 25, 2022

Choose a reason for hiding this comment

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

I think it's a valid concern but I do not have a good/definitive answer/idea. We could use a central one (similar to the short-names lock) but old client would still compete with newer ones.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I was thinking about using a separate auth.json.lock file; then the concerns about the lock file object trying to own file contents would go away, and so would this situation, AFAICS.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The underlying fcntl lock is unlocked when any file descriptor to that file is closed, isn’t it?

If so, ioutil.ReadFile of the JSON always releases the lock, making the modifyJSON lock ineffective — and we do need to use a different path.

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 was thinking about using a separate auth.json.lock file; then the concerns about the lock file object trying to own file contents would go away, and so would this situation, AFAICS.

I was considering that but didn't like the user experience. I would find it surprising/annoying if I do a --authfile file and would find a file.lock afterwards.

The underlying fcntl lock is unlocked when any file descriptor to that file is closed, isn’t it?

Holy frog, that is true and buried in the man pages. That indeed is a K.O. for this approach - we could do ref-counting on the path but that seems a bit too hacky for my taste.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Just to be explicit,pkg/lockfile is already using a global hash table and refcounting the objects to work correctly with those locks, so that works for exclusively-used lockfiles (and as long as c/storage doesn’t get a separate /v2 with a separate hash table); but it breaks if there is any other user of that file, like here.

Copy link
Collaborator

Choose a reason for hiding this comment

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

… the point about file.lock files on user-specified --authfile paths (outside of dot files, in user-maintained project repositories and the like) being annoying is quite valid, though; we might not even necessarily be able to create a lockfile (if the auth.json is in a read-only configuration directory).

flock(2) might be an option, but it is distinctly less portable (and the Linux man pages suggest that some implementations implement it on top of the fcntl locks. I’m sure there are good write-ups about the various locking mechanisms and their trade-offs, I haven’t now taken the time to find and read one.

Copy link
Collaborator

Choose a reason for hiding this comment

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

A possible partial alternative: use c/storage/pkg/ioutils.AtomicWriteFile. That could still lose data with concurrent writers, but at least it wouldn’t break readers concurrent with a single writer, who can now see empty files or partial contents (= invalid JSON).

Copy link
Member Author

Choose a reason for hiding this comment

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

Seems like we're between a rock and hard place. I will close this PR as I ran out of time and really do not have a good idea yet.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Let’s at least do the AtomicWriteFile part, that should be safe and make some difference: #1515 .

Read/Write lock each auth.json file before reading/writing
it to prevent race coditions.

Fixes: containers#1365
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
@vrothberg
Copy link
Member Author

@mtrmac updated the name and comment. Let's continue the conversation on the remaining locking issue in the threads.

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

Successfully merging this pull request may close these issues.

Multiple simultaneous podman login commands can lose entries
3 participants