Skip to content

Instantly share code, notes, and snippets.

@hayajo
Last active July 3, 2016 00:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hayajo/29e46cbffcf8b6b4d7d35fd9080ac513 to your computer and use it in GitHub Desktop.
Save hayajo/29e46cbffcf8b6b4d7d35fd9080ac513 to your computer and use it in GitHub Desktop.
unshare(2) CLONE_NEWUSER|CLONE_NEWNS のサンプルコード

USER_NEAMESPACE(7) を試す

chroot jail な環境で USER_NEAMESPACE(7) を試してみよう!

単純にUSER_NEAMESPACEの動きを確認するだけなら下記コマンドで十分なんだけどね...

vagrant@vagrant:~$ unshare -U -r /bin/bash
root@vagrant:~# id
uid=0(root) gid=0(root) groups=0(root),65534(nogroup)
root@vagrant:~# exit

使い方

dockerでルートファイルシステムを準備する

$ docker export $(docker create alpine hostname) | gzip > alpine.tar.gz

vagrantで検証環境(Ubuntu 16.04 LTS)を準備する

$ vagrant up

検証環境へログインする

$ vagrant ssh

用意したルートファイルシステムを展開する

vagrant@vagrant:~$ mkdir rootfs
vagrant@vagrant:~$ tar xzf /vagrant/alpine.tar.gz -C rootfs

userns_mountns_exec.pl を実行してUSER_NEAMESPACEに入る

vagrant@vagrant:~$ perl /vagrant/userns_mountns_exec.pl rootfs -- /bin/sh -l

ユーザーIDの確認とファイルを作成する

owner/groupはroot/rootとなる。

vagrant:/# id
uid=0(root) gid=0(root) groups=65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),0(root)
vagrant:/# touch hogehoge
vagrant:/# ls -l hogehoge
-rw-r--r--    1 root     root             0 May 11 07:28 hogehoge
vagrant:/# exit

作成したファイルを確認する

owner/groupはvagrant/vagrantとなる。

vagrant@vagrant:~$ ls -l rootfs/hogehoge
-rw-r--r-- 1 vagrant vagrant 0 May 11 07:28 rootfs/hogehoge
#!/usr/bin/env perl
use strict;
use warnings;
use 5.12.0;
require 'syscall.ph';
use File::Spec::Functions qw/catfile catdir/;
use Getopt::Long;
use POSIX;
GetOptions();
die 'rootfs required'
if !@ARGV;
my $rootfs = shift @ARGV;
die "$rootfs does not exists"
if ! -d $rootfs;
my $orig_uid = POSIX::getuid();
my $orig_gid = POSIX::getgid();
# 名前空間を作成する
my $flags = 0x10000000; # CLONE_NEWUSER
$flags |= 0x00020000; # CLONE_NEWNS
die "failed to clone process: $!"
if syscall(&SYS_unshare, $flags) < 0;
# ユーザーマッピング情報を作成する
&write_uidgid_map($$, $orig_uid, $orig_uid);
# USER_NAMESPACES配下でroot/rootになる
POSIX::setuid(0)
or die "failed to setuid: $!";
POSIX::setgid(0)
or die "failed to setgid: $!";
# ネットワーク周りのファイルをrootfs配下にコピーする
system qw|cp -H|, $_, catfile($rootfs, $_)
for qw|/etc/hosts /etc/resolv.conf|;
# ホストの`/proc`, `/sys`, `/dev`をrootfs配下にバインドマウントする
# procfsはPID_NAMESPACE配下では'ない'ので直接マウントできない
system qw|mount -o rbind|, $_, catfile($rootfs, $_)
for qw|/proc /sys /dev|;
chroot $rootfs
or die "failed to chroot to $rootfs: $!";
chdir '/'
or die "failed to chdir to /: $!";
my @cmd = (@ARGV) ? @ARGV : qw|/bin/sh -l|;
printf "execing %s (I am now %d:%d)\n", $cmd[0], POSIX::getuid(), POSIX::getgid();
exec @cmd;
# ユーザーマッピング情報`/proc/{{PID}}/(uid_map|gid_map)`を作成する
sub write_uidgid_map {
my ($pid, $root_uid, $root_gid) = @_;
my $uid_map = catfile('/proc', $pid, 'uid_map');
open my $uid_fh, '>', $uid_map
or die "faild to open $uid_map";
print $uid_fh "0 $root_uid 1\n";
close $uid_fh;
# write 'deny' to `setgroups` before prepare `gid_map`.
# see USER_NAMESPACES(7).
# http://man7.org/linux/man-pages/man7/user_namespaces.7.html
write_setgroups($pid, 'deny');
my $gid_map = catfile('/proc', $pid, 'gid_map');
open my $gid_fh, '>', $gid_map
or die "faild to open $gid_map";
print $gid_fh "0 $root_gid 1\n";
close $gid_fh;
}
# `/proc/{{PID}}/setgroups`にデータを書き込む
sub write_setgroups {
my ($pid, $data) = @_;
my $setgroups = catfile('/proc', $pid, 'setgroups');
open my $fh, '>', $setgroups
or die "failed to open $setgroups";
print $fh $data;
close $fh;
}
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure(2) do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.
# Every Vagrant development environment requires a box. You can search for
# boxes at https://atlas.hashicorp.com/search.
config.vm.box = "boxcutter/ubuntu1604"
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
# config.vm.box_check_update = false
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8080" will access port 80 on the guest machine.
# config.vm.network "forwarded_port", guest: 80, host: 8080
# Create a private network, which allows host-only access to the machine
# using a specific IP.
# config.vm.network "private_network", ip: "192.168.33.10"
# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
# config.vm.network "public_network"
# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
# config.vm.synced_folder "../data", "/vagrant_data"
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
#
# config.vm.provider "virtualbox" do |vb|
# # Display the VirtualBox GUI when booting the machine
# vb.gui = true
#
# # Customize the amount of memory on the VM:
# vb.memory = "1024"
# end
#
# View the documentation for the provider you are using for more
# information on available options.
# Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
# such as FTP and Heroku are also available. See the documentation at
# https://docs.vagrantup.com/v2/push/atlas.html for more information.
# config.push.define "atlas" do |push|
# push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
# end
# Enable provisioning with a shell script. Additional provisioners such as
# Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
# documentation for more information about their specific syntax and use.
# config.vm.provision "shell", inline: <<-SHELL
# sudo apt-get update
# sudo apt-get install -y apache2
# SHELL
end
@hayajo
Copy link
Author

hayajo commented Apr 26, 2016

@hayajo
Copy link
Author

hayajo commented Apr 26, 2016

ubuntuで試せます

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment