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

Implement AF_UNIX sockets on Windows #115

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

slonopotamus
Copy link

See moby/moby#36442


This is a rebase of #98.

@AkihiroSuda AkihiroSuda requested a review from thaJeztah May 14, 2024 02:05
@AkihiroSuda
Copy link
Collaborator

CI is failing

@slonopotamus slonopotamus force-pushed the AF_UNIX-on-windows branch 4 times, most recently from 2b1d491 to 0de2531 Compare May 14, 2024 07:04
@slonopotamus slonopotamus marked this pull request as draft May 14, 2024 07:09
@thaJeztah
Copy link
Member

thaJeztah commented May 14, 2024

Gave CI a kick after the last push; failure is in TestUnixSocketWithOpts, but also curious if TestNewUnixSocket needs a change in constraints (currently gated by "must be root", which may need a slightly different check on Windows likely)

=== RUN   TestNewUnixSocket
    unix_socket_test.go:39: requires root
--- SKIP: TestNewUnixSocket (0.00s)
=== RUN   TestUnixSocketWithOpts
    unix_socket_test_windows.go:11: The process cannot access the file because it is being used by another process.

Also look like staticcheck is complaining because on Windows it's a stub, which, well, is the intent here 🤔

Screenshot 2024-05-14 at 12 50 35

I guess a //nolint:staticcheck // Ignore SA4017, which is a stub on Windows, and has no side-effects could work

Alternatively, we could extract the whole block and have a separate Windows and Linux implementation / wrapper for l, err := net.Listen("unix", path) (on Windows skipping the umask)

That whole umask is hairy in either case 😬

@slonopotamus
Copy link
Author

I moved PR to draft until i figure out the test failure.

@slonopotamus
Copy link
Author

That whole umask is hairy in either case

Yes, feels like a race condition waiting to be exploited.

@slonopotamus slonopotamus force-pushed the AF_UNIX-on-windows branch 2 times, most recently from dbc098e to 2ef96e3 Compare May 14, 2024 13:52
@slonopotamus
Copy link
Author

Okay, I think I'm done here.

Alternatively, we could extract the whole block and have a separate Windows and Linux implementation / wrapper

Yep, I went this route.

@slonopotamus slonopotamus marked this pull request as ready for review May 14, 2024 13:55
@laurazard
Copy link
Member

cc @neersighted

Copy link
Member

@laurazard laurazard left a comment

Choose a reason for hiding this comment

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

Overall LGTM! Left a little nit about the error string.

sockets/sockets.go Outdated Show resolved Hide resolved
@laurazard
Copy link
Member

@slonopotamus Could you sign-off your commit? The DCO check is failing.

@thaJeztah
Copy link
Member

@slonopotamus Could you sign-off your commit? The DCO check is failing.

Oh! It's the "accept suggestions" option on GitHub. That one is always a pain, because it's too easy to just "click the button", and now having an incorrect commit.

Screenshot 2024-09-25 at 12 11 00

I can do a quick rebase and squash that one into the first commit.

slonopotamus and others added 2 commits September 25, 2024 12:12
See moby/moby 36442

Co-authored-by: Laura Brehm <laurabrehm@hey.com>
Signed-off-by: Marat Radchenko <marat@slonopotamus.org>
Before this change, CI would only trigger on commits to master branch.
When contributor creates a branch that would later become a PR, they do not want to use master branch.
Instead, they want to use feature-branch.
But before this commit, CI would not run in non-master branch at all, so contributor is unable to test their changes before creating a PR to upstream repository.

Signed-off-by: Marat Radchenko <marat@slonopotamus.org>
@thaJeztah
Copy link
Member

I think this PR should be OK to merge, BUT will need some follow-ups. Some quick blurbs;

  • There was discussion around this PR, and whether we should bring it in, knowing that the go-connections package as a whole needs to be re-evaluated; it originated from very far in the history of the Docker "project", not very well designed, and has quite some bad parts.
  • Then again; it's what we currently have, and while we should look at the overall issue, I don't think merging this PR brings us in a worse state.
  • The umask monstrosity is existing code (we should explore if we can find alternatives and/or propose changes in Go's stdlib or golang.org/x/sys), but this PR only moves that code, so no change there
  • AFAICS, no existing code is impacted (only moved), so should have no consequences for existing use.

That said (as mentioned) there's some bits that must be addressed before we tag a new release;

  • First of all, verify this code in codebase(s) that use it; we should probably hold-off tagging a release before that's all verified (there may be ugly corners hiding)
  • ⚠️ given that go-connections is used to (among others) create the /var/run/docker.sock socket, access to that socket is important. Currently, there's code to set permissions, which I think are currently silently ignored on Windows; we must make sure that these functions;
    • have alternatives / equivalents on Windows
    • produces an error for those that cannot be supported as-is on Windows; setting permissions (and ownership) should not be silent

// WithChown modifies the socket file's uid and gid
func WithChown(uid, gid int) SockOption {
return func(path string) error {
if err := os.Chown(path, uid, gid); err != nil {
return err
}
return nil
}
}
// WithChmod modifies socket file's access mode.
func WithChmod(mask os.FileMode) SockOption {
return func(path string) error {
if err := os.Chmod(path, mask); err != nil {
return err
}
return nil
}
}

@laurazard
Copy link
Member

I believe on Windows the for the created socket would follow the rest how filesystem permissions work there, i.e. hierarchical depending on the directory it's in. But maybe someone more knowledgeable would know better 😅

@thaJeztah
Copy link
Member

We should probably look at what we do for other parts /var/lib/docker and the named pipe. I think we set a docker group on that on Windows, but it could be SysAdmin or something along those lines.

A quick follow-up should be to make the existing functions (for now) return an error if they cannot set permissions (so that it's not silently ignored).

@laurazard
Copy link
Member

I went to look a bit into this, and from the Microsoft post about AF_UNIX support on Windows:

Communication over unix sockets can be secured by controlling the file (or directory) permissions on the pathname sockets (or the parent directory). For example, the bind socket API creates a ‘socket’ file with the given pathname. The creation of the new socket file will fail if the calling process does not has write permission on the directory where the file is being created. Similarly, for connecting to a stream socket, the connecting process should have write permission on the socket. The same level of security is available and enforced on the Windows unix socket implementation.

We're not ignoring any errors from that (on the go-connections side anyway), and are setting file ownership/permissions.

However, umask isn't a supported syscall on windows, and beyond that, the os.Chmod docs state that:

On Windows, only the 0o200 bit (owner writable) of mode is used; it controls whether the file's read-only attribute is set or cleared. The other bits are currently unused. For compatibility with Go 1.12 and earlier, use a non-zero mode. Use mode 0o400 for a read-only file and 0o600 for a readable+writable file.

Given that, I guess we're dependant on the hierarchical directory permissions on Windows from preventing other users from accessing the socket.

@thaJeztah
Copy link
Member

We're not ignoring any errors from that (on the go-connections side anyway), and are setting file ownership/permissions.

Yeah, if memory serves me well, some of those functions in Go stdlib are not implemented on Windows, and silently discarded (uid/gid being ignored). But we should check for sure.

@laurazard
Copy link
Member

Yup! I had written some of that in that reply:

the os.Chmod docs state that:

On Windows, only the 0o200 bit (owner writable) of mode is used; it controls whether the file's read-only attribute is set or cleared. The other bits are currently unused. For compatibility with Go 1.12 and earlier, use a non-zero mode. Use mode 0o400 for a read-only file and 0o600 for a readable+writable file.

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.

7 participants