-
Notifications
You must be signed in to change notification settings - Fork 582
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
osutil/sys, client: add sys.RunAsUidGid, use it for auth.json #4983
Conversation
If /home is on NFS, trying to read, write or remove the user's auth.json as root fails. This PR adds code to drop privileges to the owner of the file before attempting operating on it.
Wellp, that didn't work. back to the drawing board. |
osutil/sys/syscall.go
Outdated
|
||
ch <- f() | ||
|
||
// From Go 1.10, if we exit without unlocking, the thread is killed \o/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you tried to restore the uid/gid and then runtime.UnlockOSThread()
instead of exiting?
osutil/sys/syscall.go
Outdated
// RunAsUidGid starts a goroutine, pins it to the OS thread, calls setuid and | ||
// setgid, and runs the function. | ||
func RunAsUidGid(uid UserID, gid GroupID, f func() error) error { | ||
ch := make(chan error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, and a buffered channel instead of unbufferred one.
we're back, baby! |
Codecov Report
@@ Coverage Diff @@
## master #4983 +/- ##
==========================================
+ Coverage 79.12% 79.18% +0.05%
==========================================
Files 478 485 +7
Lines 35230 35950 +720
==========================================
+ Hits 27876 28467 +591
- Misses 5157 5234 +77
- Partials 2197 2249 +52
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yay!
return nil | ||
} | ||
return sys.RunAsUidGid(uid, gid, func() error { | ||
if err := os.MkdirAll(filepath.Dir(targetFile), 0700); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, just return the error directly? :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, the diff formatting confused me, never mind :)
osutil/sys/syscall.go
Outdated
return r1, r2, err | ||
} | ||
|
||
type UnrecoverableError struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please document this type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments inline.
Err error | ||
} | ||
|
||
func (e UnrecoverableError) Error() string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please document this error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing docs :)
osutil/sys/syscall.go
Outdated
func retryRawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { | ||
ms := &syscall.Timespec{Nsec: 1000} | ||
for i := 0; i < 30; i++ { | ||
r1, r2, err = syscall.RawSyscall(trap, a1, a2, a3) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a need to use RawSyscall vs Syscall? The difference, AFAIK, is that raw syscall blocks directly and Syscall will ping back to the runtime to say its about to enter and has just left a system call. If there's a need then please document why we use RawSyscall here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm trusting the go standard library on the distinction here (syscall.Setreuid
et al use RawSyscall
). The difference is not documented and I don't know enough of the internals to have a good opinion.
osutil/sys/syscall.go
Outdated
@@ -107,3 +109,79 @@ func FcntlGetFl(fd int) (int, error) { | |||
} | |||
return int(flags), nil | |||
} | |||
|
|||
func retryRawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this function warrants a comment
osutil/sys/syscall.go
Outdated
ruid := Getuid() | ||
rgid := Getgid() | ||
|
||
// do the setregid first =) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you document if the order is significant and if so, why, please?
osutil/sys/syscall.go
Outdated
// carefully restore thread state. | ||
defer runtime.UnlockOSThread() | ||
|
||
ruid := Getuid() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While reading I read the code for those. Is there any reason we are not using getres{u,g}id()
? It uses one less system call.
Drop retryRawSyscall (we don't do this for other syscalls, yet), document UnrecoverableError, and make RunAsUidGid defer-less.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with a nitpick.
// from the docs: | ||
// until the goroutine exits or calls UnlockOSThread, it will | ||
// always execute in this thread, and no other goroutine can. | ||
// that last bit means it's safe to setuid/setgid in here, as no |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick, That
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm torn between fixing it now, or landing it now and fixing it later :-)
After much poking around, I don't think this is safe to land as is. It all boils down to golang/go#20676, which is fixed in 1.10 but not before. Given that this is for use from snap and not snapd, and that contention should be low, we could make it very very unlikely to hit the bug. But with the number of users we have, very unlikely is still quite likely. The options to fix lp:1761193 seem to me to be:
the first one gives me hives, the second will cause very weird bugs, the third is likely a year away, so ... maybe I do the fourth? |
If /home is on NFS, trying to read, write or remove the user's
auth.json as root fails.
This PR adds code to drop privileges to the owner of the file before
attempting operating on it.
This fixes lp:1761193.