Showing
10 changed files
with
522 additions
and
0 deletions
.gitignore
0 → 100644
Makefile
0 → 100644
README.md
0 → 100644
| 1 | +# LXC-Coding | ||
| 2 | + | ||
| 3 | +Playground for user namespaces and other Linux Container stuff. | ||
| 4 | + | ||
| 5 | +## Requirements | ||
| 6 | + | ||
| 7 | +A proper kernel... | ||
| 8 | + | ||
| 9 | +## License | ||
| 10 | + | ||
| 11 | +> This program is free software: you can redistribute it and/or modify | ||
| 12 | +> it under the terms of the GNU General Public License as published by | ||
| 13 | +> the Free Software Foundation, either version 3 of the License, or | ||
| 14 | +> (at your option) any later version. | ||
| 15 | +> | ||
| 16 | +> This program is distributed in the hope that it will be useful, | ||
| 17 | +> but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | +> MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 19 | +> GNU General Public License for more details. | ||
| 20 | +> | ||
| 21 | +> You should have received a copy of the GNU General Public License | ||
| 22 | +> along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 23 | + | ||
| 24 | +## Author | ||
| 25 | + | ||
| 26 | +Georg Hopp <<georg@steffers.org>> |
ansible.cfg
0 → 100644
demo_userns.c
0 → 100644
| 1 | +/* | ||
| 2 | + * demo_userns.c | ||
| 3 | + * | ||
| 4 | + * Copyright 2013, Michael Kerrisk | ||
| 5 | + * Licensed under GNU General Public License v2 or later | ||
| 6 | + * | ||
| 7 | + * Demonstrate the use of the clone() CLONE_NEWUSER flag. | ||
| 8 | + * | ||
| 9 | + * Link with "-lcap" and make sure that the "libcap-devel" (or | ||
| 10 | + * similar) package is installed on the system. | ||
| 11 | + */ | ||
| 12 | + | ||
| 13 | +#define _GNU_SOURCE | ||
| 14 | + | ||
| 15 | +#include <sys/capability.h> | ||
| 16 | +#include <sys/wait.h> | ||
| 17 | +#include <sched.h> | ||
| 18 | +#include <stdio.h> | ||
| 19 | +#include <stdlib.h> | ||
| 20 | +#include <unistd.h> | ||
| 21 | + | ||
| 22 | +#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ | ||
| 23 | +} while (0) | ||
| 24 | + | ||
| 25 | +static int /* Startup function for cloned child */ | ||
| 26 | +childFunc(void *arg) | ||
| 27 | +{ | ||
| 28 | + cap_t caps; | ||
| 29 | + | ||
| 30 | + for (;;) { | ||
| 31 | + printf("eUID = %ld; eGID = %ld; ", | ||
| 32 | + (long) geteuid(), (long) getegid()); | ||
| 33 | + | ||
| 34 | + caps = cap_get_proc(); | ||
| 35 | + printf("capabilities: %s\n", cap_to_text(caps, NULL)); | ||
| 36 | + | ||
| 37 | + if (arg == NULL) | ||
| 38 | + break; | ||
| 39 | + | ||
| 40 | + sleep(5); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + return 0; | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +#define STACK_SIZE (1024 * 1024) | ||
| 47 | + | ||
| 48 | +static char child_stack[STACK_SIZE]; /* Space for child's stack */ | ||
| 49 | + | ||
| 50 | +int | ||
| 51 | +main(int argc, char *argv[]) | ||
| 52 | +{ | ||
| 53 | + pid_t pid; | ||
| 54 | + | ||
| 55 | + /* Create child; child commences execution in childFunc() */ | ||
| 56 | + | ||
| 57 | + pid = clone(childFunc, child_stack + STACK_SIZE, /* Assume stack | ||
| 58 | + grows downward */ | ||
| 59 | + CLONE_NEWUSER | SIGCHLD, argv[1]); | ||
| 60 | + if (pid == -1) | ||
| 61 | + errExit("clone"); | ||
| 62 | + | ||
| 63 | + /* Parent falls through to here. Wait for child. */ | ||
| 64 | + | ||
| 65 | + if (waitpid(pid, NULL, 0) == -1) | ||
| 66 | + errExit("waitpid"); | ||
| 67 | + | ||
| 68 | + exit(EXIT_SUCCESS); | ||
| 69 | +} | ||
| 70 | + | ||
| 71 | +// vim: ft=c cindent ts=4 sw=4 sts=4: |
demo_userns.o
0 → 100644
No preview for this file type
lxc-test.py
0 → 100644
| 1 | +import lxc | ||
| 2 | +import os | ||
| 3 | +import sys | ||
| 4 | +import hashlib | ||
| 5 | +import json | ||
| 6 | +import getpass | ||
| 7 | +import subprocess | ||
| 8 | + | ||
| 9 | +scriptpath = os.path.dirname(os.path.realpath(__file__)) | ||
| 10 | + | ||
| 11 | +def lxc_start(cont): | ||
| 12 | + if (os.getuid() != 0): | ||
| 13 | + uname = getpass.getuser() | ||
| 14 | + subprocess.call([scriptpath + '/create_user_cgroup']); | ||
| 15 | + for i in os.listdir('/sys/fs/cgroup'): | ||
| 16 | + if (i == 'openrc'): | ||
| 17 | + continue | ||
| 18 | + with open('/sys/fs/cgroup/'+i+'/'+uname+'/tasks', 'a') as f: | ||
| 19 | + f.write(str(os.getpid())+'\n'); | ||
| 20 | + | ||
| 21 | + cont.start(); | ||
| 22 | + | ||
| 23 | +def basic(cont): | ||
| 24 | + def work(data): | ||
| 25 | + print data | ||
| 26 | + sys.stdout.flush() | ||
| 27 | + sys.exit(0) | ||
| 28 | + | ||
| 29 | + lxc_read, lxc_write = os.pipe() | ||
| 30 | + | ||
| 31 | + cont.attach_wait(work, 'testtest', stdout=lxc_write) | ||
| 32 | + os.close(lxc_write) | ||
| 33 | + | ||
| 34 | + lxc_readfd = os.fdopen(lxc_read) | ||
| 35 | + data = lxc_readfd.readline().rstrip('\n') | ||
| 36 | + | ||
| 37 | + lxc_readfd.close() | ||
| 38 | + | ||
| 39 | + print data | ||
| 40 | + | ||
| 41 | +def basic2(cont): | ||
| 42 | + def work(data): | ||
| 43 | + print data | ||
| 44 | + sys.stdout.flush() | ||
| 45 | + sys.exit(0) | ||
| 46 | + | ||
| 47 | + lxc_read, lxc_write = os.pipe() | ||
| 48 | + | ||
| 49 | + cont.attach(work, 'testtest', stdout=lxc_write) | ||
| 50 | + os.close(lxc_write) | ||
| 51 | + | ||
| 52 | + lxc_readfd = os.fdopen(lxc_read) | ||
| 53 | + data = lxc_readfd.readline().rstrip('\n') | ||
| 54 | + | ||
| 55 | + lxc_readfd.close() | ||
| 56 | + | ||
| 57 | + print data | ||
| 58 | + | ||
| 59 | +def copy(cont, src, dest): | ||
| 60 | + def work(dest): | ||
| 61 | + infno = sys.stdin.fileno() | ||
| 62 | + data = os.read(infno, 100) | ||
| 63 | + print(data) | ||
| 64 | + | ||
| 65 | + lxc_read, lxc_write = os.pipe() | ||
| 66 | + | ||
| 67 | + pid = cont.attach(work, 'foo', stdin=lxc_read) | ||
| 68 | + os.close(lxc_read) | ||
| 69 | + | ||
| 70 | + os.write(lxc_write, 'fooooobadooooo') | ||
| 71 | + | ||
| 72 | + os.close(lxc_write) | ||
| 73 | + os.waitpid(pid, 0) | ||
| 74 | + | ||
| 75 | +def copy2(cont, src, dest): | ||
| 76 | + bufsize = 2048 | ||
| 77 | + | ||
| 78 | + def work(dest): | ||
| 79 | + infno = sys.stdin.fileno() | ||
| 80 | + with open(dest, 'w') as outfile: | ||
| 81 | + data = os.read(infno, bufsize) | ||
| 82 | + while (bufsize == len(data)): | ||
| 83 | + outfile.write(data) | ||
| 84 | + data = os.read(infno, bufsize) | ||
| 85 | + outfile.write(data) | ||
| 86 | + | ||
| 87 | + lxc_read, lxc_write = os.pipe() | ||
| 88 | + | ||
| 89 | + pid = cont.attach(work, dest, stdin=lxc_read) | ||
| 90 | + os.close(lxc_read) | ||
| 91 | + | ||
| 92 | + with open(src, 'r') as infile: | ||
| 93 | + data = infile.read(bufsize) | ||
| 94 | + while (bufsize == len(data)): | ||
| 95 | + os.write(lxc_write, data) | ||
| 96 | + data = infile.read(bufsize) | ||
| 97 | + os.write(lxc_write, data) | ||
| 98 | + | ||
| 99 | + os.close(lxc_write) | ||
| 100 | + os.waitpid(pid, 0) | ||
| 101 | + | ||
| 102 | +def verify(cont, src, dest): | ||
| 103 | + bufsize = 2048 | ||
| 104 | + | ||
| 105 | + def work(dest): | ||
| 106 | + digest = '' | ||
| 107 | + try: | ||
| 108 | + chk = hashlib.sha1() | ||
| 109 | + with open(dest, 'r') as dfile: | ||
| 110 | + data = dfile.read(bufsize) | ||
| 111 | + while(bufsize == len(data)): | ||
| 112 | + chk.update(data) | ||
| 113 | + data = dfile.read(bufsize) | ||
| 114 | + chk.update(data) | ||
| 115 | + digest = chk.hexdigest() | ||
| 116 | + except: | ||
| 117 | + pass | ||
| 118 | + sys.stdout.write(digest) | ||
| 119 | + sys.stdout.flush() | ||
| 120 | + | ||
| 121 | + lxc_read, lxc_write = os.pipe() | ||
| 122 | + | ||
| 123 | + pid = cont.attach(work, dest, stdout=lxc_write) | ||
| 124 | + os.close(lxc_write) | ||
| 125 | + | ||
| 126 | + chk = hashlib.sha1() | ||
| 127 | + with open(src, 'r') as dfile: | ||
| 128 | + data = dfile.read(bufsize) | ||
| 129 | + while (bufsize == len(data)): | ||
| 130 | + chk.update(data) | ||
| 131 | + data = dfile.read(bufsize) | ||
| 132 | + chk.update(data) | ||
| 133 | + | ||
| 134 | + lxc_chk = os.read(lxc_read, 100) | ||
| 135 | + os.close(lxc_read) | ||
| 136 | + | ||
| 137 | + return json.dumps({ | ||
| 138 | + 'equal': lxc_chk == chk.hexdigest(), | ||
| 139 | + 'local_sum': chk.hexdigest(), | ||
| 140 | + 'container_sum': lxc_chk}) | ||
| 141 | + | ||
| 142 | +cont = lxc.Container('ansible_test') | ||
| 143 | + | ||
| 144 | +assert cont.defined | ||
| 145 | + | ||
| 146 | +started = False | ||
| 147 | + | ||
| 148 | +if not cont.running: | ||
| 149 | + lxc_start(cont) | ||
| 150 | + if not cont.wait('RUNNING', timeout=5): | ||
| 151 | + raise EnvironmentError(0x01, '[lxc] unable to start container', cont) | ||
| 152 | + started = True | ||
| 153 | + | ||
| 154 | +#basic(cont) | ||
| 155 | +#basic2(cont) | ||
| 156 | +#copy(cont, 'verkauf.jpg', '/tmp/foo') | ||
| 157 | + | ||
| 158 | +print __file__ | ||
| 159 | +print os.path.realpath(__file__) | ||
| 160 | +print os.path.dirname(os.path.realpath(__file__)) | ||
| 161 | + | ||
| 162 | +vres = json.loads(verify(cont, 'verkauf.jpg', '/tmp/foo')) | ||
| 163 | +if not vres['equal']: | ||
| 164 | + print "copy file" | ||
| 165 | + copy2(cont, 'verkauf.jpg', '/tmp/foo') | ||
| 166 | +else: | ||
| 167 | + print "files are equal" | ||
| 168 | + | ||
| 169 | +if started: | ||
| 170 | + cont.shutdown(5) | ||
| 171 | + |
lxc_copy.py
0 → 100644
| 1 | +#!/usr/bin/python | ||
| 2 | +# | ||
| 3 | + | ||
| 4 | +# (c) 2014, Pavel Antonov <antonov@adwz.ru> | ||
| 5 | +# | ||
| 6 | +# This file is part of Ansible | ||
| 7 | +# | ||
| 8 | +# This module is free software: you can redistribute it and/or modify | ||
| 9 | +# it under the terms of the GNU General Public License as published by | ||
| 10 | +# the Free Software Foundation, either version 3 of the License, or | ||
| 11 | +# (at your option) any later version. | ||
| 12 | +# | ||
| 13 | +# This software is distributed in the hope that it will be useful, | ||
| 14 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | +# GNU General Public License for more details. | ||
| 17 | +# | ||
| 18 | +# You should have received a copy of the GNU General Public License | ||
| 19 | +# along with this software. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | + | ||
| 21 | +###################################################################### | ||
| 22 | + | ||
| 23 | +DOCUMENTATION = ''' | ||
| 24 | +--- | ||
| 25 | +module: lxc_copy | ||
| 26 | +author: Georg Hopp | ||
| 27 | +version_added: "1.9" | ||
| 28 | +short_description: copy files in an lxc | ||
| 29 | +description: | ||
| 30 | + - Very simple first start to copy files from host to lxc container | ||
| 31 | + even in non privileged containers | ||
| 32 | +options: | ||
| 33 | + name: | ||
| 34 | + description: | ||
| 35 | + - Lxc container name to use | ||
| 36 | + required: true | ||
| 37 | + default: null | ||
| 38 | + aliases: [] | ||
| 39 | + src: | ||
| 40 | + description: | ||
| 41 | + - Path to source file | ||
| 42 | + required: true | ||
| 43 | + default: null | ||
| 44 | + aliases: [] | ||
| 45 | + dest: | ||
| 46 | + description: | ||
| 47 | + - Path to file in container | ||
| 48 | + required: true | ||
| 49 | + default: null | ||
| 50 | + aliases: [] | ||
| 51 | +requirements: [ "lxc", "create_user_cgroup" ] | ||
| 52 | +''' | ||
| 53 | + | ||
| 54 | +EXAMPLES = ''' | ||
| 55 | +Copy a file from the host in the container: | ||
| 56 | + | ||
| 57 | +- hosts: localhost | ||
| 58 | + tasks: | ||
| 59 | + - name: copy host file to container | ||
| 60 | + lxc_copy: | ||
| 61 | + name: ansible_test | ||
| 62 | + src: nicefile | ||
| 63 | + dest: /tmp/verynicefile | ||
| 64 | +''' | ||
| 65 | + | ||
| 66 | +try: | ||
| 67 | + import lxc | ||
| 68 | + import sys | ||
| 69 | + import os | ||
| 70 | + import json | ||
| 71 | + import getpass | ||
| 72 | + import hashlib | ||
| 73 | + from contextlib import contextmanager | ||
| 74 | +except ImportError, e: | ||
| 75 | + print "failed=True msg='failed to import python module: %s'" % e | ||
| 76 | + sys.exit(1) | ||
| 77 | + | ||
| 78 | +class LxcManager: | ||
| 79 | + @contextmanager | ||
| 80 | + def lxc_started(self): | ||
| 81 | + started = self.container.running | ||
| 82 | + | ||
| 83 | + if not started and not self.container.running: | ||
| 84 | + if (os.getuid() != 0): | ||
| 85 | + uname = getpass.getuser() | ||
| 86 | + subprocess.call(["create_user_cgroup"]); | ||
| 87 | + for i in os.listdir('/sys/fs/cgroup'): | ||
| 88 | + if (i == 'openrc'): | ||
| 89 | + continue | ||
| 90 | + with open('/sys/fs/cgroup/'+i+'/'+uname+'/tasks', 'a') as f: | ||
| 91 | + f.write(str(os.getpid())+'\n'); | ||
| 92 | + | ||
| 93 | + self.container.start(); | ||
| 94 | + | ||
| 95 | + yield self | ||
| 96 | + | ||
| 97 | + if not started and self.container.running: | ||
| 98 | + self.container.shutdown(5) | ||
| 99 | + | ||
| 100 | + def __init__(self, module): | ||
| 101 | + self.module = module | ||
| 102 | + self.name = self.module.params.get('name') | ||
| 103 | + self.src = self.module.params.get('src') | ||
| 104 | + self.dest = self.module.params.get('dest') | ||
| 105 | + self.container = lxc.Container(self.name) | ||
| 106 | + self.changed = False | ||
| 107 | + self.log = [] | ||
| 108 | + self.error_msg = None | ||
| 109 | + self.buffer_size = 20480 | ||
| 110 | + | ||
| 111 | + def get_log(self, as_string=True): | ||
| 112 | + return "".join(self.log) if as_string else self.log | ||
| 113 | + | ||
| 114 | + def _verify(self): | ||
| 115 | + def work(dest): | ||
| 116 | + digest = '' | ||
| 117 | + try: | ||
| 118 | + chk = hashlib.sha1() | ||
| 119 | + with open(dest, 'r') as dfile: | ||
| 120 | + data = dfile.read(self.buffer_size) | ||
| 121 | + while(self.buffer_size == len(data)): | ||
| 122 | + chk.update(data) | ||
| 123 | + data = dfile.read(self.buffer_size) | ||
| 124 | + chk.update(data) | ||
| 125 | + digest = chk.hexdigest() | ||
| 126 | + except: | ||
| 127 | + pass | ||
| 128 | + sys.stdout.write(digest) | ||
| 129 | + sys.stdout.flush() | ||
| 130 | + | ||
| 131 | + lxc_read, lxc_write = os.pipe() | ||
| 132 | + | ||
| 133 | + pid = self.container.attach(work, self.dest, stdout=lxc_write) | ||
| 134 | + os.close(lxc_write) | ||
| 135 | + | ||
| 136 | + chk = hashlib.sha1() | ||
| 137 | + with open(self.src, 'r') as dfile: | ||
| 138 | + data = dfile.read(self.buffer_size) | ||
| 139 | + while (self.buffer_size == len(data)): | ||
| 140 | + chk.update(data) | ||
| 141 | + data = dfile.read(self.buffer_size) | ||
| 142 | + chk.update(data) | ||
| 143 | + | ||
| 144 | + lxc_chk = os.read(lxc_read, 100) # read digest from container.... | ||
| 145 | + os.close(lxc_read) | ||
| 146 | + | ||
| 147 | + self.files_equal = lxc_chk == chk.hexdigest() | ||
| 148 | + self.local_sum = chk.hexdigest() | ||
| 149 | + self.container_sum = lxc_chk | ||
| 150 | + | ||
| 151 | + def copy(self): | ||
| 152 | + with self.lxc_started(): | ||
| 153 | + self._verify() | ||
| 154 | + if not self.files_equal: | ||
| 155 | + def work(dest): | ||
| 156 | + infno = sys.stdin.fileno() | ||
| 157 | + with open(dest, 'w') as outfile: | ||
| 158 | + data = os.read(infno, self.buffer_size) | ||
| 159 | + while (self.buffer_size == len(data)): | ||
| 160 | + outfile.write(data) | ||
| 161 | + data = os.read(infno, self.buffer_size) | ||
| 162 | + outfile.write(data) | ||
| 163 | + | ||
| 164 | + lxc_read, lxc_write = os.pipe() | ||
| 165 | + | ||
| 166 | + pid = self.container.attach(work, self.dest, stdin=lxc_read) | ||
| 167 | + os.close(lxc_read) | ||
| 168 | + | ||
| 169 | + with open(self.src, 'r') as infile: | ||
| 170 | + data = infile.read(self.buffer_size) | ||
| 171 | + while (self.buffer_size == len(data)): | ||
| 172 | + os.write(lxc_write, data) | ||
| 173 | + data = infile.read(self.buffer_size) | ||
| 174 | + os.write(lxc_write, data) | ||
| 175 | + | ||
| 176 | + os.close(lxc_write) | ||
| 177 | + os.waitpid(pid, 0) | ||
| 178 | + self.changed = True | ||
| 179 | + | ||
| 180 | + def has_changed(self): | ||
| 181 | + return self.changed | ||
| 182 | + | ||
| 183 | + | ||
| 184 | +def main(): | ||
| 185 | + module = AnsibleModule( | ||
| 186 | + argument_spec = dict( | ||
| 187 | + name = dict(required=True, default=None), | ||
| 188 | + src = dict(required=True, default=None), | ||
| 189 | + dest = dict(required=True, default=None), | ||
| 190 | + ) | ||
| 191 | + ) | ||
| 192 | + | ||
| 193 | + manager = LxcManager(module) | ||
| 194 | + | ||
| 195 | + image_id = None | ||
| 196 | + msg = '' | ||
| 197 | + do_build = False | ||
| 198 | + | ||
| 199 | + manager.copy() | ||
| 200 | + | ||
| 201 | + module.exit_json(failed=False, changed=manager.has_changed(), msg="File copied") | ||
| 202 | + | ||
| 203 | +# import module snippets | ||
| 204 | +from ansible.module_utils.basic import * | ||
| 205 | +if __name__ == '__main__': | ||
| 206 | + main() |
playbook.yml
0 → 100644
| 1 | +--- | ||
| 2 | +- name: test playbook for lxc_copy | ||
| 3 | + hosts: all | ||
| 4 | + tasks: | ||
| 5 | + - name: copy single file in container | ||
| 6 | + lxc_copy: | ||
| 7 | + args: | ||
| 8 | + name: ansible_test | ||
| 9 | + src: files/verkauf.jpg | ||
| 10 | + dest: /tmp/foo | ||
| 11 | + | ||
| 12 | + - name: copy files in container | ||
| 13 | + lxc_copy: | ||
| 14 | + args: | ||
| 15 | + name: ansible_test | ||
| 16 | + src: files/{{ item }} | ||
| 17 | + dest: /tmp/{{ item }} | ||
| 18 | + with_items: ['a', 'b', 'c', 'd', 'e', 'f', 'g'] |
Please
register
or
login
to post a comment