fix: container security hardening, onboarding viewport scaling, boot screen cleanup

Container security:
- Add --cap-drop ALL + --security-opt no-new-privileges:true to 12 containers
  missing hardening in first-boot-containers.sh (mempool-db, electrumx,
  mempool-api, mempool-web, electrs-ui, btcpay-db, nbxplorer, nostr-rs-relay,
  strfry, tailscale, bitcoin-ui, lnd-ui)
- Mirror same hardening in deploy-to-target.sh for consistency
- Add --read-only + tmpfs to nostr-rs-relay
- Fix filebrowser deploy to include security flags
- Remove duplicate UI image definitions in image-versions.sh
- Separate Jellyfin capabilities (needs FOWNER, exec tmpfs for CoreCLR JIT)
- Harden archy-net creation with existence check and error handling

UI fixes:
- Fix onboarding viewport scaling: all 7 screens now use h-full + max-h-full
  pattern so containers never overflow viewport regardless of padding
- Remove path-option-card wrappers from seed verify inputs, left-justify labels
- Remove batteries/barbarian icons from boot screen (keep bitcoin, cloud, github, save)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-31 17:35:34 +01:00
parent f29fa2e729
commit a896ecd431
14 changed files with 160 additions and 51 deletions

View File

@@ -59,13 +59,21 @@ pub(super) fn get_app_capabilities(app_id: &str) -> Vec<String> {
"--cap-add=NET_RAW".to_string(),
],
"nextcloud" | "btcpay-server" | "btcpayserver"
| "jellyfin" | "onlyoffice" | "onlyoffice-documentserver" | "portainer" => vec![
| "onlyoffice" | "onlyoffice-documentserver" | "portainer" => vec![
"--cap-add=CHOWN".to_string(),
"--cap-add=SETUID".to_string(),
"--cap-add=SETGID".to_string(),
"--cap-add=DAC_OVERRIDE".to_string(),
"--cap-add=NET_BIND_SERVICE".to_string(),
],
// Jellyfin: CoreCLR needs exec-enabled tmpfs for JIT compilation
"jellyfin" => vec![
"--cap-add=CHOWN".to_string(),
"--cap-add=FOWNER".to_string(),
"--cap-add=SETUID".to_string(),
"--cap-add=SETGID".to_string(),
"--cap-add=DAC_OVERRIDE".to_string(),
],
// Nginx Proxy Manager needs to bind low ports
"nginx-proxy-manager" => vec![
"--cap-add=CHOWN".to_string(),

View File

@@ -158,11 +158,37 @@ impl RpcHandler {
run_args.push("--cap-add=NET_RAW");
run_args.push("--device=/dev/net/tun");
} else if needs_archy_net(package_id) {
let _ = tokio::process::Command::new("podman")
// Create archy-net if it doesn't exist (idempotent — "already exists" is fine)
match tokio::process::Command::new("podman")
.args(["network", "create", "archy-net"])
.output()
.await
{
Ok(output) if !output.status.success() => {
let stderr = String::from_utf8_lossy(&output.stderr);
if !stderr.contains("already exists") {
tracing::warn!("Failed to create archy-net: {}", stderr.trim());
}
}
Err(e) => {
tracing::warn!("Failed to run podman network create: {}", e);
}
_ => {}
}
// Verify the network actually exists before using it
let net_check = tokio::process::Command::new("podman")
.args(["network", "exists", "archy-net"])
.status()
.await;
run_args.push("--network=archy-net");
if net_check.map(|s| s.success()).unwrap_or(false) {
run_args.push("--network=archy-net");
} else {
tracing::error!(
"archy-net network does not exist — {} will use default network. \
Inter-container DNS will not work.",
package_id
);
}
}
// Security hardening (skip for privileged containers)
@@ -184,6 +210,11 @@ impl RpcHandler {
run_args.push("--tmpfs=/tmp:rw,noexec,nosuid,size=256m");
run_args.push("--tmpfs=/run:rw,noexec,nosuid,size=64m");
}
// Jellyfin: .NET CoreCLR needs exec-enabled /tmp for JIT compilation
if package_id == "jellyfin" {
run_args.push("--tmpfs=/tmp:rw,exec,size=256m");
}
}
// Create data directories