0

I'm working on adding some restrictions to my build process - to detect cycles, specifically. To achieve this I've been experimenting with user namespaces.

Here's my 'hello world' program:

#include <sched.h>
#include <unistd.h>

int main()
{
    if( unshare(CLONE_NEWUSER) != 0)
    {
        return -1;
    }

    execl("/bin/sh", "/bin/sh", "-e", "-c", "make", NULL);
    return 0;
}

Here is the makefile being run by make, namespace_test.cpp is the name of the file above:

namespace_test: namespace_test.cpp
    g++ namespace_test.cpp -o ./namespace_test

When everything is up to date (as determined by make) the exec'd program works as expected:

make: 'namespace_test' is up to date.

But if make actually runs the g++ invocation I get an opaque error:

g++ namespace_test.cpp -o ./namespace_test
make: g++: Invalid argument
make: *** [Makefile:2: namespace_test] Error 127

What is the reason for this behavior?

kaylum
  • 13,833
  • 2
  • 22
  • 31
Dan O
  • 4,323
  • 5
  • 29
  • 44
  • I suspect I'm failing to correctly set up the uid_map and gid_map. Will report back an answer if that's the case and I figure it out. – Dan O Sep 04 '21 at 07:28
  • 1
    Is working fine for me in both cases (up to date and after a touch), anyway you have more info about Error 127 [here](https://stackoverflow.com/questions/36365752/make-error-127-when-running-trying-to-compile-code) – David Ranieri Sep 04 '21 at 07:41
  • Sorry, when you say 'is working fine' do you mean the `namespace_test` program that is invoking its own makefile - or just the makefile? – Dan O Sep 04 '21 at 08:16
  • 1
    I can compile using your `Makefile` and also `touch namespace_test.cpp` and run the program invoking `make`, in both cases it works fine: https://pastebin.com/1402LdRc – David Ranieri Sep 04 '21 at 08:22
  • 1
    That's interesting, maybe I will compare behavior in other distros. Docker and podman do work on this machine, though, so I think my little test is not totally correct. – Dan O Sep 04 '21 at 08:32
  • 1
    If it helps, thats the ouput of `g++ -v namespace_test.cpp -o namespace_test` on my machine: https://pastebin.com/zDJy4WWS (Ubuntu 21.04) – David Ranieri Sep 04 '21 at 08:52

1 Answers1

0

This error was due to my failure to set up the uid_map and gid_map. I have not produced a satisfactory, explicit, minimal example of the error, but I have written a working minimal solution, that I will share here. Notice that int main() is identical, except before exec'ing the target command we first set up the uid_map and then the gid_map (granting ourselves permission via setgroups).

On my terminal $ id informs me that my real uid and gid are both 1000, so I have hardcoded that in the maps. It is more correct to query for the original id at the start of the process, see this excellent blog post. Also instrumental in this solution is this man page.

#include <cstdio>
#include <cstring>
#include <fcntl.h>
#include <sched.h>
#include <stdlib.h>
#include <unistd.h>

#define fatal_error(...) \
do { \
    fprintf(stderr, "namespace_test \033[1;31merror:\033[0m "); \
    fprintf(stderr, __VA_ARGS__ ); \
    fprintf(stderr, "\n"); \
    exit(EXIT_FAILURE); \
} while(0)

void write_string_to_file(const char* filename, const char* str, size_t str_len)
{
    int fd = open(filename, O_RDWR);
    if(fd == -1)
    {
        fatal_error("Failed to open %s: %m", filename);
    }

    if( write(fd, str, str_len) != str_len )
    {
        fatal_error("Failed to write %s: %m", filename);
    }

    close(fd);
}

void write_uid_mapping()
{
    const char* mapping = "0 1000 1";
    write_string_to_file("/proc/self/uid_map", mapping, strlen(mapping));
}

void write_set_groups()
{
    const char* deny = "deny\n";
    write_string_to_file("/proc/self/setgroups", deny, strlen(deny));
}

void write_gid_mapping()
{
    write_set_groups();

    const char* mapping = "0 1000 1";
    write_string_to_file("/proc/self/gid_map", mapping, strlen(mapping));
}

int main()
{
    if(unshare(CLONE_NEWUSER) != 0)
    {
        fatal_error("Failed to move into new user namespace");
    }

    write_uid_mapping();
    write_gid_mapping();

    execl("/bin/sh", "/bin/sh", "-e", "-c", "make", NULL);

    return 0;
}
Dan O
  • 4,323
  • 5
  • 29
  • 44