Skip to content

Commit 10a9dae

Browse files
authored
dev -> master
2 parents 466975c + d54d247 commit 10a9dae

File tree

12 files changed

+324
-129
lines changed

12 files changed

+324
-129
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
id: tag_step
2222
run: |-
2323
program_version=$(./dist/raptor-cage -V | awk '{print $2}')
24-
tag_name="${program_version}-$(date +%y%m%d%H%M)-$(git rev-parse HEAD | head -c 7)"
24+
tag_name="${program_version}-$(date +%y%m%d%H%M)"
2525
printf "tag_name=${tag_name}\nprogram_version=${program_version}\n" | tee -a "$GITHUB_OUTPUT"
2626
mv dist/raptor-cage.tgz dist/raptor-cage-${tag_name}.tgz
2727
mv dist/raptor-cage.sha256 dist/raptor-cage-${tag_name}.sha256

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ debug/
22
target/
33
dist/
44
.SRCINFO
5+
id_ed25519

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "raptor-cage"
3-
version = "1.0.1"
3+
version = "1.0.2"
44
edition = "2021"
55
license = "CIL-1.0"
66

README.md

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
<p>
66
Run games in a secure sandbox, various native and non-native titles are supported.
77
</p>
8+
<img alt="Downloads" src="https://img.shields.io/github/downloads/RX0FA/raptor-cage/total?style=flat-square&label=DOWNLOADS&labelColor=0567ff&color=696969" />
9+
<img alt="Latest Release" src="https://img.shields.io/github/v/release/RX0FA/raptor-cage?style=flat-square&label=LATEST%20RELEASE&labelColor=0567ff&color=696969" />
10+
<img alt="AUR" src="https://img.shields.io/aur/version/raptor-cage-bin?style=flat-square&label=AUR&labelColor=0567ff&color=696969" />
811
</div>
912

1013
## ⬇️ Installation
@@ -32,22 +35,45 @@ sudo install -Dm755 raptor-cage "/usr/local/bin/rcage"
3235

3336
## 💡 Usage
3437

35-
### Command Line
38+
> ⚠️ Network access is denied by default
39+
40+
### Command Line Examples
3641

3742
```bash
3843
# Run Windows game, runner and prefix paths are relative to Bottles data directory.
3944
rcage run -r soda-9.0-1 -p my_prefix -d ~/games/some_game -b game.exe
4045

4146
# Run native binary, and pass custom parameters.
4247
rcage run -r soda-9.0-1 -p my_prefix -d ~/games/some_game -b native_binary -- --param1
48+
49+
# Mount game path as read-write, mount installer path as read-only, then start interactive shell.
50+
rcage run -r soda-9.0-1 -p my_prefix -d ~/games/some_game:rw -v ~/installers:/installers:
51+
52+
# Mount game path as read-write, mount installer path as read-only, then start "setup.exe".
53+
rcage run -r soda-9.0-1 -p my_prefix -d ~/games/some_game:rw -v ~/installers:/installers: -b /installers/setup.exe
4354
```
4455

56+
### `rcage run` Enum Parameters
57+
58+
* --network-mode:
59+
* `full_access`: no network restrictions at all.
60+
* `restricted_access`: restricts access to some network features such as DNS resolving and SSL certificates, however internet connection is still possible through direct IPs.
61+
* `no_access`: network access is completely blocked, this is the default value if no option is passed.
62+
* --device-access:
63+
* `all`: sandboxed program will have access to all devices i.e., `/dev` is completely exposed inside the sandbox.
64+
* `minimal`: a limited amount of devices are exposed inside the sandbox i.e., GPU, gamepads, etc; this is the default value.
65+
* --upscale-mode:
66+
* `none`: no upscaling applied, this is the default value.
67+
* `dlss`: enable NVIDIA DLSS, **support depends on the wine runner**, raptor-cage only configures the necessary flags.
68+
* `fsr`: enable FSR, it requires additional options separated by `:`, the command value should look like `fsr:mode:strength`. Mode can be one of `none`, `quality`, `balanced`, `performance` or `ultra`; strength is a value that goes from 0 to 5; (example command: `--upscale-mode=fsr:balanced:1`). **Support depends on the wine runner** being used.
69+
* --sync-mode: one of `none`, `fsync` or `esync`. The default value depends on the runner being used.
70+
4571
## 📌 Frequently Asked Questions
4672

4773
* How to enable MangoHud?
4874
Use the `-e MANGOHUD=1` parameter for games that use DXVK and VK3D, other games (OpenGL and WineD3D) may require to prepend `mangohud` before the binary (e.g., `mangohud wine game.exe`).
4975
* What is the difference with Bottles?
50-
Bottles is a GUI to manage Wine/Proton instances and their dependencies, it runs under Flatpak and it uses the same sandbox permissions as Bottles itself, that means that applications that are launched from Bottles have access to everything Bottles has access to (you can see what can Bottles access [here](https://github.com/flathub/com.usebottles.bottles/blob/master/com.usebottles.bottles.yml#L9)), raptor-cage launches applications with a restricted sandbox by default, and allows the user to adjust permissions independently.
76+
Bottles is a GUI to manage Wine/Proton instances and their dependencies, and it runs under Flatpak; applications that are launched from Bottles have access to everything Bottles has access to (you can see what can Bottles access [here](https://github.com/flathub/com.usebottles.bottles/blob/master/com.usebottles.bottles.yml#L9)), raptor-cage launches applications with a restricted sandbox by default, and allows the user to adjust permissions independently.
5177
* Do I need Bottles in order to use raptor-cage?
5278
No, Bottles is not needed, although is highly recommended in order to manage Wine/Proton versions and dependencies. If you don't want to use Bottles, you can download any Wine/Proton version you like, extract it anywhere and choose the respective path when running raptor-cage (`-r`).
5379
* What is the difference with Bubblewrap?
@@ -83,11 +109,10 @@ cargo upgrade --dry-run
83109
#### General
84110

85111
* Some games (like HC2, DXM) create a detached sub-process, since we are using `--die-with-parent`, said games will not run when executed directly (with `-b` parameter, executing a shell and launching manually still works); so we need to think in a way to detect child processes and wait for them, or at least add a flag to enable this feature. Disabling `--die-with-parent` is another option, but that would undermine security a bit and leave lingering wine processes all over the place. Maybe add a `--lead-process=NAME_EXE:TIMEOUT` to wait for another process inside the sandbox.
86-
* Implement bash autocompletion, should be able to autocomplete prefix and runner names based on the ones detected under Bottles.
112+
* Implement bash autocompletion, should be able to autocomplete prefix and runner names based on the ones detected under Bottles. Also consider using [clap_complete](https://crates.io/crates/clap_complete).
87113
* Add `integrate` sub-command to create integrations e.g., `.desktop` shortcut, entry on Heroic launcher.
88114
* Native wayland support, see https://www.phoronix.com/news/Wine-9.22-Released and https://wiki.archlinux.org/title/Wine#Wayland. Also consider bringing back `--unshare-ipc` if using Wayland prevents the issue described in bwrap.rs#90.
89115
* Add `kill` sub-command to terminate all processes in a sandbox, need to connect to existing bwrap container.
90-
* Add argument to mount additional paths (needed for installers and maintenance), syntax can be similar to Docker's `-v PATH:FLAGS`.
91116
* When using the `integrate` sub-command to create a `.desktop` shortcut, extract executable icon and set it respectively. It can be done with a small windows executable calling a win32 API call or natively on Linux by using `wrestool`.
92117
* Add NTSYNC support, see also https://www.phoronix.com/news/Linux-6.14-NTSYNC-Driver-Ready.
93118

src/cli.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ pub enum Commands {
1818
/// Environment variable overrides.
1919
#[arg(short = 'e', long = "setenv", value_name="KEY=VALUE", action = ArgAction::Append)]
2020
environment: Vec<String>,
21+
/// Additional mount points.
22+
#[arg(short = 'v', long = "volume", value_name="PATH", action = ArgAction::Append)]
23+
volumes: Vec<String>,
2124
/// Disable namespace isolation.
2225
#[arg(long, default_value = "false")]
2326
no_namespace_isolation: bool,
@@ -31,7 +34,7 @@ pub enum Commands {
3134
#[arg(long, value_name = "ACCESS", default_value = "minimal", value_parser)]
3235
device_access: DeviceAccess,
3336
/// Print additional troubleshooting information.
34-
#[arg(short, long, default_value = "false")]
37+
#[arg(long, default_value = "false")]
3538
verbose: bool,
3639
/// One of none, dlss, fsr:mode:stre.
3740
#[arg(long, value_name = "MODE", default_value = "none", value_parser)]

src/invoker.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
11
use crate::sandbox::{
22
bwrap,
3-
sandbox::{
4-
DeviceAccess, LaunchConfig, LaunchParams, MountConfig, NetworkMode, RuntimeEnv, SandboxConfig,
5-
},
3+
mount::{MountConfig, MountMapping},
4+
sandbox::{DeviceAccess, LaunchConfig, LaunchParams, NetworkMode, RuntimeEnv, SandboxConfig},
65
user_mapping::UserMapping,
76
wine::{SyncMode, UpscaleMode},
87
};
98
use std::{collections::HashMap, path::PathBuf, str::FromStr};
109

10+
fn parse_mappings(volumes: &[String]) -> anyhow::Result<Vec<MountMapping>> {
11+
let mut mappings: Vec<MountMapping> = Vec::with_capacity(volumes.len());
12+
for volume in volumes {
13+
let mapping =
14+
MountMapping::from_str(volume).map_err(|e| anyhow::anyhow!("volume error: {}", e))?;
15+
mappings.push(mapping);
16+
}
17+
Ok(mappings)
18+
}
19+
1120
pub fn run(
12-
environment: Vec<String>,
21+
environment: &[String],
22+
volumes: &[String],
1323
no_namespace_isolation: bool,
1424
user_mapping: UserMapping,
1525
network_mode: NetworkMode,
@@ -57,5 +67,11 @@ pub fn run(
5767
.collect();
5868
let mut runtime_env = RuntimeEnv::from_env()?;
5969
runtime_env.overrides = Some(env_overrides);
60-
bwrap::run(&sandbox_config, &launch_config, &runtime_env)
70+
let mount_mappings = parse_mappings(volumes)?;
71+
bwrap::run(
72+
&sandbox_config,
73+
&launch_config,
74+
&runtime_env,
75+
&mount_mappings,
76+
)
6177
}

src/main.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ fn main() -> anyhow::Result<()> {
1111
match args.command {
1212
Commands::Run {
1313
environment,
14+
volumes,
1415
no_namespace_isolation,
1516
user_mapping,
1617
network_mode,
@@ -24,7 +25,8 @@ fn main() -> anyhow::Result<()> {
2425
app_bin,
2526
app_args,
2627
} => invoker::run(
27-
environment,
28+
&environment,
29+
&volumes,
2830
no_namespace_isolation,
2931
user_mapping,
3032
network_mode,

src/sandbox/bwrap.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::display::Display;
2+
use super::mount::MountMapping;
23
use super::sandbox::{
34
DeviceAccess, LaunchConfig, LaunchParams, NetworkMode, RuntimeEnv, SandboxConfig,
45
};
@@ -68,6 +69,21 @@ pub fn get_device_args(device_access: &DeviceAccess) -> anyhow::Result<Vec<Strin
6869
}
6970
}
7071

72+
fn get_mount_args(mount_mappings: &[MountMapping]) -> Vec<String> {
73+
let mut args: Vec<String> = Vec::with_capacity(mount_mappings.len() * 3);
74+
for mapping in mount_mappings {
75+
let bind_param = if mapping.target_config.writable {
76+
"--bind"
77+
} else {
78+
"--ro-bind"
79+
};
80+
let source = mapping.source_path.to_string_lossy();
81+
let target = mapping.target_config.path.to_string_lossy();
82+
args.extend(vec![bind_param.into(), source.into(), target.into()]);
83+
}
84+
args
85+
}
86+
7187
const INNER_WINE_ROOT: &str = "/opt/wine";
7288
const INNER_WINE_PREFIX: &str = "/var/lib/wine";
7389
const INNER_APP_DIR: &str = "/app";
@@ -76,6 +92,7 @@ fn build_args(
7692
sandbox_config: &SandboxConfig,
7793
launch_config: &LaunchConfig,
7894
runtime_env: &RuntimeEnv,
95+
mount_mappings: &[MountMapping],
7996
empty_file_path: &str,
8097
) -> anyhow::Result<Vec<String>> {
8198
let mut args = vec![
@@ -350,6 +367,9 @@ fn build_args(
350367
let term = env::var("TERM").unwrap_or("xterm-256color".into());
351368
let shell = env::var("SHELL").unwrap_or("bash".into());
352369
let shell_params: Vec<String> = vec!["--setenv".into(), "TERM".into(), term, shell];
370+
// Additional mounts.
371+
let mount_args = get_mount_args(mount_mappings);
372+
final_args.extend(mount_args);
353373
// Depending on the launch params, add the necessary arguments to start a regular shell or execute
354374
// the specified command.
355375
match &launch_config.launch_params {
@@ -408,14 +428,21 @@ pub fn run(
408428
sandbox_config: &SandboxConfig,
409429
launch_config: &LaunchConfig,
410430
runtime_env: &RuntimeEnv,
431+
mount_mappings: &[MountMapping],
411432
) -> anyhow::Result<()> {
412433
// Temporary file will be automatically removed when variable goes out of scope.
413434
let temp_file = NamedTempFile::new()?;
414435
let temp_file_path = temp_file
415436
.path()
416437
.to_str()
417438
.context("could not get temporary file path")?;
418-
let args = build_args(sandbox_config, launch_config, &runtime_env, temp_file_path)?;
439+
let args = build_args(
440+
sandbox_config,
441+
launch_config,
442+
runtime_env,
443+
mount_mappings,
444+
temp_file_path,
445+
)?;
419446
let mut cmd = Command::new("bwrap")
420447
.args(args)
421448
.stdout(Stdio::inherit())

src/sandbox/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod bottles;
22
pub mod bwrap;
33
mod display;
4+
pub mod mount;
45
pub mod sandbox;
56
pub mod user_mapping;
67
pub mod wine;

0 commit comments

Comments
 (0)