Skip to content

Commit

Permalink
Various improvements. See changelog.txt.
Browse files Browse the repository at this point in the history
  • Loading branch information
parke committed Jun 28, 2021
1 parent a512249 commit 8be59c5
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 44 deletions.
10 changes: 8 additions & 2 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@


20210627
Implemented environment passthru via -e or --env.
Added 'e' and 'w' indicators to command prompt.
Omit the newroot-option to remain in the external mount namespace.


20210624

Fix GitHub issue #3, as follows:
If remount readonly fails, retry with different flags until success.
Split Lxroot :: env() into two parts. Second part happens after umount2().
Disabled some poorly-formed / overly-restrictive unit tests.
Split Lxroot::env() into two parts. Second part happens after umount2().
Disabled some poorly-formed (i.e. overly-restrictive) unit tests.


20210621
Expand Down
85 changes: 53 additions & 32 deletions lxroot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
// Distributed under GPLv3 (see end of file) WITHOUT ANY WARRANTY.


#define LXROOT_VERSION "0.0.20210624"
#define LXROOT_VERSION "0.0.20210628"


const char * help = // xxhe ------------------------------------- help
"\n"
"usage: lxroot [mode] newroot [options] [--] [command [arg ...] ]\n\n"
"usage: lxroot [[mode] newroot] [options] [--] [command [arg ...] ]\n\n"

"options\n"
" -short one or more short options\n"
Expand All @@ -35,11 +35,11 @@ const char * help2 = // xxhe ----------------------------------- help2

// short options ( for plannig purposes only, possibly inaccurate )
// . . . . . .
// a f hostfs(?) k p PID(?) u UID(?) z
// b g l q v verbose
// c h hostname? m MOUNT(?) r root w write
// d dbus(?) i n NET s /sys(?) x x11
// e env(?) j o t y
// a - f - hostfs k - p - PID u - UID z
// b - g - l - q - v - verbose
// c - h - hstname m - MOUNT r root w write
// d - dbus i - n NET s - /sys x x11
// e environ j - o - t y
//
// A F K P U Z
// B G L Q V
Expand All @@ -48,8 +48,8 @@ const char * help2 = // xxhe ----------------------------------- help2
// E J O T Y
//
// long options ( for plannig purposes only, possibly inaccurate )
// dbus env help help-more network pid pulseaudio root trace uid
// verbose version write x11
// dbus env help help-more network pid pulseaudio root trace uid
// verbose version write x11


const char * help_more = // xxlo ----------------------------- help_more
Expand All @@ -62,14 +62,15 @@ const char * help_more = // xxlo ----------------------------- help_more

"SHORT OPTIONS\n\n"

" e import (almost) all external environment variables\n"
" n allow network access (CLONE_NEWNET = 0)\n"
" r simulate root user (map uid and gid to zero)\n"
" w allow full write access to all read-auto binds\n"
" x allow X11 access (bind /tmp/.X11-unix and set $DISPLAY)\n\n"

"LONG OPTIONS\n\n"

// " --env import all environment variables\n"
" --env import (almost) all external environment variables\n"
" --help display help\n"
" --help-more display more help\n"
" --network allow network access (CLONE_NEWNET = 0)\n"
Expand Down Expand Up @@ -121,9 +122,13 @@ const char * help_more = // xxlo ----------------------------- help_more
"Note that the newroot, full-overlay, and partial-overlay options all\n"
"have the same form, namely: [mode] path\n\n"

"The first option of this form is the newroot option. The newroot\n"
"The first option of this form is the newroot-option. The newroot-\n"
"option specfies the newroot.\n\n"

"If no newroot-option is specified, then lxroot will neither bind,\n"
"chroot, nor pivot. This is useful to simulate root or deny network\n"
"access while retaining the current mount namespace.\n\n"

"FULL OVERLAY\n\n"

"Zero or more full-overlay options may occur anywhere before the first\n"
Expand Down Expand Up @@ -1526,6 +1531,7 @@ struct Option_Reader // xxop -------------------- struct Option_Reader


void do_command () { // ------------------ Option_Reader do_command
assert ( command == nullptr );
type = o_command; command |= p; };


Expand Down Expand Up @@ -1557,7 +1563,6 @@ struct Option_Reader // xxop -------------------- struct Option_Reader

void path_or_command () { // -------- Option_Reader path_or_command
// path_or_command
if ( command ) { p == command ? do_command() : path(); return; }
bool found = Lib::is_dir( overlay ? overlay + "/" + arg0 : arg0 );
found ? path() : do_command(); }

Expand Down Expand Up @@ -1708,6 +1713,7 @@ struct Init_Tool { // xxin ------------------------- struct Init_Tool

case o_cd: cd(a); break;
case o_command: command(a); break;
case o_env: mut .opt_env = o_env; break;
case o_full: mut .guestname = a.arg0; break;
case o_help: Lib :: help_print(); break;
case o_help_more: Lib :: help_more_print(); break;
Expand Down Expand Up @@ -1742,15 +1748,20 @@ struct Init_Tool { // xxin ------------------------- struct Init_Tool


static void command ( const Option & a ) { // --- Init_Tool command

/* 20210626 obsolete
if ( st.newroot == nullptr && a.arg0 ) {
printe ( "lxroot directory not found %s", a.arg0.s );
exit ( 1 ); }
*/

mut .command = a.command; }


static void shortopt ( const Option & a ) { // - Init_Tool shortopt
for ( mstr p = a.arg0.s+1 ; *p ; p++ ) {
switch ( *p ) {
case 'e': mut .opt_env = o_env; break;
case 'n': mut .opt_network = o_network; break;
case 'r': mut .opt_root = o_root; break;
case 'w': mut .opt_write = o_write; break;
Expand Down Expand Up @@ -1829,8 +1840,10 @@ struct Env_Tool : Lib { // xxen --------------------- struct Env_Tool
|| st.opt_root == o_root
|| st.opt_x11 == o_x11 ) {
opts = "-";
if ( st.opt_env == o_env ) { opts += "e"; }
if ( st.opt_network == o_network ) { opts += "n"; }
if ( st.opt_root == o_root ) { opts += "r"; }
if ( st.opt_write == o_write ) { opts += "w"; }
if ( st.opt_x11 == o_x11 ) { opts += "x"; } }

ostr ps1 = "PS1=";
Expand Down Expand Up @@ -1863,12 +1876,11 @@ struct Env_Tool : Lib { // xxen --------------------- struct Env_Tool
|| is_busybox ( st.command[0] ) ) { ps1_bash(); } }


/* 20210602 temporarily disabled
void passthru () { // --------------------------- Env_Tool passthru
static void passthru () { // -------------------- Env_Tool passthru
if ( st.opt_env == o_env ) {
for ( Argv p (environ) ; * p ; p++ ) {
env .soft ( * p ); } } }
*/
for ( Argv p (environ) ; * p ; p ++ ) {
trace1 ( "env_tool passthru %s", p[0].s );
mut .env .soft ( * p ); } } }


public:
Expand All @@ -1882,7 +1894,8 @@ struct Env_Tool : Lib { // xxen --------------------- struct Env_Tool
static void after_pivot () { // -------------- Env_Tool after_pivot
shell(); //
argv(); // depends on env.SHELL
ps1(); } // depends on command[0]
ps1(); // depends on command[0]
passthru(); }


}; // end class Env_Tool ------------------------ end class Env_Tool
Expand All @@ -1898,7 +1911,9 @@ class Lxroot : Lib { // xxlx ------------------------- class Lxroot

void init () { // ------------------------------------- Lxroot init
q.options ( Init_Tool :: process );
if ( st.newroot == nullptr ) { help_print ( 1 ); } }
// 20210626 obsolete
// if ( st.newroot == nullptr ) { help_print ( 1 ); } }
return; }


void unshare () { // ------------------------------- Lxroot unshare
Expand Down Expand Up @@ -1940,6 +1955,7 @@ class Lxroot : Lib { // xxlx ------------------------- class Lxroot


void bind () { // ------------------------------------- Lxroot bind
if ( st.newroot == nullptr ) { return; }
q.binds ( [](auto b) {
sys .bind ( b.newroot_dst, b.src ); } );
// note /proc is mounted later on in Lxroot :: proc().
Expand All @@ -1959,7 +1975,8 @@ class Lxroot : Lib { // xxlx ------------------------- class Lxroot

static void expose_path // ----------------------- Lxroot expose_path
( str path, opt readauto = o_none ) {
if ( path == nullptr ) { return; }
if ( st.newroot == nullptr ) { return; }
if ( path == nullptr ) { return; }
mfrag parent;
opt mode;
q.calculate_parent ( path, parent, mode );
Expand Down Expand Up @@ -2009,7 +2026,11 @@ class Lxroot : Lib { // xxlx ------------------------- class Lxroot


void pivot_prepare ( str pivot ) { // -------- Lxroot pivot_prepare
if ( pivot == nullptr ) { die1 ( "pivot_preapre pivot is nullptr" ); }

// 20210626 obsolete
//if( pivot == nullptr ) { die1 ( "pivot_preapre pivot is nullptr" ); }


// verify that pivot has at least one sub-direcotry (for put_old)
assert ( put_old == nullptr );
q.scandir ( pivot, [&](auto e) {
Expand All @@ -2020,6 +2041,7 @@ class Lxroot : Lib { // xxlx ------------------------- class Lxroot


void pivot () { // ----------------------------------- Lxroot pivot
if ( st.newroot == nullptr ) { return; }
pivot_prepare ( st.newroot );
sys .pivot ( st.newroot, st.newroot + put_old );
sys .chdir ( "/" );
Expand Down Expand Up @@ -2054,9 +2076,11 @@ class Lxroot : Lib { // xxlx ------------------------- class Lxroot
// 20201213 fork()ing early complicates debugging with gdb.
// Perhaps I should implement fork()ing both early and late?

if ( st.newroot ) {
sys .mount ( "proc", "/proc", "proc" );
sys .umount2 ( put_old.s, MNT_DETACH ); } }
if ( st.newroot ) { sys .mount ( "proc", "/proc", "proc" ); } }


void umount2 () { // ------------------------------- Lxroot umount2
if ( st.newroot ) { sys .umount2 ( put_old.s, MNT_DETACH ); } }


void chdir () { // ----------------------------------- Lxroot chdir
Expand All @@ -2070,21 +2094,17 @@ class Lxroot : Lib { // xxlx ------------------------- class Lxroot
if ( st .newroot && is_dir ( home ) ) { sys .chdir ( home ); } }


void umount2 () { // ------------------------------- Lxroot umount2
// someday I may move the the call to sys.umount2() to here.
return; }


void xray () { // ------------------------------------- Lxroot xray

if ( global_opt_trace == o_trace ) {

printe ( "\n" );
printe ( "xray uid %d %d\n", getuid(), getgid() );
printe ( "xray cwd %s\n", getcwd().s );
Argv ( st .env .data() ) .print ( "xray env " );
printe ( "xray environment\n" );
Argv ( st .env .data() ) .print ( "xray env " );
printe ( "xray command\n" );
st .command .print ( "xray cmd " );
st .command .print ( "xray cmd " );

printe ( "\n" );
printe ( "xray binds\n" );
Expand All @@ -2107,7 +2127,7 @@ class Lxroot : Lib { // xxlx ------------------------- class Lxroot
unshare(); // before uid_map and bind
uid_map(); // after unshare
bind(); // after unshare
fgetpwent(); // after bind
fgetpwent(); // after bind, before expose
expose(); // after fgetpwent, before remount
remount(); // after expose
options(); // late, before pivot, after remount
Expand All @@ -2119,6 +2139,7 @@ class Lxroot : Lib { // xxlx ------------------------- class Lxroot
/* pivot to umount2 should be as tight as possible */
env(); // after pivot, therefore after umount2
chdir(); // after umount2 ( because put_old may shadow $HOME !! )
xray();
exec(); // last
return 1; } // exec failed!

Expand Down
20 changes: 13 additions & 7 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@

Lxroot is a lightweight alternative to chroot, Docker, and other software virtualization tools.

Lxroot allows a non-root user to quickly and easily create "chroot-style" virtual software environments (Linux namespaces), and then run one or more programs (a "guest userland") inside those namespaces.
Lxroot allows a non-root user to quickly and easily create a "chroot-style" virtual software environment (via Linux namespaces), and then run one or more programs (a "guest userland") inside that environment.

For example, with Lxroot a non-root user can...

- simultaneously run multiple, different guest userlands on a single Linux host
- run, as just one example, an Arch Linux userland on an Ubuntu Linux host
- run, for example, an Arch Linux userland on an Ubuntu Linux host
- run a legacy userland on a modern host
- run software in an "altered version" of the host system itself
- run any given graphical X11 client (from any guest userland) on the host's X11 server
- create clean, controlled, and isolated guest environments for installing, building, and/or running software packages
- create a custom userland for developing software and/or building software packages
- control (or test) the installation (or upgrade) of software packages, as a non-root user, by installing and running the software in an isolated and easily disposable userland
- manage userlands with standard CLI tools: clone them with `rsync`, archive them with `tar` or `mksquashfs`, delete them `rm -rf`
- deny software access to the network
- restrict read and/or write access to specific directories
- share one or more directories between an Lxroot environment and the host system
- share one or more directories between multiple Lxroot environments.
- clone a userland with `rsync`; archive a userland with `tar`
- "upgrade" software by building a new, fresh userland, rather than attempting an in-place upgrade

All without root access!

Expand Down Expand Up @@ -200,7 +200,7 @@ You may also wish to look at the `demo.sh` file.

Below is the output of `lxroot --help-more`:

usage: lxroot [mode] newroot [options] [--] [command [arg ...] ]
usage: lxroot [[mode] newroot] [options] [--] [command [arg ...] ]

options
-short one or more short options
Expand All @@ -223,13 +223,15 @@ Below is the output of `lxroot --help-more`:

SHORT OPTIONS

e import (almost) all external environment variables
n allow network access (CLONE_NEWNET = 0)
r simulate root user (map uid and gid to zero)
w allow full write access to all read-auto binds
x allow X11 access (bind /tmp/.X11-unix and set $DISPLAY)

LONG OPTIONS

--env import (almost) all external environment variables
--help display help
--help-more display more help
--network allow network access (CLONE_NEWNET = 0)
Expand Down Expand Up @@ -281,9 +283,13 @@ Below is the output of `lxroot --help-more`:
Note that the newroot, full-overlay, and partial-overlay options all
have the same form, namely: [mode] path

The first option of this form is the newroot option. The newroot
The first option of this form is the newroot-option. The newroot-
option specfies the newroot.

If no newroot-option is specified, then lxroot will neither bind,
chroot, nor pivot. This is useful to simulate root or deny network
access while retaining the current mount namespace.

FULL OVERLAY

Zero or more full-overlay options may occur anywhere before the first
Expand Down
Loading

0 comments on commit 8be59c5

Please sign in to comment.