I have sinned, dear Father // Father, I have sinned
at main 638 lines 19 kB view raw view rendered
1# Running Snaps Without Systemd: A Manual Approach 2 3## Overview 4 5This guide documents how to manually run a simple snap (command-line tool, no services) when systemd is not available. This is an educational exercise to understand snap internals—**this approach is NOT recommended for production use**. 6 7## Prerequisites 8 9- A `.snap` file (the snap package you want to run) 10- `unsquashfs` or `squashfs-tools` installed 11- Basic understanding of Linux filesystems and environment variables 12- Root or appropriate permissions for mounting 13 14## Understanding Snap Structure 15 16A `.snap` file is a **SquashFS filesystem** containing: 17- `/meta/snap.yaml` - Snap metadata 18- Application binaries and data 19- Libraries and dependencies 20- Desktop files, icons, etc. 21 22The snap expects to be mounted at a specific location (default: `/snap/<snap-name>/<revision>`). 23 24## Manual Steps to Run a Snap 25 26### Step 1: Extract or Mount the Snap 27 28#### Option A: Extract the Snap (Simpler, No Root Required) 29 30```bash 31# Extract the snap to a directory 32mkdir -p ~/snaps/extracted 33unsquashfs -d ~/snaps/extracted/my-snap /path/to/my-snap_1.0_amd64.snap 34 35# Note the extraction path 36SNAP_MOUNT=/home/yourusername/snaps/extracted/my-snap 37``` 38 39#### Option B: Mount the Snap (More Authentic) 40 41```bash 42# Create a mount point 43sudo mkdir -p /snap/my-snap/x1 44 45# Mount the squashfs file 46sudo mount -t squashfs -o ro,nodev /path/to/my-snap_1.0_amd64.snap /snap/my-snap/x1 47 48# Set mount point 49SNAP_MOUNT=/snap/my-snap/x1 50``` 51 52### Step 2: Parse the Snap Metadata 53 54```bash 55# Read the snap.yaml to find the app command 56cat $SNAP_MOUNT/meta/snap.yaml 57 58# Example output: 59# name: hello 60# version: 2.10 61# apps: 62# hello: 63# command: bin/hello 64``` 65 66### Step 3: Set Up Required Environment Variables 67 68Snaps expect specific environment variables to be set. Create a script or export these: 69 70```bash 71#!/bin/bash 72 73# Basic snap environment variables 74export SNAP="$SNAP_MOUNT" 75export SNAP_NAME="my-snap" 76export SNAP_VERSION="1.0" 77export SNAP_REVISION="x1" 78export SNAP_ARCH="amd64" # or your architecture 79 80# Data directories (writable areas for the snap) 81export SNAP_DATA="/var/snap/$SNAP_NAME/$SNAP_REVISION" 82export SNAP_COMMON="/var/snap/$SNAP_NAME/common" 83 84# User-specific data directories 85export SNAP_USER_DATA="$HOME/snap/$SNAP_NAME/$SNAP_REVISION" 86export SNAP_USER_COMMON="$HOME/snap/$SNAP_NAME/common" 87 88# Instance-related (for regular snaps, same as SNAP_NAME) 89export SNAP_INSTANCE_NAME="$SNAP_NAME" 90export SNAP_INSTANCE_KEY="" 91 92# Library path for graphics drivers, etc. 93export SNAP_LIBRARY_PATH="/var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32" 94 95# User ID information 96export SNAP_UID=$(id -u) 97export SNAP_EUID=$(id -u) 98 99# Exposed home directory (uppercase version) 100export SNAP_REAL_HOME="$HOME" 101 102# For classic confinement, preserve TMPDIR 103# For strict confinement, you might want a snap-specific tmp 104# export TMPDIR="/tmp/snap.$SNAP_NAME" 105``` 106 107### Step 4: Create Data Directories 108 109```bash 110# Create system-wide data directories (may require sudo) 111sudo mkdir -p /var/snap/$SNAP_NAME/$SNAP_REVISION 112sudo mkdir -p /var/snap/$SNAP_NAME/common 113 114# Create user data directories 115mkdir -p $HOME/snap/$SNAP_NAME/$SNAP_REVISION 116mkdir -p $HOME/snap/$SNAP_NAME/common 117``` 118 119### Step 5: Run the Application 120 121```bash 122# Source the environment (or use the script above) 123source /path/to/snap-env.sh 124 125# Execute the command from snap.yaml 126# If the command is "bin/hello", then: 127$SNAP/bin/hello [arguments] 128 129# Or more generally: 130cd $SNAP 131./path/to/command 132``` 133 134### Complete Example Script 135 136Here's a complete script to run a simple snap: 137 138```bash 139#!/bin/bash 140set -e 141 142# Configuration 143SNAP_FILE="/path/to/my-snap_1.0_amd64.snap" 144SNAP_NAME="my-snap" 145SNAP_REVISION="x1" 146EXTRACT_DIR="$HOME/snaps/extracted" 147MOUNT_DIR="$EXTRACT_DIR/$SNAP_NAME" 148 149# Step 1: Extract the snap 150echo "Extracting snap..." 151mkdir -p "$EXTRACT_DIR" 152unsquashfs -f -d "$MOUNT_DIR" "$SNAP_FILE" 153 154# Step 2: Parse snap.yaml to find the command 155echo "Reading snap metadata..." 156COMMAND=$(grep -A 2 "^ $SNAP_NAME:" "$MOUNT_DIR/meta/snap.yaml" | grep "command:" | awk '{print $2}') 157VERSION=$(grep "^version:" "$MOUNT_DIR/meta/snap.yaml" | awk '{print $2}') 158 159echo "Found command: $COMMAND" 160echo "Version: $VERSION" 161 162# Step 3: Set up environment 163export SNAP="$MOUNT_DIR" 164export SNAP_NAME="$SNAP_NAME" 165export SNAP_VERSION="$VERSION" 166export SNAP_REVISION="$SNAP_REVISION" 167export SNAP_ARCH=$(uname -m) 168 169export SNAP_DATA="/var/snap/$SNAP_NAME/$SNAP_REVISION" 170export SNAP_COMMON="/var/snap/$SNAP_NAME/common" 171export SNAP_USER_DATA="$HOME/snap/$SNAP_NAME/$SNAP_REVISION" 172export SNAP_USER_COMMON="$HOME/snap/$SNAP_NAME/common" 173 174export SNAP_INSTANCE_NAME="$SNAP_NAME" 175export SNAP_INSTANCE_KEY="" 176export SNAP_LIBRARY_PATH="/var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32" 177export SNAP_UID=$(id -u) 178export SNAP_EUID=$(id -u) 179export SNAP_REAL_HOME="$HOME" 180 181# Step 4: Create data directories 182echo "Creating data directories..." 183mkdir -p "$SNAP_USER_DATA" 184mkdir -p "$SNAP_USER_COMMON" 185 186# For system directories, you might need sudo: 187# sudo mkdir -p "$SNAP_DATA" 188# sudo mkdir -p "$SNAP_COMMON" 189 190# Step 5: Run the application 191echo "Running snap application..." 192cd "$SNAP" 193exec ./$COMMAND "$@" 194``` 195 196## What Will Be Missing 197 198Running snaps manually without the snapd ecosystem means you lose **significant functionality**: 199 200### 1. **No Confinement/Security** 201 202- **AppArmor profiles**: Not loaded, snap runs without MAC (Mandatory Access Control) 203- **Seccomp filters**: Not applied, snap has access to all syscalls 204- **Device cgroup**: Not configured, snap can access all devices 205- **Mount namespaces**: Not created, snap sees the entire host filesystem 206- **User namespaces**: Not used for privilege separation 207 208**Impact**: The snap runs with **no security boundaries**. A malicious snap could compromise your entire system. 209 210### 2. **No Automatic Updates** 211 212- No connection to Snap Store 213- No background update checks 214- No automatic snap refreshes 215- Must manually download and replace snap files 216 217**Impact**: You must manually track and install updates. Security patches won't be applied automatically. 218 219### 3. **No Interface Connections** 220 221- No access to plugs and slots system 222- Cannot connect to system resources like: 223 - Network Manager 224 - Audio (PulseAudio/PipeWire) 225 - Bluetooth 226 - Hardware devices (cameras, printers, etc.) 227- No automatic permission management 228 229**Impact**: The snap may fail if it expects interface connections. Many features won't work. 230 231### 4. **No Services** 232 233- Cannot run systemd services defined in the snap 234- No daemon management (start/stop/restart) 235- No socket activation 236- No timer-based execution 237- No automatic service restarts on failure 238 239**Impact**: Only command-line applications can be run. Background services, daemons, and scheduled tasks won't work. 240 241### 5. **No Mount Management** 242 243- No dynamic mount namespace setup 244- Layouts defined in snap.yaml won't be applied 245- No bind mounts for content sharing 246- No per-snap `/tmp` or other private mounts 247 248**Impact**: Applications expecting specific filesystem layouts will fail. Content interfaces won't work. 249 250### 6. **No State Management** 251 252- No tracking of installed snaps 253- No revision management 254- No rollback capability 255- No state.json or other snapd state files 256 257**Impact**: Cannot track what's installed, cannot rollback to previous versions. 258 259### 7. **No snapctl Integration** 260 261#### What is snapctl? 262 263`snapctl` is a command-line tool that snaps use to communicate with the snapd daemon. It's the bridge between a snap application and the snap system's runtime services. When a snap executes, snapd sets up environment variables (like `$SNAP_COOKIE`) that allow `snapctl` to authenticate and communicate with the snapd daemon over a Unix socket. 264 265#### Why is it needed? 266 267Snaps use `snapctl` for several critical functions: 268 2691. **Configuration Management** 270 - `snapctl get <key>` - Read configuration values 271 - `snapctl set <key>=<value>` - Write configuration values 272 - Configuration is stored by snapd and persists across snap updates 273 2742. **Service Management** 275 - `snapctl start/stop/restart <service>` - Control snap services 276 - Query service status 277 - Coordinate between multiple services in a snap 278 2793. **Hook Execution Context** 280 - Hooks (install, configure, pre-refresh, post-refresh, etc.) run with snapctl access 281 - Hooks use snapctl to validate configuration, set defaults, perform migrations 282 - Essential for snap lifecycle management 283 2844. **Health Checks** 285 - `snapctl set-health` - Report snap health status to snapd 286 - Used by monitoring and management tools 287 2885. **Connection Information** 289 - Query which interfaces are connected 290 - Get connection attributes and parameters 291 2926. **System Information** 293 - Query snap revision, version, architecture 294 - Get information about other installed snaps 295 296#### The Problem 297 298Without snapd running, `snapctl` has nowhere to connect. The command will fail with: 299 300``` 301error: snapctl: cannot invoke snapctl operation commands from outside of a snap 302``` 303 304Or if it tries to connect: 305 306``` 307error: cannot communicate with server: connection refused 308``` 309 310Many "enterprise" or complex snaps (like charmcraft, lxd, multipass, etc.) heavily rely on snapctl for configuration management. They expect to be able to read/write configuration through snapctl, which breaks completely without snapd. 311 312#### Detection Mechanism 313 314Snaps detect they're running under snapd via: 315- `$SNAP_COOKIE` environment variable (authentication token) 316- `$SNAP_CONTEXT` environment variable (legacy) 317- Unix socket at `/run/snapd.socket` or `/run/snapd-snap.socket` 318 319Libraries like `snaphelpers` (Python) automatically try to use snapctl and fail when it's unavailable. 320 321#### Workarounds 322 323**1. Mock Configuration Files** 324 325Create a fake configuration that the snap might read: 326 327```bash 328# Some snaps fall back to reading config files 329mkdir -p "$SNAP_DATA" 330cat > "$SNAP_DATA/config.json" << EOF 331{ 332 "key": "value" 333} 334EOF 335``` 336 337**Limitation**: Only works if the snap has fallback code paths. Most don't. 338 339**2. Stub snapctl Command** 340 341Create a fake `snapctl` that returns empty/default values: 342 343```bash 344#!/bin/bash 345# Fake snapctl that returns empty config 346case "$1" in 347 get) 348 echo "{}" # Return empty JSON config 349 ;; 350 set) 351 exit 0 # Pretend set succeeded 352 ;; 353 *) 354 exit 1 355 ;; 356esac 357``` 358 359Add it to PATH before running the snap: 360 361```bash 362export PATH="/path/to/fake-snapctl:$PATH" 363``` 364 365**Limitation**: 366- Very brittle, depends on what the snap expects 367- Doesn't work for snaps that validate responses 368- Can cause unexpected behavior if snap makes decisions based on config 369 370**3. Patch the Snap** 371 372For Python-based snaps using `snaphelpers`: 373 374```bash 375# Extract the snap 376unsquashfs -d my-snap my-snap.snap 377 378# Modify the code to skip snapctl 379# Example: patch snaphelpers to not use snapctl 380vim my-snap/lib/python3.12/site-packages/snaphelpers/_conf.py 381 382# Repack (requires snap-pack tool, part of snapd) 383snap pack my-snap/ 384``` 385 386**Limitation**: 387- Requires understanding the snap's internals 388- Must repack the snap (needs snapd tools!) 389- Breaks snap signatures 390- Must repeat for every snap update 391 392**4. Run with Environment Variables** 393 394Some snaps check for specific environment variables and disable features: 395 396```bash 397# Tell snap not to use snapctl (if supported) 398export SNAP_TESTING=1 399export IGNORE_SNAP_CONFIG=1 400 401# Run the snap 402./my-snap 403``` 404 405**Limitation**: Only works if the snap explicitly supports these flags (rare). 406 407**5. Use Classic Confinement Snaps** 408 409Classic confinement snaps sometimes have less dependency on snapctl: 410 411```bash 412# Classic snaps might work better without snapd 413# But classic snaps typically need snapd to install anyway 414``` 415 416**Limitation**: Classic snaps still often use snapctl. 417 418#### Real-World Example: charmcraft 419 420Charmcraft (as seen in the error above) uses the `craft-application` framework which uses `snaphelpers` for configuration. On startup, it tries: 421 422```python 423debug_mode = self.services.get("config").get("debug") 424``` 425 426This internally calls: 427```python 428snapctl.run("get", "-d", "debug") 429``` 430 431Which fails immediately without snapd. 432 433**Workaround**: Patch craft-application or set environment to skip config: 434 435```bash 436# This doesn't actually work for charmcraft, just illustrative 437export CRAFT_DEBUG=0 # Try to bypass config system 438``` 439 440Unfortunately, charmcraft doesn't have a clean way to disable snapctl usage. 441 442#### Bottom Line 443 444**There is no general workaround for snapctl.** Each snap that uses it must be handled individually: 445 446- Simple snaps that only use snapctl for optional features might work 447- Complex snaps that depend on snapctl for core functionality will fail 448- The only real solution is to run snapd 449 450If you cannot run snapd, you must: 4511. Choose snaps that don't use snapctl (rare, mostly simple CLI tools) 4522. Patch the snap to remove snapctl dependencies (difficult) 4533. Use alternative packaging formats (AppImage, static binaries, containers) 454 455**Snaps that commonly use snapctl** (avoid these without snapd): 456- charmcraft, lxd, multipass, microk8s 457- Most "platform" or "enterprise" snaps 458- Snaps with complex configuration 459- Snaps with services and hooks 460 461**Snaps that might work** (less snapctl usage): 462- Simple CLI tools: shellcheck, yq, jq-alike tools 463- Static binaries wrapped as snaps 464- Snaps with no configuration or hooks 465 466### 8. **No Command Chain Support** 467 468- Command chains defined in snap.yaml won't execute 469- No wrapper script execution 470- No environment setup by command chains 471 472**Impact**: Some snaps rely on command chains for initialization. These will fail silently. 473 474### 9. **No User Data Migration** 475 476- No automatic migration of user data between revisions 477- No XDG directory symlink management 478- No exposed home directory setup 479 480**Impact**: User data management is manual and error-prone. 481 482### 10. **No Desktop Integration** 483 484- Desktop files not registered with system 485- Application won't appear in application menus 486- Icons not installed to system locations 487- MIME type associations not created 488- URL handlers not registered 489 490**Impact**: The application won't integrate with your desktop environment. 491 492### 11. **No Parallel Installs** 493 494- Cannot install multiple versions or instances 495- No instance key support 496- Name conflicts if multiple versions exist 497 498**Impact**: Limited to one "installation" per snap name. 499 500### 12. **No Graphics Acceleration** 501 502- No automatic graphics driver mounting 503- NVIDIA/AMD drivers not available inside snap 504- No access to `/var/lib/snapd/lib/gl` 505 506**Impact**: Graphics applications may fail or run with software rendering only. 507 508### 13. **No Snap-Confine Isolation** 509 510- No PID 1 mount namespace tricks 511- No `/snap` directory bind mounts 512- No namespace file creation in `/run/snapd/ns/` 513- No cgroup tracking for process management 514 515**Impact**: Multiple issues with process isolation and management. 516 517### 14. **No Seed/Preseeding Support** 518 519- Cannot participate in system seeding 520- Not part of system image creation 521- Cannot be part of Ubuntu Core images 522 523**Impact**: Cannot be used in embedded or appliance scenarios. 524 525### 15. **No Assertions/Signing Verification** 526 527- Snap signatures not verified 528- Snap declarations not checked 529- No trust chain validation 530- No revision authority verification 531 532**Impact**: Cannot verify snap authenticity or integrity. Could run tampered snaps. 533 534## Additional Caveats 535 536### Environment Variables 537 538The snap may expect additional environment variables: 539 540- `$SNAP_COOKIE` - For snapctl communication (won't work) 541- `$SNAP_CONTEXT` - Legacy snapctl communication (won't work) 542- `$XDG_RUNTIME_DIR` - Should point to snap-specific runtime dir 543- `$HOME` - Typically overridden to `$SNAP_USER_DATA` for strict confinement 544 545### Library Dependencies 546 547- The snap may have dependencies on system libraries 548- Without proper `SNAP_LIBRARY_PATH` setup, libraries won't be found 549- Classic confinement snaps expect host libraries 550 551### Path Issues 552 553- The snap may expect to run from specific mount points (`/snap/...`) 554- Some snaps hardcode paths assuming standard snap mount locations 555- Environment variable expansion (`$SNAP`, etc.) must work correctly 556 557### Permissions 558 559- File permissions inside the snap may not match your user 560- SELinux/AppArmor contexts won't be correct 561- Extended attributes may cause issues 562 563## Use Cases for Manual Snap Running 564 565Despite all the limitations, manual snap running might be useful for: 566 5671. **Educational purposes**: Understanding how snaps work internally 5682. **Debugging**: Examining snap contents without installing 5693. **Offline/air-gapped systems**: Where snapd cannot be installed 5704. **Emergency recovery**: Running tools from snaps when system is broken 5715. **Embedded systems**: Very constrained environments without systemd 5726. **Research**: Analyzing snap behavior and structure 573 574## Alternative: Snap-Confine Without Snapd 575 576If you need **some** confinement without full snapd, you could theoretically: 577 5781. Compile `snap-confine` standalone 5792. Write AppArmor profiles manually 5803. Create seccomp filters manually 5814. Set up mount namespaces manually 5825. Configure device cgroups manually 583 584This is **extremely complex** and not recommended. You're essentially reimplementing snapd. 585 586## Recommended Alternatives 587 588If you're in a restricted environment, consider these alternatives: 589 590### 1. Static Binaries 591- Use statically compiled binaries instead 592- Tools like Go produce static binaries 593- No runtime dependencies 594 595### 2. AppImage 596- Self-contained application bundles 597- Don't require systemd 598- Mount via FUSE (which has its own requirements) 599 600### 3. Flatpak 601- Also uses namespaces but has different requirements 602- Can run without systemd (with limitations) 603 604### 4. Docker/Podman Containers 605- Better isolation without snapd 606- More mature tooling for constrained environments 607 608### 5. Traditional Packages 609- Distribution packages (deb, rpm) 610- Follows system conventions 611- Better integration without systemd 612 613## Conclusion 614 615Running snaps without snapd and systemd is possible for **very simple, statically-linked command-line tools**, but you lose: 616 617- All security features (AppArmor, seccomp, namespaces) 618- All management features (updates, rollback, configuration) 619- All integration features (interfaces, desktop integration, services) 620- All snapd-specific functionality 621 622The effort required grows exponentially with snap complexity. For anything beyond basic CLI tools, the missing functionality makes this approach impractical. 623 624**Bottom line**: If you can't install systemd, snaps are not the right packaging format for your use case. Consider the alternatives listed above. 625 626## References 627 628- [Snapd Source Code](https://github.com/canonical/snapd) 629- [SquashFS Documentation](https://www.kernel.org/doc/Documentation/filesystems/squashfs.txt) 630- Key snapd components: 631 - `snap-confine`: Confinement helper (requires compiled binary) 632 - `snap-exec`: Execution helper inside confinement 633 - `snap-update-ns`: Mount namespace manager 634 - `snapd` daemon: State management and API server 635 636## Warning 637 638⚠️ **This guide is for educational purposes only.** Running snaps without proper confinement is a security risk. The snap has full access to your system, just like any other executable. Only run snaps from trusted sources, and never use this approach in production environments.