syscall.Chroot and syscall.Setuid

3,301 views
Skip to first unread message

ziutek

unread,
Jan 19, 2011, 4:28:24 PM1/19/11
to golang-nuts
How they work if I use 6/8g compiler?

I think that GC thread isn't affected by this calls and runs with
original UID and without chroot. Any gorutine created after Chroot/
Setuid runs with changed UID and in chroot environment.

Am I right?

What if GOMAXPROCS == 1 or GOMAXPROCS > 1 ?

What happens when I call Chroot/Setuid in new created gorutine?

Does it make sense to use these syscalls to jail and change privileges
to the main thread or some gorutine? If so, how secure it will be if
program initially is started with root privileges?

--
Michał Derkacz
http://github.com/ziutek

David Roundy

unread,
Jan 19, 2011, 4:40:28 PM1/19/11
to ziutek, golang-nuts
My reading of chroot(2) suggest that the entire process is affected,
and I presume setuid is the same. So that would mean that all
goroutines are affected.

David

--
David Roundy

Dave Cheney

unread,
Jan 19, 2011, 4:45:13 PM1/19/11
to ziutek, golang-nuts
I am pretty sure that won't work.

Chroot/setuid work at the process level not the thread level. Even if it were possible to alter the effective uid for a thread, the runtime model of mapping n goroutines to m threads would make it impossible to lock a certain goroutine to a thread with reduced privs.

Happy to be proven wrong, why don't you code up an example and give it a try?

Cheers

Dave


Sent from my iPhone

Jacek Masiulaniec

unread,
Jan 19, 2011, 5:02:56 PM1/19/11
to golang-nuts
Both chroot and setuid are defined to take effect at process level.
However, the setuid situation in the dominant UNIX-like system is laughable.

http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/linux-thread-problems.html

"The kernel still lacks the ability to share several resources between tasks
that, according to the POSIX threading model, must be shared by all threads
within a single process. So there are still, even as of 2010 and kernel version
2.6.33, problems with threads on Linux. Quite a lot of necessary changes to
the kernel still remain outstanding."

In Linux, setuid affects only the thread that happened to be executing
the system call.
The remaining ones are unaffected.

Jacek

ziutek

unread,
Jan 19, 2011, 5:31:11 PM1/19/11
to golang-nuts
Hmm... There is thread list for my Go application:

# ps -efL|grep komunikat
net_info 26760 1 26760 0 6 20:32 pts/5 00:00:01 ./
komunikaty
root 26760 1 26761 0 6 20:32 pts/5 00:00:00 ./
komunikaty
net_info 26760 1 31395 0 6 20:33 pts/5 00:00:00 ./
komunikaty
net_info 26760 1 31398 0 6 20:33 pts/5 00:00:01 ./
komunikaty
root 26760 1 31879 0 6 20:33 pts/5 00:00:00 ./
komunikaty
net_info 26760 1 31910 0 6 20:33 pts/5 00:00:00 ./
komunikaty
#UID PID PPID LWP C NLWP STIME TTY TIME CMD

main() looks like this:

// Listen on port < 1024
ls, err := net.Listen("tcp", addr)

// chroot and setuid to net_info user
chrootuid(os.Args[2], os.Args[3])

// Run http server
err = http.Serve(ls, &disp)

Why some threads runs with root user and some with net_info user?



ziutek

unread,
Jan 19, 2011, 5:34:45 PM1/19/11
to golang-nuts

ziutek

unread,
Jan 19, 2011, 5:48:28 PM1/19/11
to golang-nuts
> In Linux, setuid affects only the thread that happened to be executing
> the system call.
> The remaining ones are unaffected.

I work on Linux system. So Chroot affects the whole process but Setuid
only the single thread, isn't it?

Jacek Masiulaniec

unread,
Jan 19, 2011, 6:14:15 PM1/19/11
to ziutek, golang-nuts

Sadly, yes.

ziutek

unread,
Jan 19, 2011, 6:32:31 PM1/19/11
to golang-nuts
Go scheduler probably don't take into account that one thread has new
UID, isn't it? If not, is there any chance to improve the scheduler in
this subject?

My naive proposal: add new function (maybe runtime.Setuid) that create
new system thread with new UID and instructs the scheduler to schedule
the gorutine which called Setuid (and their Children) only in this new
thread.

Jacek Masiulaniec

unread,
Jan 19, 2011, 7:55:51 PM1/19/11
to ziutek, golang-nuts
Operating system errors are poor foundation for applications.

Setuid problem aside, if you chroot you will quickly notice loss of many
common services. Consider how are DNS queries to be made in absence
of /etc/resolv.conf? Moreover, crypto/rand package relies on /dev/urandom,
crypto/tls on /etc/ssl, and so on.

It's possible to engineer solutions to these problems. The OpenBSD project
has over the years introduced chroot, privilege drop, and privilege separation
to many system tools and services.

Sadly, doing that in Go in a way that is portable across the UNIX land
is made impossible by that Linux threading issue.

Currently, the easiest way of getting some safety benefit with minimum hassle
is to sudo -u user a program that listens on high ports only.

ziutek

unread,
Jan 20, 2011, 4:19:22 AM1/20/11
to golang-nuts
> Operating system errors are poor foundation for applications.

You're right. So maybe add os.Setuid which on Linux changes UIDs for
all threads?

Overall, I need a predictable behavior. Current behavior, where one
gorutine sometime works with one user privileges and sometimes with
other isn't acceptable.

But as long there isn't os.Setuid I'm wondering about other options. I
noticed runtime.LockOSThread function. If I use this function in one
gorutine, in which OS thread will be scheduled new gorutines, created
later by the gorutine that called LockOSThread?

The documentation says:
func LockOSThread()

LockOSThread wires the calling goroutine to its current operating
system thread. Until the calling goroutine exits or calls
UnlockOSThread, it will always execute in that thread, and no other
goroutine can. LockOSThread cannot be used during init functions.

Jacek Masiulaniec

unread,
Jan 20, 2011, 4:38:15 AM1/20/11
to ziutek, golang-nuts
On 20 January 2011 09:19, ziutek <ziu...@lnet.pl> wrote:
>> Operating system errors are poor foundation for applications.
>
> You're right. So maybe add os.Setuid which on Linux changes UIDs for
> all threads?

The whole point is that on Linux it's impossible.

> Overall, I need a predictable behavior. Current behavior, where one
> gorutine sometime works with one user privileges and sometimes with
> other isn't acceptable.

Sure. Report the problem on the right forum: http://lkml.org
It has nothing to do with Go.

ziutek

unread,
Jan 20, 2011, 4:52:35 AM1/20/11
to golang-nuts
> The whole point is that on Linux it's impossible.

I think that Go scheduler can do that. Because UID is important only
in interaction with OS, then if Go scheduler can schedule gorutine
when it need OS interaction, it can change UIDs for all threads at the
right time.

Florian Weimer

unread,
Jan 20, 2011, 2:50:39 PM1/20/11
to Jacek Masiulaniec, golang-nuts
* Jacek Masiulaniec:

> Both chroot and setuid are defined to take effect at process level.
> However, the setuid situation in the dominant UNIX-like system is laughable.
>
> http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/linux-thread-problems.html

This part is about LinuxThreads. It's fixed in NPTL. The Go run-time
just doesn't use the proper flags when calling clone(), I guess.

Jacek Masiulaniec

unread,
Jan 20, 2011, 6:54:19 PM1/20/11
to Florian Weimer, golang-nuts

No, according to that article it's not fixed but ignored. A call to kernel's
setuid still does not act as advertised. Instead, it's suggested that
users write setuid wrappers that simulate desired behavior with the
aid of signals.

ziutek

unread,
Jan 20, 2011, 9:28:16 PM1/20/11
to golang-nuts
I read a bit about Linux threads and I have the following opinion in
this topic:

1. Linux kernel isn't 100% POSIX compliant and this isn't a bug.
2. Behavior of clone system call is strictly defined. It can't create
threads in POSIX sense.
3. The GLIBC is responsible for Linux threads compatibility with
POSIX.
4. Thread created by clone system call is Linux thread, not POSIX
thread.
5. If someone wants to create POSIX compliant thread, it should use
GLIBC and its pthread_create function. If thread created by GLIBC will
not be compatible with POSIX, then it is a bug (in GLIBC, not in Linux
kernel).
6. If Go doesn't use GLIBC than it must take into account the specific
Linux kernel behavior.

Albert Strasheim

unread,
Jan 21, 2011, 1:57:36 AM1/21/11
to golang-nuts
Hello

On Jan 21, 4:28 am, ziutek <ziu...@Lnet.pl> wrote:
> 6. If Go doesn't use GLIBC than it must take into account the specific
> Linux kernel behavior.

I've also been reading up and I've come to the same conclusions you
have.

Time to file a Go issue?

Regards

Albert

ziutek

unread,
Jan 21, 2011, 4:04:32 AM1/21/11
to golang-nuts
> Time to file a Go issue?

Simple question:

Can I write a web application using http package, which works on port
80 without root privileges?

If not, there is Go issue (Linux version).

roger peppe

unread,
Jan 21, 2011, 4:11:00 AM1/21/11
to ziutek, golang-nuts
On 21 January 2011 09:04, ziutek <ziu...@lnet.pl> wrote:
>> Time to file a Go issue?
>
> Simple question:
>
> Can I write a web application using http package, which works on port
> 80 without root privileges?

you can always use http.ListenAndServe to listen on an
explicit port.

ziutek

unread,
Jan 21, 2011, 4:18:11 AM1/21/11
to golang-nuts
> you can always use http.ListenAndServe to listen on an
> explicit port.

If I haven't root privileges, I can't listen on port number 80.
If I have root privileges I can, but I can't effectively change UID
after listen.

Look for my real example:

// Listen on port < 1024
ls, err := net.Listen("tcp", addr)

// chroot and setuid to net_info user
chrootuid(os.Args[2], os.Args[3])

// Run http server
err = http.Serve(ls, &disp)

Some threads after this still have root privileges.

Albert Strasheim

unread,
Jan 21, 2011, 4:24:36 AM1/21/11
to golang-nuts
Hello

On Jan 21, 11:18 am, ziutek <ziu...@Lnet.pl> wrote:
> > you can always use http.ListenAndServe to listen on an
> > explicit port.
> If I haven't root privileges, I can't listen on port number 80.
> If I have root privileges I can, but I can't effectively change UID
> after listen.
> Some threads after this still have root privileges.

And it's not just a port problem.

To implement a server (even one that listens on a high port) that does
privilege separation like OpenSSH, you also need a reliable Setuid.

Regards

Albert

roger peppe

unread,
Jan 21, 2011, 4:39:57 AM1/21/11
to ziutek, golang-nuts
On 21 January 2011 09:18, ziutek <ziu...@lnet.pl> wrote:
>> you can always use http.ListenAndServe to listen on an
>> explicit port.
>
> If I haven't root privileges, I can't listen on port number 80.

sorry, an extra comma meant that i misinterpreted your question
(as "Can I write a web application using http package (which works on port
80) without root privileges?")

it's probably worth filing an issue.

ziutek

unread,
Jan 21, 2011, 6:11:36 AM1/21/11
to golang-nuts

Eric Clark

unread,
Jan 21, 2011, 12:39:46 PM1/21/11
to ziutek, golang-nuts
Using capabilities, you can grant CAP_NET_BIND_SERVICE[1] which will
allow your go program to bind to low ports. I tested this in the past
and it works fine. Dropping the capability after binding to the port
would be nice, but I think you would have to add a function to the
syscall package to implement it.

Personally, I wouldn't run a go program on port 80 and would instead
run lighttpd or nginx as a reverse proxy in front of it but this
shouldn't be a requirement. The setuid problem is separate from this,
as Albert points out, and it would be nice to resolve it.

1: http://linux.die.net/man/7/capabilities

ziutek

unread,
Jan 21, 2011, 1:39:39 PM1/21/11
to golang-nuts
> Dropping the capability after binding to the port
> would be nice, but I think you would have to add a function to the
> syscall package to implement it.

I wonder if setcap syscall will change capabilities for overall
process, or only for one thread.

Alexandre Fiori

unread,
May 15, 2013, 12:11:36 PM5/15/13
to golan...@googlegroups.com

I've been using setcap (tool, not the syscall) on linux to bind on low ports and it works flawless.

On ubuntu, I had to install libcap2-bin and execute /sbin/setcap 'cap_net_bind_service=+ep' /path/to/command
+ep is effective and permitted

After that the command might run as non-root and bind on low ports.

赵雪松

unread,
Oct 27, 2014, 10:09:15 PM10/27/14
to golan...@googlegroups.com, ziu...@lnet.pl
and that also means that Setuid affects it's goroutine and the the whole thread but not the process,isn't true?

在 2011年1月20日星期四UTC+8上午7时14分15秒,Jacek Masiulaniec写道:

Ian Taylor

unread,
Oct 28, 2014, 12:45:42 AM10/28/14
to 赵雪松, golang-nuts, ziu...@lnet.pl
On Mon, Oct 27, 2014 at 7:09 PM, 赵雪松 <zxs8...@gmail.com> wrote:
> and that also means that Setuid affects it's goroutine and the the whole
> thread but not the process,isn't true?

http://golang.org/issue/1435

Ian

> 在 2011年1月20日星期四UTC+8上午7时14分15秒,Jacek Masiulaniec写道:
>>
>> On 19 January 2011 22:48, ziutek <ziu...@lnet.pl> wrote:
>> >> In Linux, setuid affects only the thread that happened to be executing
>> >> the system call.
>> >> The remaining ones are unaffected.
>> >
>> > I work on Linux system. So Chroot affects the whole process but Setuid
>> > only the single thread, isn't it?
>>
>> Sadly, yes.
>
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages