Skip to content

Commit

Permalink
Update README, change cp and mv arguments, move to tun2socks/slirpnet…
Browse files Browse the repository at this point in the history
…stack
  • Loading branch information
blechschmidt committed Dec 27, 2024
1 parent 8f99304 commit 42a58d3
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 49 deletions.
File renamed without changes.
37 changes: 19 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

## Linux Network and Security Sandbox

**This project is still in an early phase of development (pre-alpha). It is not ready for production use
and has [known issues](/doc/issues.md), which are not (yet) documented exhaustively.
Please expect bugs and breaking changes.**
**This project is still in an early phase of development. Please expect bugs and breaking changes.**

Pallium is a Linux network and security sandbox. In contrast to many other sandboxing solutions,
pallium can provide reasonable protection against Linux kernel exploits
Expand Down Expand Up @@ -48,10 +46,11 @@ Pallium supports [build provenance attestations](https://docs.github.com/en/acti
since v0.1.0-alpha4.
To verify the authenticity of the binary, you can use the [GitHub CLI](https://cli.github.com/):
```shell
gh attestation verify pallium-x86_64-bundle-linux --repo blechschmidt/pallium
gh attestation verify pallium-x86_64-bundle-linux --owner blechschmidt --signer-workflow blechschmidt/pallium/.github/workflows/deploy.yml
```

This ensures that the binary was built by the GitHub Actions CI/CD pipeline and has not been tampered with.
This ensures that the binary was built by the GitHub Actions CI/CD workflow from the code in the repository and has not
been tampered with.

### Automated Installation
Having cloned pallium, it can be installed using the included installation script:
Expand All @@ -75,17 +74,17 @@ This installation method does not require root, but you will need to manually in

The following additional software is required depending on the hop chain and needs to be installed separately.

| Feature | Binaries |
|-------------------------|-------------------------------------------------------------------|
| OpenVPN | openvpn |
| SOCKS | [tun2socks](https://github.com/xjasonlyu/tun2socks) |
| Tor (SOCKS) | tor |
| SSH (SOCKS) | ssh |
| HTTP | [tun2socks](https://github.com/xjasonlyu/tun2socks) |
| DHCP (bridging) | dnsmasq |
| GUI isolation | [Xpra](https://xpra.org/) |
| Unprivileged sandboxing | [slirp4netns](https://github.com/rootless-containers/slirp4netns) |
| Kernel isolation | [gVisor](https://github.com/google/gvisor) |
| Feature | Binaries |
|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|
| OpenVPN | openvpn |
| SOCKS | [tun2socks](https://github.com/xjasonlyu/tun2socks) |
| Tor (SOCKS) | tor |
| SSH (SOCKS) | ssh |
| HTTP | [tun2socks](https://github.com/xjasonlyu/tun2socks) |
| DHCP (bridging) | dnsmasq |
| GUI isolation | [Xpra](https://xpra.org/) |
| Unprivileged sandboxing | [slirpnetstack](https://github.com/tun2proxy/slirpnetstack) (preferred) or [slirp4netns](https://github.com/rootless-containers/slirp4netns) |
| Kernel isolation | [gVisor](https://github.com/google/gvisor) |


## CLI Usage
Expand Down Expand Up @@ -236,8 +235,10 @@ following the profile name instead of opening a shell. The command consists of a
`pallium stop my_profile` stops a session and terminates all programs inside the session.

### cp
`pallium cp my_profile:/tmp/file.txt /tmp/file.txt` copies a file from within the `my_profile` sandbox to the host. The
command supports recursive copying of directories through the `-r` flag.
`pallium cp --from my_profile /file/in/sandbox.txt /file/on/host.txt` copies a file from within the `my_profile` sandbox
to the host. The command supports recursive copying of directories through the `-r` flag.
With `pallium cp --from sandbox1 /file/in/sandbox1.txt --to sandbox2 /file/in/sandbox2.txt`, a file can be
moved between sandboxes. Without `--from` or `--to`, the respective arguments refer to paths on the host.

### mv
The `pallium mv` command works analogously to the copy command but moves files and directories instead of copying them.
Expand Down
2 changes: 1 addition & 1 deletion dist/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ install_go() {

build_slirpnetstack() {
(
git clone https://github.com/blechschmidt/slirpnetstack slirpnetstack-repo
git clone https://github.com/tun2proxy/slirpnetstack slirpnetstack-repo
cd slirpnetstack-repo
go build
)
Expand Down
12 changes: 0 additions & 12 deletions doc/issues.md

This file was deleted.

29 changes: 12 additions & 17 deletions pallium/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,31 +93,26 @@ def pallium_run(args):

def parse_path(path: str, session: int, sandbox_name: typing.Optional[str] = None):
"""
Parse an scp-style path to a usable path to copy/move from/to.
Parse path to a usable path to copy/move from/to.
@param path: The path.
@param session: The session index.
@param sandbox_name: If the sandbox name is None, the path is a host path that is returned as is.
@return: A full path.
"""
if sandbox_name is None:
path = path.rstrip('/')
if ':' not in path:
return path
split = path.split(':', maxsplit=1)
if '/' in split[0]: # The part before the colon is not a sandbox name.
return path
config_path = get_config_path(split[0])
profile = Profile.from_file(config_path)
path = split[1]
else:
config_path = get_config_path(sandbox_name)
profile = Profile.from_file(config_path)
return path
config_path = get_config_path(sandbox_name)
profile = Profile.from_file(config_path)
session = profile.get_session(session)
pid = session.sandbox_pid
return '/proc/%d/root' % pid + path


def pallium_cp(args):
src_path = parse_path(args.src, args.session, args.from_sandbox)
dst_path = parse_path(args.dst, args.session, args.to)
dst_path = parse_path(args.dst, args.session, args.to_sandbox)
if os.path.isdir(dst_path):
dst_path = os.path.join(dst_path, os.path.basename(src_path))
if args.recursive:
Expand All @@ -128,7 +123,7 @@ def pallium_cp(args):

def pallium_mv(args):
src_path = parse_path(args.src, args.session, args.from_sandbox)
dst_path = parse_path(args.dst, args.session, args.to)
dst_path = parse_path(args.dst, args.session, args.to_sandbox)
shutil.move(src_path, dst_path)


Expand Down Expand Up @@ -332,14 +327,14 @@ def main(args=None):
parser_cp.add_argument('dst', help='Destination path.')
parser_cp.add_argument('-r', '--recursive', help='Copy directories recursively.', action='store_true')
parser_cp.add_argument('--from', help='Source sandbox.', dest='from_sandbox') # from is a reserved keyword
parser_cp.add_argument('--to', help='Destination sandbox.')
parser_cp.add_argument('--to', help='Destination sandbox.', dest='to_sandbox') # analogous to --from
parser_add_session(parser_cp)

parser_mv = main_cmd_parser.add_parser('mv', help='Move a file or directory from or to a sandbox.')
parser_mv.add_argument('src', help='Source path.')
parser_mv.add_argument('dst', help='Destination path.')
parser_mv.add_argument('--from', help='Source sandbox.', dest='from_sandbox') # from is a reserved keyword
parser_mv.add_argument('--to', help='Destination sandbox.')
parser_mv.add_argument('--to', help='Destination sandbox.', dest='to_sandbox') # analogous to --from
parser_add_session(parser_mv)

main_cmd_parser.add_parser('list', help='List profiles.')
Expand Down
16 changes: 15 additions & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,20 +125,34 @@ def test_mv(self):
}
}

with PalliumTestSession(profile) as session, tempfile.NamedTemporaryFile() as tmp:
with (PalliumTestSession(profile) as session, tempfile.NamedTemporaryFile() as tmp,
tempfile.TemporaryDirectory() as tmpdir):
with open(os.path.join(tmpdir, 'hello.txt'), 'w') as f:
f.write('world')

with open(tmp.name, 'w') as f:
f.write('hello world')

exec_result = session.exec(['whoami'])
assert exec_result == 'johndoe'

# Test mv command
subprocess.call(['pallium', 'mv', tmp.name, '--to', session.profile_path, '/home/johndoe/hello.txt'])
exec_result = session.exec(['cat', '/home/johndoe/hello.txt'])
assert exec_result == 'hello world'
assert not os.path.exists(tmp.name)

subprocess.call(['pallium', 'mv', '/home/johndoe/hello.txt', tmp.name, '--from', session.profile_path])
with open(tmp.name, 'r') as f:
assert f.read() == 'hello world'

# Test cp -r command
subprocess.call(['pallium', 'cp', '-r', tmpdir, '--to', session.profile_path, '/home/johndoe/testdir'])
exec_result = session.exec(['cat', '/home/johndoe/testdir/hello.txt'])
assert exec_result == 'world'
assert os.path.isdir(tmpdir)
assert os.path.isfile(os.path.join(tmpdir, 'hello.txt'))

def test_port_forwarding(self):
profile = {
'network': {
Expand Down

0 comments on commit 42a58d3

Please sign in to comment.