commit d370a511005a88fc42c08e735111d941f47b72df Author: Georg Hopp Date: Sat Apr 9 22:22:45 2016 +0200 Add vry basic README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b9af02 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# ignore vim swap files +.*.sw? + +# ignore generated stuff +demo_userns +create_user_cgroup diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a4e12fd --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +PROGRAMS = demo_userns + +demo_userns_OBJS = demo_userns.o +OBJECTS = $(demo_userns_OBJS) + +CFLAGS = -O0 -ggdb -Wall -Werror + +$(PROGRAMS): $(OBJECTS) + $(CC) $(LDFLAGS) -lcap -o $@ $($@_OBJS) + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/README.md b/README.md new file mode 100644 index 0000000..31860fd --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# LXC-Coding + +Playground for user namespaces and other Linux Container stuff. + +## Requirements + +A proper kernel... + +## License + +> This program is free software: you can redistribute it and/or modify +> it under the terms of the GNU General Public License as published by +> the Free Software Foundation, either version 3 of the License, or +> (at your option) any later version. +> +> This program is distributed in the hope that it will be useful, +> but WITHOUT ANY WARRANTY; without even the implied warranty of +> MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +> GNU General Public License for more details. +> +> You should have received a copy of the GNU General Public License +> along with this program. If not, see . + +## Author + +Georg Hopp <> diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..ad887ee --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,11 @@ +[defaults] +inventory = hosts +host_key_checking = False +nocows=0 + +ansible_managed = Ansible managed: {file} on {host} + +# # do redis facts caching +# gathering = smart +# fact_caching = redis +# fact_caching_timeout = 86400 diff --git a/demo_userns.c b/demo_userns.c new file mode 100644 index 0000000..acc4f8d --- /dev/null +++ b/demo_userns.c @@ -0,0 +1,71 @@ +/* + * demo_userns.c + * + * Copyright 2013, Michael Kerrisk + * Licensed under GNU General Public License v2 or later + * + * Demonstrate the use of the clone() CLONE_NEWUSER flag. + * + * Link with "-lcap" and make sure that the "libcap-devel" (or + * similar) package is installed on the system. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ +} while (0) + +static int /* Startup function for cloned child */ +childFunc(void *arg) +{ + cap_t caps; + + for (;;) { + printf("eUID = %ld; eGID = %ld; ", + (long) geteuid(), (long) getegid()); + + caps = cap_get_proc(); + printf("capabilities: %s\n", cap_to_text(caps, NULL)); + + if (arg == NULL) + break; + + sleep(5); + } + + return 0; +} + +#define STACK_SIZE (1024 * 1024) + +static char child_stack[STACK_SIZE]; /* Space for child's stack */ + +int +main(int argc, char *argv[]) +{ + pid_t pid; + + /* Create child; child commences execution in childFunc() */ + + pid = clone(childFunc, child_stack + STACK_SIZE, /* Assume stack + grows downward */ + CLONE_NEWUSER | SIGCHLD, argv[1]); + if (pid == -1) + errExit("clone"); + + /* Parent falls through to here. Wait for child. */ + + if (waitpid(pid, NULL, 0) == -1) + errExit("waitpid"); + + exit(EXIT_SUCCESS); +} + +// vim: ft=c cindent ts=4 sw=4 sts=4: diff --git a/demo_userns.o b/demo_userns.o new file mode 100644 index 0000000..9273ee9 Binary files /dev/null and b/demo_userns.o differ diff --git a/hosts b/hosts new file mode 100644 index 0000000..2302eda --- /dev/null +++ b/hosts @@ -0,0 +1 @@ +localhost ansible_connection=local diff --git a/lxc-test.py b/lxc-test.py new file mode 100644 index 0000000..cbdadc0 --- /dev/null +++ b/lxc-test.py @@ -0,0 +1,171 @@ +import lxc +import os +import sys +import hashlib +import json +import getpass +import subprocess + +scriptpath = os.path.dirname(os.path.realpath(__file__)) + +def lxc_start(cont): + if (os.getuid() != 0): + uname = getpass.getuser() + subprocess.call([scriptpath + '/create_user_cgroup']); + for i in os.listdir('/sys/fs/cgroup'): + if (i == 'openrc'): + continue + with open('/sys/fs/cgroup/'+i+'/'+uname+'/tasks', 'a') as f: + f.write(str(os.getpid())+'\n'); + + cont.start(); + +def basic(cont): + def work(data): + print data + sys.stdout.flush() + sys.exit(0) + + lxc_read, lxc_write = os.pipe() + + cont.attach_wait(work, 'testtest', stdout=lxc_write) + os.close(lxc_write) + + lxc_readfd = os.fdopen(lxc_read) + data = lxc_readfd.readline().rstrip('\n') + + lxc_readfd.close() + + print data + +def basic2(cont): + def work(data): + print data + sys.stdout.flush() + sys.exit(0) + + lxc_read, lxc_write = os.pipe() + + cont.attach(work, 'testtest', stdout=lxc_write) + os.close(lxc_write) + + lxc_readfd = os.fdopen(lxc_read) + data = lxc_readfd.readline().rstrip('\n') + + lxc_readfd.close() + + print data + +def copy(cont, src, dest): + def work(dest): + infno = sys.stdin.fileno() + data = os.read(infno, 100) + print(data) + + lxc_read, lxc_write = os.pipe() + + pid = cont.attach(work, 'foo', stdin=lxc_read) + os.close(lxc_read) + + os.write(lxc_write, 'fooooobadooooo') + + os.close(lxc_write) + os.waitpid(pid, 0) + +def copy2(cont, src, dest): + bufsize = 2048 + + def work(dest): + infno = sys.stdin.fileno() + with open(dest, 'w') as outfile: + data = os.read(infno, bufsize) + while (bufsize == len(data)): + outfile.write(data) + data = os.read(infno, bufsize) + outfile.write(data) + + lxc_read, lxc_write = os.pipe() + + pid = cont.attach(work, dest, stdin=lxc_read) + os.close(lxc_read) + + with open(src, 'r') as infile: + data = infile.read(bufsize) + while (bufsize == len(data)): + os.write(lxc_write, data) + data = infile.read(bufsize) + os.write(lxc_write, data) + + os.close(lxc_write) + os.waitpid(pid, 0) + +def verify(cont, src, dest): + bufsize = 2048 + + def work(dest): + digest = '' + try: + chk = hashlib.sha1() + with open(dest, 'r') as dfile: + data = dfile.read(bufsize) + while(bufsize == len(data)): + chk.update(data) + data = dfile.read(bufsize) + chk.update(data) + digest = chk.hexdigest() + except: + pass + sys.stdout.write(digest) + sys.stdout.flush() + + lxc_read, lxc_write = os.pipe() + + pid = cont.attach(work, dest, stdout=lxc_write) + os.close(lxc_write) + + chk = hashlib.sha1() + with open(src, 'r') as dfile: + data = dfile.read(bufsize) + while (bufsize == len(data)): + chk.update(data) + data = dfile.read(bufsize) + chk.update(data) + + lxc_chk = os.read(lxc_read, 100) + os.close(lxc_read) + + return json.dumps({ + 'equal': lxc_chk == chk.hexdigest(), + 'local_sum': chk.hexdigest(), + 'container_sum': lxc_chk}) + +cont = lxc.Container('ansible_test') + +assert cont.defined + +started = False + +if not cont.running: + lxc_start(cont) + if not cont.wait('RUNNING', timeout=5): + raise EnvironmentError(0x01, '[lxc] unable to start container', cont) + started = True + +#basic(cont) +#basic2(cont) +#copy(cont, 'verkauf.jpg', '/tmp/foo') + +print __file__ +print os.path.realpath(__file__) +print os.path.dirname(os.path.realpath(__file__)) + +vres = json.loads(verify(cont, 'verkauf.jpg', '/tmp/foo')) +if not vres['equal']: + print "copy file" + copy2(cont, 'verkauf.jpg', '/tmp/foo') +else: + print "files are equal" + +if started: + cont.shutdown(5) + diff --git a/lxc_copy.py b/lxc_copy.py new file mode 100644 index 0000000..e5e5b49 --- /dev/null +++ b/lxc_copy.py @@ -0,0 +1,206 @@ +#!/usr/bin/python +# + +# (c) 2014, Pavel Antonov +# +# This file is part of Ansible +# +# This module is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This software is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this software. If not, see . + +###################################################################### + +DOCUMENTATION = ''' +--- +module: lxc_copy +author: Georg Hopp +version_added: "1.9" +short_description: copy files in an lxc +description: + - Very simple first start to copy files from host to lxc container + even in non privileged containers +options: + name: + description: + - Lxc container name to use + required: true + default: null + aliases: [] + src: + description: + - Path to source file + required: true + default: null + aliases: [] + dest: + description: + - Path to file in container + required: true + default: null + aliases: [] +requirements: [ "lxc", "create_user_cgroup" ] +''' + +EXAMPLES = ''' +Copy a file from the host in the container: + +- hosts: localhost + tasks: + - name: copy host file to container + lxc_copy: + name: ansible_test + src: nicefile + dest: /tmp/verynicefile +''' + +try: + import lxc + import sys + import os + import json + import getpass + import hashlib + from contextlib import contextmanager +except ImportError, e: + print "failed=True msg='failed to import python module: %s'" % e + sys.exit(1) + +class LxcManager: + @contextmanager + def lxc_started(self): + started = self.container.running + + if not started and not self.container.running: + if (os.getuid() != 0): + uname = getpass.getuser() + subprocess.call(["create_user_cgroup"]); + for i in os.listdir('/sys/fs/cgroup'): + if (i == 'openrc'): + continue + with open('/sys/fs/cgroup/'+i+'/'+uname+'/tasks', 'a') as f: + f.write(str(os.getpid())+'\n'); + + self.container.start(); + + yield self + + if not started and self.container.running: + self.container.shutdown(5) + + def __init__(self, module): + self.module = module + self.name = self.module.params.get('name') + self.src = self.module.params.get('src') + self.dest = self.module.params.get('dest') + self.container = lxc.Container(self.name) + self.changed = False + self.log = [] + self.error_msg = None + self.buffer_size = 20480 + + def get_log(self, as_string=True): + return "".join(self.log) if as_string else self.log + + def _verify(self): + def work(dest): + digest = '' + try: + chk = hashlib.sha1() + with open(dest, 'r') as dfile: + data = dfile.read(self.buffer_size) + while(self.buffer_size == len(data)): + chk.update(data) + data = dfile.read(self.buffer_size) + chk.update(data) + digest = chk.hexdigest() + except: + pass + sys.stdout.write(digest) + sys.stdout.flush() + + lxc_read, lxc_write = os.pipe() + + pid = self.container.attach(work, self.dest, stdout=lxc_write) + os.close(lxc_write) + + chk = hashlib.sha1() + with open(self.src, 'r') as dfile: + data = dfile.read(self.buffer_size) + while (self.buffer_size == len(data)): + chk.update(data) + data = dfile.read(self.buffer_size) + chk.update(data) + + lxc_chk = os.read(lxc_read, 100) # read digest from container.... + os.close(lxc_read) + + self.files_equal = lxc_chk == chk.hexdigest() + self.local_sum = chk.hexdigest() + self.container_sum = lxc_chk + + def copy(self): + with self.lxc_started(): + self._verify() + if not self.files_equal: + def work(dest): + infno = sys.stdin.fileno() + with open(dest, 'w') as outfile: + data = os.read(infno, self.buffer_size) + while (self.buffer_size == len(data)): + outfile.write(data) + data = os.read(infno, self.buffer_size) + outfile.write(data) + + lxc_read, lxc_write = os.pipe() + + pid = self.container.attach(work, self.dest, stdin=lxc_read) + os.close(lxc_read) + + with open(self.src, 'r') as infile: + data = infile.read(self.buffer_size) + while (self.buffer_size == len(data)): + os.write(lxc_write, data) + data = infile.read(self.buffer_size) + os.write(lxc_write, data) + + os.close(lxc_write) + os.waitpid(pid, 0) + self.changed = True + + def has_changed(self): + return self.changed + + +def main(): + module = AnsibleModule( + argument_spec = dict( + name = dict(required=True, default=None), + src = dict(required=True, default=None), + dest = dict(required=True, default=None), + ) + ) + + manager = LxcManager(module) + + image_id = None + msg = '' + do_build = False + + manager.copy() + + module.exit_json(failed=False, changed=manager.has_changed(), msg="File copied") + +# import module snippets +from ansible.module_utils.basic import * +if __name__ == '__main__': + main() diff --git a/playbook.yml b/playbook.yml new file mode 100644 index 0000000..a676e4e --- /dev/null +++ b/playbook.yml @@ -0,0 +1,18 @@ +--- +- name: test playbook for lxc_copy + hosts: all + tasks: + - name: copy single file in container + lxc_copy: + args: + name: ansible_test + src: files/verkauf.jpg + dest: /tmp/foo + + - name: copy files in container + lxc_copy: + args: + name: ansible_test + src: files/{{ item }} + dest: /tmp/{{ item }} + with_items: ['a', 'b', 'c', 'd', 'e', 'f', 'g']