This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Testing on hosts with firewalls


On 29 Dec 2016 20:47, Florian Weimer wrote:
> On 12/29/2016 07:08 PM, Mike Frysinger wrote:
> > does it help if you run all the tests in a unique net namespace ?
> > i think our skel test framework should set up unique namespaces
> > all the time anyways to prevent inadvertent leakage, and to handle
> > issues like port collisions.
> 
> This is an interesting idea.
> 
> I don't see how I can set up interfaces in a network namespace as an 
> unprivileged user.

it's trivial:
- create a new user namespace
- map current id to root
- create all the namespaces you want (like net)
- set up the loopback interface as needed
- create a new user namespace (or not if you want to run as "root")
- map root back to current id
- run all tests against loopback

the `unshare` program can be used to experiment (but it wouldn't be
used by tests themselves).  just run:
$ id
uid=8282(vapier) gid=100(users) groups=100(users)
$ unshare -Urn
# id
uid=0(root) gid=0(root) groups=0(root)
# ip a s dev lo
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
# ip link set up dev lo
# ip a s dev lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever

here's some code below that i've written that should show how to do
all of this.  you can do:
$ gcc -Wall quick-unshare.c -o quick-unshare
$ ./quick-unshare -Un bash

it would mean you don't have access to the network if you want to hit
external services, but i don't think tests should be written in general
to require a working network connection.
-mike
#define _GNU_SOURCE

#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <sched.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>

static bool vunshare(int flags)
{
	if (unshare(flags) == -1) {
		if (errno != EINVAL)
			err(1, "unshare failed");
		return false;
	}
	return true;
}

static void unshare_net(void)
{
	if (!vunshare(CLONE_NEWNET))
		return;

	int sock;
	struct ifreq ifr;

	sock = socket(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0);
	if (sock < 0)
		err(1, "socket(AF_LOCAL) failed");

	/* Equiv of `ip link set up lo`.  Kernel will assign 127.0.0.1 for us. */
	strcpy(ifr.ifr_name, "lo");
	if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
		err(1, "ioctl(SIOCGIFFLAGS) failed");

	/* The kernel preserves ifr.ifr_name for use. */
	ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
	if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
		err(1, "ioctl(SIOCSIFFLAGS) failed");

	close(sock);
}

static void map_uid_gid(uid_t iuid, gid_t igid, uid_t ouid, gid_t ogid)
{
	FILE *fp;

	fp = fopen("/proc/self/setgroups", "w");
	if (fp) {
		fputs("deny\n", fp);
		fclose(fp);
	}

	fp = fopen("/proc/self/uid_map", "w");
	fprintf(fp, "%u %u 1\n", iuid, ouid);
	fclose(fp);

	fp = fopen("/proc/self/gid_map", "w");
	fprintf(fp, "%u %u 1\n", igid, ogid);
	fclose(fp);
}

static void usage(void)
{
	puts("Usage: unshare [options] <program>");
	exit(EX_USAGE);
}

int main(int argc, char *argv[])
{
	int c;
	bool newipc = false;
	bool newmnt = false;
	bool newnet = false;
	bool newuts = false;
	bool newusr = false;
	uid_t uid;
	gid_t gid;

	while ((c = getopt(argc, argv, "+imnuU")) != -1) {
		switch (c) {
		case 'i': newipc = true; break;
		case 'm': newmnt = true; break;
		case 'n': newnet = true; break;
		case 'u': newuts = true; break;
		case 'U': newusr = true; break;
		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;

	if (argc == 0)
		usage();

	if (newusr) {
		uid = getuid();
		gid = getgid();
		if (vunshare(CLONE_NEWUSER))
			map_uid_gid(0, 0, uid, gid);
		else
			newusr = false;
	}

	if (newmnt)
		vunshare(CLONE_NEWNS);
	if (newuts)
		vunshare(CLONE_NEWUTS);
	if (newipc)
		vunshare(CLONE_NEWIPC);
	if (newnet)
		unshare_net();

	if (newusr)
		if (vunshare(CLONE_NEWUSER))
			map_uid_gid(uid, gid, 0, 0);

	execvp(argv[0], argv);
	fprintf(stderr, "%s: %s\n", argv[0], strerror(errno));
	return 127;
}

Attachment: signature.asc
Description: Digital signature


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]