first commit

Bu işleme şunda yer alıyor:
Albert S. 2018-01-02 16:38:14 +01:00
işleme 11dd36a7d8
4 değiştirilmiş dosya ile 265 ekleme ve 0 silme

2
Makefile Normal dosya
Dosyayı Görüntüle

@ -0,0 +1,2 @@
all: qsni.c
gcc -std=c11 -Wall -Wextra qsni.c -o qsni

64
README.md Normal dosya
Dosyayı Görüntüle

@ -0,0 +1,64 @@
qsni¹
====
qsni (quite simple network isolation) allows for simple assignment
of per cgroup iptables rules to programs.
While you can also achieve this (and more) using network namespaces,
the setup is not as simple/easy.
Requirements
------------
You need an iptables version that supports cgroup matching (e. g.
version >= 1.6);
The following kernel config paramaters must be set:
CONFIG_NETFILTER_XT_MATCH_CGROUP
CONFIG_NET_CLS_CGROUP
Example
=======
$ qsni blocked ping google.com
ping: unknown host google.com
$ qsni lan bash
$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
ping: sendmsg: Operation not permitted
$ ping 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.127 ms
$ qsni someprofile bash
already assigned to a net class, thus you can't use this binary to change that
$
Setup
=====
If cgroup_root isn't mounted to /sys/fs/cgroup, do it or change the
constant in the source to the correct path.
make
cp qsni /usr/bin/
chmod o=rx /usr/bin/qsni
chown root:root /usr/bin/qsni
setcap 'cap_setuid=ep cap_setgid=ep' /usr/bin/qsni
mkdir /etc/qsni.d
chmod o=rx /etc/qsni.d
cp profiles/blocked /etc/qsni.d/blocked
chmod o=r /etc/qsni.d/blocked
Every profile must have its own unique CGROUP_ID value in the profile
file.
Security discussion
--------------------
This alone is not a satisfactory way to prevent misbehaving programs
to contact destinations you don't want them to. While the restrictions
also apply to the children of the launched progorams, at a minimum, file
system isolation is also necessary and perhaps IPC etc.
qsni however does not aim to be a complete "jailing/isolation" solution.
Nevertheless, I have use cases for it, hence its existence.
¹ name is preliminary,

21
profiles/blocked Çalıştırılabilir dosya
Dosyayı Görüntüle

@ -0,0 +1,21 @@
#!/bin/sh
export PATH="/sbin:/usr/sbin:/usr:/bin"
CGROUP_ID=2000
function addrule()
{
iptables -C $@ -m cgroup --cgroup $CGROUP_ID &> /dev/null || iptables -A $@ -m cgroup --cgroup $CGROUP_ID
if [ $? -ne 0 ] ; then
echo "Failed adding iptables rule" >&2
exit 1
fi
}
NAME=$(basename $0)
[ -d /sys/fs/cgroup/net_cls/$NAME ] || mkdir /sys/fs/cgroup/net_cls/$NAME
if [ $? -ne 0 ] ; then
echo "Failed creating cgroup directory";
exit 1
fi
echo -n "$CGROUP_ID" > /sys/fs/cgroup/net_cls/$NAME/net_cls.classid
addrule OUTPUT -j DROP

178
qsni.c Normal dosya
Dosyayı Görüntüle

@ -0,0 +1,178 @@
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <linux/limits.h>
#define NET_CLS_DIR "/sys/fs/cgroup/net_cls/"
#define IPTABLES_PROFILES_DIR "/etc/qsni.d/"
//exits if we are already inside a profile.
void ensure_outside_profile()
{
FILE *fp = fopen("/proc/self/cgroup", "r");
if(fp == NULL)
{
fprintf(stderr, "Failed to open cgroup file\n");
exit(EXIT_FAILURE);
}
char *line = NULL;
size_t n = 0;
while(getline(&line, &n, fp) != -1)
{
char *id = line;
char *tmp = strchr(id, ':');
if(tmp == NULL)
{
fprintf(stderr, "Misformated cgroups file\n");
exit(EXIT_FAILURE);
}
*tmp = 0;
++tmp;
char *controllers = tmp;
tmp = strchr(controllers, ':');
if(tmp == NULL)
{
fprintf(stderr, "Misformated cgroups file\n");
exit(EXIT_FAILURE);
}
*tmp = 0;
++tmp;
char *assigned = tmp;
if(strstr(controllers, "net_cls") != NULL)
{
if(assigned[0] == '/' && assigned[1] != '\n')
{
fprintf(stderr, "already assigned to a net class, thus you can't use this binary to change that\n");
exit(EXIT_FAILURE);
}
}
line = NULL;
n = 0;
}
fclose(fp);
}
void init_profile(const char *profilepath)
{
pid_t pid = fork();
if(pid == 0)
{
if(clearenv() != 0)
{
perror("clearenv");
exit(EXIT_FAILURE);
}
int ret = execl(profilepath, profilepath, (char *) NULL);
if(ret == -1)
{
perror("execl of child");
exit(EXIT_FAILURE);
}
}
else if(pid > 0)
{
int status=1;
pid_t w = waitpid(pid, &status, 0);
if(w == -1)
{
perror("waitpid");
exit(EXIT_FAILURE);
}
if(! WIFEXITED(status) || WEXITSTATUS(status) != 0)
{
fprintf(stderr, "profile setup script failed\n");
exit(EXIT_FAILURE);
}
}
if(pid == -1)
{
perror("fork");
exit(EXIT_FAILURE);
}
}
void drop(uid_t u, gid_t g)
{
if(setgid(g) != 0)
{
perror("setgid");
exit(EXIT_FAILURE);
}
if(setuid(u) != 0)
{
perror("setuid");
exit(EXIT_FAILURE);
}
}
void assign_to_profile(const char *profilename)
{
char taskspath[PATH_MAX +1];
snprintf(taskspath, sizeof(taskspath), "%s/%s/tasks", NET_CLS_DIR, profilename);
pid_t mypid = getpid();
FILE *fp = fopen(taskspath, "a");
if(fp == NULL)
{
perror("fopen");
exit(EXIT_FAILURE);
}
fprintf(fp, "%ld", (long)mypid);
fclose(fp);
}
int main(int argc, char *argv[])
{
if(argc < 3)
{
fprintf(stderr, "Usage: %s profile command [arguments...]\n", argv[0]);
exit(EXIT_FAILURE);
}
ensure_outside_profile();
char *profilename = argv[1];
char profilepath[PATH_MAX +1];
snprintf(profilepath, sizeof(profilepath), "%s/%s", IPTABLES_PROFILES_DIR, profilename);
int ret = access(profilepath, R_OK);
if(ret != 0)
{
perror("check for profile path failed\n");
exit(EXIT_FAILURE);
}
uid_t myuid = getuid();
gid_t myguid = getgid();
if(setuid(0) != 0)
{
perror("setuid");
exit(EXIT_FAILURE);
}
init_profile(profilepath);
assign_to_profile(profilename);
drop(myuid, myguid);
argv += 2;
int result = execvp(argv[0], argv);
if(result == -1)
{
perror("execv");
exit(EXIT_FAILURE);
}
return 0;
}