initial commit
This commit is contained in:
commit
24ff954904
9 changed files with 419 additions and 0 deletions
16
Makefile
Normal file
16
Makefile
Normal file
|
@ -0,0 +1,16 @@
|
|||
all:
|
||||
rm -rf starter/alpine-make-rootfs
|
||||
git clone https://github.com/alpinelinux/alpine-make-rootfs
|
||||
mv alpine-make-rootfs starter/alpine-make-rootfs
|
||||
go build -o ./run
|
||||
gcc starter/starter.c -o starter/starter
|
||||
starter/create_rootfs.sh
|
||||
install_minrootfs:
|
||||
rm -rf starter/alpine-make-rootfs
|
||||
git clone https://github.com/alpinelinux/alpine-make-rootfs
|
||||
mv alpine-make-rootfs starter/alpine-make-rootfs
|
||||
mkrootfs:
|
||||
starter/create_rootfs.sh
|
||||
compile:
|
||||
go build -o ./run
|
||||
gcc starter/starter.c -o starter/starter
|
17
main.go
Normal file
17
main.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
//"os"
|
||||
//"fmt"
|
||||
//"log"
|
||||
//exec "os/exec"
|
||||
runner "runner/runner"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err:=runner.Init()
|
||||
if(err!=nil) {
|
||||
panic(err)
|
||||
}
|
||||
//runner.IsolatedRun(exec.Command("ls", "/"))
|
||||
}
|
89
runner/runner.go
Normal file
89
runner/runner.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
package runner
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
exec "os/exec"
|
||||
cgroups "github.com/containerd/cgroups"
|
||||
//cgroup2 "github.com/containerd/cgroups/v3/cgroup2"
|
||||
//specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
rand "math/rand"
|
||||
"time"
|
||||
"os/user"
|
||||
)
|
||||
|
||||
const runner_username string = "gaterunner"
|
||||
const runIdLength = 20
|
||||
var coresIsolated []int
|
||||
|
||||
type Limits {
|
||||
core int
|
||||
memory int
|
||||
}
|
||||
|
||||
func extractNumbers(s string) (result []int) {
|
||||
lastNumber,isNumber := false,false
|
||||
var curNumber=0
|
||||
for _,char :=range s {
|
||||
isNumber=(char>='0' && char<='9')
|
||||
if(isNumber) {
|
||||
curNumber*=10
|
||||
curNumber+=int(char-'0')
|
||||
}
|
||||
if(!isNumber && lastNumber) {
|
||||
result=append(result,curNumber)
|
||||
curNumber=0
|
||||
}
|
||||
lastNumber=isNumber
|
||||
}
|
||||
if(lastNumber) {
|
||||
result=append(result,curNumber)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Init() error {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
//croup initialisation:
|
||||
if cgroups.Mode() == cgroups.Unified {
|
||||
log.Println("cgroups v2 usage approved")
|
||||
} else {
|
||||
return fmt.Errorf("cgroups v2 are not enabled")//TODO: trouble description
|
||||
}
|
||||
//isolated cores initialisation:
|
||||
cmdlineBytes := make([]byte, 400)
|
||||
cmdlineFile,_:=os.Open("/proc/cmdline");
|
||||
countCmdlineBytes,_:=cmdlineFile.Read(cmdlineBytes);
|
||||
cmdline:=string(cmdlineBytes[:countCmdlineBytes])
|
||||
kernelParams:=strings.Split(cmdline," ")
|
||||
for _,param := range kernelParams{
|
||||
if(len(param)>=9 && param[:9]=="isolcpus=") {
|
||||
coresIsolated=append(coresIsolated, extractNumbers(param[9:])...)
|
||||
}
|
||||
}
|
||||
if(len(coresIsolated)==0) {
|
||||
return fmt.Errorf("no free cores available")//TODO: trouble description
|
||||
}
|
||||
log.Println("running on cores:",coresIsolated)
|
||||
//user setup:
|
||||
_,err:=user.Lookup(runner_username)
|
||||
if err!=nil{
|
||||
if errors.As(err, new(user.UnknownUserError)) {
|
||||
exec.Command("useradd", runner_username).Run();
|
||||
} else {
|
||||
return fmt.Errorf("user error:",err)
|
||||
}
|
||||
}
|
||||
log.Println("Runner initialisation successful!")
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsolatedRun(command []string,core int) {
|
||||
var runId string
|
||||
for i:=0;i<runIdLength;i++{
|
||||
runId+=string('a'+byte(rand.Int31n(26)))
|
||||
}
|
||||
}
|
32
runner/runner_test.go
Normal file
32
runner/runner_test.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package runner
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type ENTestPair struct {
|
||||
test string
|
||||
result []int
|
||||
}
|
||||
|
||||
var ENTests = []ENTestPair {
|
||||
{"123",[]int{123}},
|
||||
{"abc123abc",[]int{123}},
|
||||
{"",[]int{}},
|
||||
{"0",[]int{0}},
|
||||
{"1a2a3a6",[]int{1,2,3,6}},
|
||||
}
|
||||
|
||||
func TestExtractNumbers(t *testing.T) {
|
||||
for _,test:=range(ENTests) {
|
||||
result:=extractNumbers(test.test)
|
||||
if(len(result)!=len(test.result)) {
|
||||
t.Error("for",test.test,"expected",test.result,"got",result)
|
||||
}
|
||||
for i:=0;i<len(result);i++ {
|
||||
if(result[i]!=test.result[i]) {
|
||||
t.Error("for",test.test,"expected",test.result,"got",result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
51
starter/cgroup_prepare.c
Normal file
51
starter/cgroup_prepare.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include<sys/prctl.h>
|
||||
#include<sys/mount.h>
|
||||
#include<sys/stat.h>
|
||||
#include<unistd.h>
|
||||
#include<linux/limits.h>
|
||||
|
||||
#define CGROUP_NAME_SIZE 20
|
||||
char cgroup_name[CGROUP_NAME_SIZE+1];
|
||||
const char* cgroups_path="/sys/fs/cgroup/";
|
||||
|
||||
void prepare_cgroup(struct limits* limits) {
|
||||
for(int i=0;i<CGROUP_NAME_SIZE;i++) {
|
||||
cgroup_name[i]='a'+rand()%26;
|
||||
}
|
||||
char cwd[PATH_MAX];
|
||||
if(getcwd(cwd,sizeof(cwd))==NULL) die("getcwd error: %m");
|
||||
chdir(cgroups_path);
|
||||
mkdir(cgroup_name,0755);
|
||||
chdir(cgroup_name);
|
||||
char* memory_string=NULL;
|
||||
asprintf(&memory_string,"%d\n",limits->memory);
|
||||
write_file("memory.max",memory_string);
|
||||
free(memory_string);
|
||||
char* cpus_string=NULL;
|
||||
asprintf(&cpus_string,"%d\n",limits->core);
|
||||
write_file("cpuset.cpus",cpus_string);
|
||||
free(cpus_string);
|
||||
write_file("pids.max","1\n");
|
||||
//write_file("cpuset.cpus","3\n");
|
||||
chdir(cwd);
|
||||
}
|
||||
|
||||
void add_to_cgroup(int pid) {
|
||||
char cwd[PATH_MAX];
|
||||
if(getcwd(cwd,sizeof(cwd))==NULL) die("getcwd error: %m");
|
||||
chdir(cgroups_path);
|
||||
chdir(cgroup_name);
|
||||
char* pidstr=NULL;
|
||||
asprintf(&pidstr,"%d\n",pid);
|
||||
write_file("cgroup.procs",pidstr);
|
||||
free(pidstr);
|
||||
chdir(cwd);
|
||||
}
|
||||
|
||||
void remove_cgroup() {
|
||||
char cwd[PATH_MAX];
|
||||
if(getcwd(cwd,sizeof(cwd))==NULL) die("getcwd error: %m");
|
||||
if(chdir(cgroups_path)) die("chdir error: %m");
|
||||
if(rmdir(cgroup_name)) die("rmdir error: %m");
|
||||
if(chdir(cwd)) die("chdir error: %m");
|
||||
}
|
17
starter/create_rootfs.sh
Executable file
17
starter/create_rootfs.sh
Executable file
|
@ -0,0 +1,17 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [[ $(whoami) != root ]]
|
||||
then
|
||||
echo "run as root please"
|
||||
exit
|
||||
fi
|
||||
|
||||
NAME=minrootfs
|
||||
SCRIPT_DIR=$(dirname -- "$(readlink -f "${BASH_SOURCE}")")
|
||||
|
||||
rm -rf $SCRIPT_DIR/$NAME
|
||||
sudo $SCRIPT_DIR/alpine-make-rootfs/alpine-make-rootfs $SCRIPT_DIR/$NAME
|
||||
#chown -R nobody:nogroup $SCRIPT_DIR/$NAME
|
||||
#rm -r ../$NAME
|
||||
#mv $NAME ../$NAME
|
||||
|
77
starter/ns_exec.c
Normal file
77
starter/ns_exec.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
#include<sys/prctl.h>
|
||||
#include<sys/mount.h>
|
||||
#include<sys/stat.h>
|
||||
|
||||
static void procfs_prepare()
|
||||
{
|
||||
if (mkdir("/proc", 0555) && errno != EEXIST) die("Failed to mkdir /proc: %m\n");
|
||||
if (mount("proc", "/proc", "proc", 0, "")) die("Failed to mount proc: %m\n");
|
||||
}
|
||||
|
||||
#define hostname "runner"
|
||||
|
||||
#define put_old "oldfs"
|
||||
//#define shared_folder "../shared"
|
||||
//#define shared_mountpoint "shared"
|
||||
void mnt_prepare(char* rootfs, char* shared_mountpoint) {
|
||||
//char* shared_folder = malloc(strlen(shared_mountpoint)+4);
|
||||
//strcpy(shared_folder,"../");
|
||||
//strcat(shared_folder,shared_mountpoint);
|
||||
char* shared_folder=NULL;
|
||||
asprintf(&shared_folder,"../%s",shared_mountpoint);
|
||||
if (mount(rootfs,rootfs,"ext4",MS_BIND,"")) die("failed to mount %s: %m", rootfs);
|
||||
//if (mount(shared_mountpoint,shared_mountpoint,"ext4",MS_BIND,"")) die("failed to mount %s: %m",shared_mountpoint);
|
||||
if (chdir(rootfs)) die("falied to cd:%m");
|
||||
if (mount("/sys","sys","sysfs",0,"")) die("failed to mount: %m");
|
||||
//if (mount("/dev","dev","udev",0,"")) die("failed to mount: %m");
|
||||
if (mkdir("shared", 0777) && errno != EEXIST) die("Failed to mkdir %s: %m\n", shared_mountpoint);
|
||||
if (mount(shared_folder,"shared","ext4",MS_BIND,"")) die("failed to mount: %m");
|
||||
if (mkdir(put_old, 0000) && errno != EEXIST) die("Failed to mkdir %s: %m\n", put_old);
|
||||
if (syscall(SYS_pivot_root, ".", put_old)) die("Failed to pivot_root from %s to %s: %m\n", rootfs, put_old);
|
||||
if (chdir("/")) die("Failed to chdir to new root: %m\n");
|
||||
procfs_prepare();
|
||||
if (umount2(put_old, MNT_DETACH)) die("Failed to umount put_old %s: %m\n", put_old);
|
||||
if (rmdir(put_old)) die("Failed to rmdir: %m");
|
||||
free(shared_folder);
|
||||
}
|
||||
void ro_fs(char* shared_mountpoint) {
|
||||
char* shared_folder = malloc(strlen(shared_mountpoint)+4);
|
||||
strcpy(shared_folder,"../");
|
||||
strcat(shared_folder,shared_mountpoint);
|
||||
if (mount("/","/","ext4",MS_REMOUNT | MS_RDONLY | MS_BIND,"")) die("failed to mount: %m");
|
||||
if (mount(shared_mountpoint,"shared","ext4",MS_REMOUNT | MS_RDONLY | MS_BIND,"")) die("failed to mount: %m");
|
||||
free(shared_folder);
|
||||
}
|
||||
//#undef shared_mountpoint
|
||||
//#undef shared_folder
|
||||
#undef put_old
|
||||
|
||||
void await_setup(int pipe) { // wait for signal from parent
|
||||
char buf[2];
|
||||
if (read(pipe, buf, 2) != 2) die("Failed to read from pipe: %m\n");
|
||||
}
|
||||
|
||||
static int nsrun(void* arg) {
|
||||
#define setup "./setup.sh"
|
||||
//die when parent dies
|
||||
if (prctl(PR_SET_PDEATHSIG, SIGKILL)) die("cannot PR_SET_PDEATHSIG for child process: %m\n");
|
||||
struct params *params = (struct params*) arg;
|
||||
await_setup(params->fd[0]);
|
||||
mnt_prepare("minrootfs",params->shared_folder);
|
||||
//if(chdir("shared")) die("faled to chdir: %m");
|
||||
sethostname(hostname,sizeof(hostname));
|
||||
//if(chdir("..")) die("faled to chdir: %m");
|
||||
ro_fs(params->shared_folder);
|
||||
if(setgid(1000)) die("failed to setgid:%m");
|
||||
if(setuid(1000)) die("failed to setuid:%m");
|
||||
char** argv = params->argv;
|
||||
char* cmd = argv[0];
|
||||
char* env[]={
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"PWD=/",
|
||||
"NULL",
|
||||
};
|
||||
if (execve(cmd,argv,env) == -1) die("failed to exec,%m");
|
||||
return 1;
|
||||
#undef setup
|
||||
}
|
98
starter/starter.c
Normal file
98
starter/starter.c
Normal file
|
@ -0,0 +1,98 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <sys/wait.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/sched.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include "util.h"
|
||||
|
||||
struct params {
|
||||
char* shared_folder;
|
||||
int fd[2];
|
||||
char **argv;
|
||||
};
|
||||
|
||||
struct limits {
|
||||
size_t memory;
|
||||
int core;
|
||||
};
|
||||
|
||||
#include "ns_exec.c"
|
||||
#include "cgroup_prepare.c"
|
||||
|
||||
//const size_t STACK_SIZE=1000000;
|
||||
#define STACK_SIZE 1000000
|
||||
static char nmstack[STACK_SIZE];
|
||||
|
||||
static void parse_args(int argc, char **argv, struct params *params,struct limits *limits){
|
||||
if (argc < 5) {
|
||||
printf("usage:\n starter <core_id> <memory_amount> <shared folder> <command, arg1,arg2,...>");
|
||||
exit(0);
|
||||
}
|
||||
argc--;argv++;
|
||||
limits->core=atoi(argv[0]);
|
||||
argc--;argv++;
|
||||
limits->memory=atoi(argv[0]);
|
||||
argc--;argv++;
|
||||
params->shared_folder=argv[0];
|
||||
argc--;argv++;
|
||||
|
||||
params->argv = argv;
|
||||
}
|
||||
|
||||
static void prepare_userns(int pid) {
|
||||
char path[100];
|
||||
char line[100];
|
||||
|
||||
int uid = getuid();
|
||||
int gid = getgid();
|
||||
//int unprivileged_uid=1002;
|
||||
//int unprivileged_gid=1002;
|
||||
int unprivileged_uid=66534;
|
||||
int unprivileged_gid=65534;
|
||||
|
||||
sprintf(path, "/proc/%d/uid_map", pid);
|
||||
sprintf(line, "0 %d 1\n1 %d 1000\n", uid, unprivileged_uid);
|
||||
write_file(path, line);
|
||||
|
||||
sprintf(path, "/proc/%d/setgroups", pid);
|
||||
sprintf(line, "deny");
|
||||
write_file(path, line);
|
||||
|
||||
sprintf(path, "/proc/%d/gid_map", pid);
|
||||
sprintf(line, "0 %d 1\n1 %d 1000\n", gid, unprivileged_gid);
|
||||
write_file(path, line);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc,char** argv) {
|
||||
srand(time(NULL));
|
||||
struct params params;
|
||||
memset(¶ms, 0, sizeof(struct params));
|
||||
struct limits limits;
|
||||
memset(&limits, 0, sizeof(struct limits));
|
||||
parse_args(argc, argv, ¶ms, &limits);
|
||||
prepare_cgroup(&limits);
|
||||
//exit(0);
|
||||
if(setuid(0)) die("need to be run as root");
|
||||
if(setgid(0)) die("need to be run as root");
|
||||
if (pipe(params.fd) < 0) exit(0);
|
||||
int clone_flags = SIGCHLD | CLONE_NEWUTS | CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWIPC | CLONE_NEWCGROUP/* | CLONE_NEWTIME*/;
|
||||
int nsrun_pid=clone(nsrun,nmstack+STACK_SIZE,clone_flags,¶ms);
|
||||
int pipe=params.fd[1];
|
||||
//sleep(1);
|
||||
prepare_userns(nsrun_pid);
|
||||
if (nsrun_pid<0) {die("faled to clone");}
|
||||
add_to_cgroup(nsrun_pid);
|
||||
if (write(pipe, "OK", 2) != 2) die("Failed to write to pipe: %m");//report readiness
|
||||
if (waitpid(nsrun_pid, NULL, 0) == -1) die("Failed to wait pid %d: %m\n", nsrun_pid);
|
||||
//int pipe=params.fd[1];
|
||||
remove_cgroup();
|
||||
return 0;
|
||||
}
|
22
starter/util.h
Normal file
22
starter/util.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
//#ifndef ISOLATE_UTIL_H
|
||||
//#define ISOLATE_UTIL_H
|
||||
|
||||
static void die(const char *fmt, ...)
|
||||
{
|
||||
va_list params;
|
||||
|
||||
va_start(params, fmt);
|
||||
vfprintf(stderr, fmt, params);
|
||||
va_end(params);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void write_file(char* path, char* line)
|
||||
{
|
||||
FILE *f = fopen(path, "w");
|
||||
if (f == NULL) {die("Failed to open file %s: %m\n", path);}
|
||||
if (fwrite(line, 1, strlen(line), f) < 0) {die("Failed to write to file %s:\n", path);}
|
||||
if (fclose(f) != 0) {die("Failed to close file %s: %m\n", path);}
|
||||
}
|
||||
|
||||
//#endif //ISOLATE_UTIL_H
|
Loading…
Reference in a new issue