This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Overview
Relevant source files
Purpose and Scope
This document provides an introduction to sshfs-mac-docker, explaining what the system is, the problem it solves, and how it achieves its goals at a high level. This overview covers the system's architecture, core components, and operational model without going into detailed implementation specifics.
For step-by-step usage instructions, see Getting Started. For in-depth architectural analysis, see Architecture. For detailed configuration reference, see Configuration Reference.
Sources: README.md:1-90
What is sshfs-mac-docker?
sshfs-mac-docker is a containerized solution that enables macOS users to mount remote SSH filesystems without installing macFUSE or kernel extensions on their host system. The system runs an isolated Docker container that bridges SSH-based remote filesystem access (via SSHFS) with native macOS file browsing (via Samba/SMB protocol).
Key Problem Solved: Eliminates the need for macFUSE and custom kernel extensions (kext) on macOS by moving all FUSE-related operations inside a Docker container.
Sources: README.md:1-5
Problem Statement
Traditional SSHFS usage on macOS requires:
- Installation of macFUSE (formerly OSXFUSE)
- Loading kernel extensions that require system-level privileges
- Security policy modifications on modern macOS versions
- Potential system instability from third-party kernel modules
sshfs-mac-docker avoids all of these requirements by:
- Running FUSE operations entirely within a Linux container
- Exposing mounted remote filesystems through the well-established SMB protocol
- Requiring only Docker/OrbStack installation on the macOS host
Sources: README.md3
System Architecture Overview
The system employs a three-tier architecture where the Docker container acts as a protocol translation bridge:
Sources: README.md5 Dockerfile:1-34
graph TB
subgraph "Tier_1[macOS Host]"
Finder["macOS Finder"]
Terminal["Terminal"]
end
subgraph "Tier_2[Docker Container: docker-sshfs]"
Samba["smbd\n(Samba daemon)"]
SSHFS["sshfs process"]
subgraph "Filesystem"
SambaShare["/samba-share\n(chmod 777)"]
Remote["/remote\n(SSHFS mount point)"]
SymLink["Symbolic Link:\n/samba-share/remote → /remote"]
end
User["sshuser:sshpass\n(UID 1000, GID 1000)"]
end
subgraph "Tier_3[Remote Infrastructure]"
SSHServer["Remote SSH Server\nuser@host:path"]
end
Finder -->|SMB Protocol smb://container-ip Ports 139/445| Samba
Terminal -->|docker exec| User
Samba -->|Serves| SambaShare
SambaShare -.->|Contains| SymLink
SymLink -.->|Links to| Remote
SSHFS -->|SSH Port 22| SSHServer
SSHServer -->|Mounted at| Remote
User -->|Executes| SSHFS
Core Components and Code Entities
The system consists of three primary configuration artifacts and several runtime entities:
Configuration Files
| File | Purpose | Key Settings |
|---|---|---|
Dockerfile | Container image definition | Base: ubuntu:latest |
Packages: sshfs, samba | ||
User: sshuser | ||
smb.conf | Samba server configuration | Share path: /samba-share |
| Guest access enabled | ||
Force user: sshuser | ||
/etc/fuse.conf | FUSE permissions | user_allow_other enabled |
Sources: Dockerfile:1-34 smb.conf:1-20
Runtime Components
Component Details:
smbdprocess: Samba daemon started via Dockerfile33 as container's main processsshuseraccount: Non-root user created in Dockerfile9 with hardcoded password/remotedirectory: SSHFS mount point created in Dockerfile12/samba-sharedirectory: Samba root created in Dockerfile15 with 777 permissions Dockerfile21- Symbolic link : Integration point created in Dockerfile30 linking
/samba-share/remote→/remote
Sources: Dockerfile:9-30 Dockerfile33
Key Technologies
The system integrates three core technologies:
| Technology | Role | Configuration |
|---|---|---|
| SSHFS | Remote filesystem client | Mount options in README.md49 |
Requires allow_other, uid=1000, gid=1000 | ||
| Samba | SMB/CIFS server | Configured in smb.conf:1-20 |
| Ports 139 and 445 | ||
| FUSE | Filesystem in userspace | Enabled in Dockerfile24 |
With user_allow_other permission |
Critical Integration:
- FUSE's
allow_other: Enables Samba (running as different user) to access SSHFS mounts README.md52 - UID/GID mapping : Sets ownership to
sshuser(1000:1000) for write access README.md53 - Symbolic link : Zero-copy integration between SSHFS mount and Samba share Dockerfile30
Sources: README.md:49-53 Dockerfile24 Dockerfile30 smb.conf:1-20
Operational Model
The system requires three phases to establish a working connection:
Phase 1: Container Initialization
- Builds image from Dockerfile:1-34
- Starts
smbdin foreground mode Dockerfile33 - Binds SMB ports to localhost README.md31
Sources: README.md22 README.md31 Dockerfile33
Phase 2: Remote Filesystem Mounting
- Executes SSHFS as
sshuserinside container README.md:41-49 - Mounts remote filesystem at
/remote - Accessible via symbolic link at
/samba-share/remoteDockerfile30
Sources: README.md:41-49 Dockerfile30
Phase 3: macOS Client Connection
- Must use container IP address (not
localhost) README.md57 - Connect as "Guest" README.md67
- Files appear in Finder under Network devices README.md69
Sources: README.md:57-69
Why Samba Instead of Docker Volume Mounts?
Docker's native volume mounting (docker run -v) cannot be used because:
- Volume mounts are designed for local filesystems : They map host directories to container paths
- SSHFS operates inside the container : The mounted remote filesystem exists only in the container's namespace
- No reverse mounting capability : Docker cannot expose a container's internal mount back to the host
The Samba approach works because:
- SMB is a network protocol that crosses namespace boundaries
- Samba can serve any filesystem accessible within the container
- macOS has native SMB client support (Finder)
Sources: README.md:13-16
Platform Requirements
The system has specific platform dependencies:
| Platform | Status | Notes |
|---|---|---|
| OrbStack | ✅ Recommended | Works out-of-box with proper networking README.md9 |
| Docker Desktop | ⚠️ Requires modifications | Network routing must be modified README.md9 |
| macOS | Required | Target platform for SMB client |
Critical Detail: While SMB ports are forwarded to 127.0.0.1:139 and 127.0.0.1:445, macOS Finder must connect using the container's internal Docker IP address (e.g., 172.17.0.2), not localhost README.md57
Sources: README.md9 README.md57
Security Considerations
The system's security model includes:
- Privileged container : Required for FUSE operations README.md31
- Localhost-only port binding : SMB ports bound to
127.0.0.1prevent external network access README.md34 - Guest authentication : SMB configured for guest-only access smb.conf:13-14
- Forced user identity : All operations execute as
sshuserregardless of SMB client smb.conf19 - Hardcoded credentials :
sshuser:sshpassdefined in Dockerfile9
Sources: README.md31 README.md34 smb.conf:13-14 smb.conf19 Dockerfile9
Limitations and Alternatives
Known Limitations:
- Requires running container with
--privilegedflag - Manual mount/unmount operations required
- Container IP discovery needed for each connection
- "Device or resource busy" error requires unmounting from Finder first README.md:79-85
Alternative Solution: For users who prefer not to use Docker, the project documentation references fuse-t (https://github.com/macos-fuse-t/fuse-t) as a potential alternative README.md89
Sources: README.md:79-85 README.md89
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Getting Started
Relevant source files
Purpose and Scope
This page provides a high-level overview of the setup and operational workflow for sshfs-mac-docker. It covers the complete lifecycle from building the Docker image to actively accessing remote files through macOS Finder. The material focuses on the sequence of steps required to establish a working system and the key files and commands involved.
For detailed information about specific topics:
Sources: README.md
Setup Overview
The sshfs-mac-docker setup process consists of four distinct phases that must be executed in sequence. Each phase depends on the successful completion of the previous phase.
graph TB
Phase1["Phase 1: Image Build\ndocker build -t docker-sshfs ."]
Phase2["Phase 2: Container Start\ndocker run --privileged"]
Phase3["Phase 3: Remote Mount\nsshfs user@host:path /samba-share"]
Phase4["Phase 4: macOS Connection\nsmb://container-ip"]
Phase1 --> Phase2
Phase2 --> Phase3
Phase3 --> Phase4
Phase1 -.-> Files1["Creates:\n- /remote\n- /samba-share\n- /samba-share/remote (symlink)\n- sshuser account"]
Phase2 -.-> Process2["Starts:\n- smbd daemon\n- Ports 139/445 forwarded"]
Phase3 -.-> Mount3["Mounts:\n- Remote filesystem at /remote\n- Accessible via /samba-share/remote"]
Phase4 -.-> Access4["Enables:\n- Finder access\n- Read/write operations"]
Setup Phase Diagram
Sources: README.md:19-69, Dockerfile:1-34
Key Files and Commands
The following table maps the major system components to their corresponding files and commands:
| Component | File/Command | Purpose | Reference |
|---|---|---|---|
| Build Definition | Dockerfile | Defines image with sshfs, samba, sshuser, directories | Dockerfile:1-34 |
| Samba Configuration | smb.conf | Configures guest access, forced user, share path | smb.conf |
| FUSE Configuration | /etc/fuse.conf | Enables user_allow_other option | Dockerfile24 |
| Remote Mount Point | /remote | Directory where SSHFS mounts remote filesystem | Dockerfile12 |
| Samba Share Directory | /samba-share | Root directory served by Samba with 777 permissions | Dockerfile15 Dockerfile21 |
| Symbolic Link | /samba-share/remote | Links Samba share to SSHFS mount point | Dockerfile30 |
| Service User | sshuser | Non-root user (uid=1000) for SSHFS operations | Dockerfile9 |
| Build Command | docker build -t docker-sshfs . | Builds image from Dockerfile | README.md22 |
| Run Command | docker run --privileged -p 127.0.0.1:139:139 -p 127.0.0.1:445:445 | Starts container with port forwarding | README.md31 |
| Mount Command | sshfs -o allow_other,uid=1000,gid=1000 user@host:path /samba-share | Mounts remote filesystem via SSHFS | README.md49 |
| Unmount Command | fusermount -u /samba-share | Unmounts SSHFS filesystem | README.md76 |
Sources: README.md:19-86, Dockerfile:1-34
Container Filesystem Layout
The Docker container establishes a specific filesystem structure that enables the SSH-to-SMB protocol bridge. Understanding this layout is essential for troubleshooting and configuration.
Sources: Dockerfile:12, Dockerfile:15, Dockerfile:18, Dockerfile:21, Dockerfile:24, Dockerfile:30
graph TB
subgraph Container["Container Filesystem"]
subgraph ETC["/etc"]
SmbConf["/etc/samba/smb.conf\n(copied from host)"]
FuseConf["/etc/fuse.conf\n(user_allow_other added)"]
end
subgraph Root["/"]
Remote["/remote\n(SSHFS mount point)\nmkdir at build time"]
SambaShare["/samba-share\n(Samba root directory)\nchmod 777"]
end
subgraph SambaShareContents["/samba-share contents"]
Symlink["/samba-share/remote\n(symbolic link)\nln -s /remote"]
end
subgraph Users["/home"]
SshUserHome["/home/sshuser\n(created by useradd)"]
end
end
Remote -.->|target of symlink| Symlink
SambaShare -->|contains| Symlink
BuildStep1["Dockerfile:12\nRUN mkdir /remote"]
BuildStep2["Dockerfile:15\nRUN mkdir /samba-share"]
BuildStep3["Dockerfile:18\nCOPY smb.conf"]
BuildStep4["Dockerfile:21\nRUN chmod -R 777"]
BuildStep5["Dockerfile:24\nRUN echo user_allow_other"]
BuildStep6["Dockerfile:30\nRUN ln -s /remote"]
BuildStep1 -.-> Remote
BuildStep2 -.-> SambaShare
BuildStep3 -.-> SmbConf
BuildStep4 -.-> SambaShare
BuildStep5 -.-> FuseConf
BuildStep6 -.-> Symlink
Command Sequence
The following diagram shows the exact command sequence required to establish a working system, with the specific flags and options required for each command.
Sources: README.md:22, README.md:31, README.md:41-49, README.md:57-69, Dockerfile:33
sequenceDiagram
participant User
participant DockerCLI as "docker CLI"
participant Container as "docker-sshfs container"
participant Finder as "macOS Finder"
rect rgb(240, 240, 240)
Note over User,Container: Build Phase
User->>DockerCLI: docker build -t docker-sshfs .
DockerCLI->>DockerCLI: Process Dockerfile:1-34
DockerCLI->>DockerCLI: Create image with sshfs, samba, sshuser
DockerCLI-->>User: Image "docker-sshfs" created
end
rect rgb(240, 240, 240)
Note over User,Container: Container Start Phase
User->>DockerCLI: docker run --privileged --name docker-sshfs\n-p 127.0.0.1:139:139 -p 127.0.0.1:445:445 docker-sshfs
DockerCLI->>Container: Start container
Container->>Container: Execute CMD: smbd --foreground --no-process-group
Container-->>DockerCLI: smbd listening on ports 139/445
Note over Container: Container running, awaiting SSHFS mount
end
rect rgb(240, 240, 240)
Note over User,Container: Mount Phase
User->>DockerCLI: docker exec -it docker-sshfs bash
DockerCLI->>Container: Attach interactive shell
User->>Container: sshfs -o allow_other,uid=1000,gid=1000\nuser@host:path /samba-share
Container->>Container: Mount remote filesystem at /remote\nAccessible via /samba-share/remote symlink
Container-->>User: Remote filesystem mounted
end
rect rgb(240, 240, 240)
Note over User,Finder: Connection Phase
User->>DockerCLI: docker inspect --format '{{ .NetworkSettings.IPAddress }}'\ndocker-sshfs
DockerCLI-->>User: Return container IP (e.g., 172.17.0.2)
User->>Finder: Go → Connect to Server\nsmb://[container-ip]
Finder->>Container: SMB connection request (ports 139/445)
Container-->>Finder: Request authentication
User->>Finder: Connect as Guest
Finder->>Container: Access /samba-share
Container-->>Finder: Serve files via smbd
Note over Finder: Remote files accessible in Finder
end
stateDiagram-v2
[*] --> ImageNotBuilt : Repository Cloned
ImageNotBuilt --> ImageBuilt : docker build -t docker-sshfs . Creates - sshuser, /remote, /samba-share, symlink
ImageBuilt --> ContainerRunning : docker run --privileged -p 139 - 139 -p 445 - 445 Starts - smbd daemon (Dockerfile - 33)
ContainerRunning --> RemoteMounted : docker exec + sshfs -o allow_other,uid=1000,gid=1000 Mounts - remote filesystem at /remote
RemoteMounted --> MacConnected : Finder → smb - //[container-ip] Connect as Guest
MacConnected --> FullyOperational : Files accessible in Finder
FullyOperational --> MacConnected : Unmount from Finder
MacConnected --> RemoteMounted : fusermount -u /samba-share
RemoteMounted --> ContainerRunning : fusermount -u completes
ContainerRunning --> ImageBuilt : docker stop docker-sshfs
note right of ImageNotBuilt
Prerequisites:
- Docker/OrbStack installed
- Cloned repository
end note
note right of RemoteMounted
Critical mount options:
- allow_other (Dockerfile : 24)
- uid=1000,gid=1000 (sshuser uid)
end note
note right of MacConnected
Common issue:
Device or resource busy
Must unmount from Finder first
end note
System State Transitions
The system progresses through several distinct states during setup and operation. Each state has specific prerequisites and produces specific outcomes.
Sources: README.md:22-86, Dockerfile:9, Dockerfile:24, Dockerfile:33
Critical Configuration Points
Several configuration elements established during the build process are essential for the system to function correctly:
User Configuration
The sshuser account is created with uid=1000 and gid=1000 Dockerfile9 This uid/gid pairing is referenced in the SSHFS mount command README.md49 to ensure write permissions.
Directory Permissions
The /samba-share directory is set to 777 permissions Dockerfile21 to allow the Samba service to write files, which are then forwarded through the symbolic link to the SSHFS mount.
FUSE Configuration
The user_allow_other option is appended to /etc/fuse.conf Dockerfile24 to enable the allow_other mount option, which permits processes other than the mounting user (including the smbd daemon) to access the mounted filesystem.
Symbolic Link Integration
The symbolic link from /samba-share/remote to /remote Dockerfile30 creates the integration point between the Samba share and the SSHFS mount, allowing Samba to serve files from the remote filesystem without data duplication.
Port Forwarding
Ports 139 and 445 are forwarded to 127.0.0.1 README.md31 to provide localhost-only access to the Samba service, preventing external network exposure while still allowing macOS Finder to connect via the container's internal IP address.
Sources: Dockerfile:9, Dockerfile:21, Dockerfile:24, Dockerfile:30, README.md:31, README.md:49
Quick Reference Command Summary
| Operation | Command | Location |
|---|---|---|
| Build image | docker build -t docker-sshfs . | Host terminal |
| Start container | docker run --privileged --name docker-sshfs -p 127.0.0.1:139:139 -p 127.0.0.1:445:445 docker-sshfs | Host terminal |
| Access container | docker exec -it docker-sshfs bash | Host terminal |
| Mount remote | sshfs -o allow_other,uid=1000,gid=1000 user@host:path /samba-share | Inside container |
| Get container IP | docker inspect --format '{{ .NetworkSettings.IPAddress }}' docker-sshfs | Host terminal |
| Connect from Finder | smb://[container-ip] | Finder → Go → Connect to Server |
| Unmount filesystem | fusermount -u /samba-share | Inside container |
| Stop container | docker stop docker-sshfs | Host terminal |
Sources: README.md:22-86
Next Steps
After completing the basic setup outlined on this page:
- Review Prerequisites and Platform Requirements for detailed platform considerations, especially regarding OrbStack versus Docker Desktop
- Follow the detailed steps in Building the Container through Connecting from macOS
- Consult Unmounting and Cleanup when you need to disconnect or reconfigure
- If you encounter issues, see Troubleshooting for common problems and solutions
- For a deeper understanding of the system architecture, see Architecture
Sources: README.md
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Prerequisites and Platform Requirements
Relevant source files
Purpose and Scope
This page documents the software and platform requirements needed to run sshfs-mac-docker. It covers the required Docker runtime environment, platform-specific considerations for OrbStack versus Docker Desktop, and system capabilities that must be available. For detailed troubleshooting of platform-specific issues, see Platform-Specific Issues. For instructions on building the container once prerequisites are met, see Building the Container.
Sources : README.md:1-90
Required Software Components
The sshfs-mac-docker system requires only two software components on the host macOS system:
| Component | Purpose | Version Notes |
|---|---|---|
| macOS | Host operating system | Any modern version supporting Docker |
| Docker Runtime | Container execution environment | OrbStack (recommended) or Docker Desktop |
Notably, sshfs-mac-docker does not require macFUSE or any kernel extensions to be installed on macOS. All FUSE operations are contained within the Docker container itself.
Sources : README.md3 README.md9
Docker Platform Options
Platform Requirements Comparison
Sources : README.md9 README.md:31-34
OrbStack (Recommended)
OrbStack is the recommended Docker runtime for sshfs-mac-docker because it provides native support for the networking requirements without additional configuration.
Key Advantages :
- Container networking works seamlessly with SMB/CIFS protocol
- No network routing modifications required
- Direct container IP access from macOS Finder
Installation : Available at https://orbstack.dev/
When using OrbStack, the SMB connection workflow operates as documented without additional steps.
Sources : README.md9
Docker Desktop (Alternative with Limitations)
Docker Desktop can be used but has known networking limitations that prevent standard operation.
Known Limitation : Docker Desktop's network implementation does not allow macOS Finder to connect to SMB shares using the standard port forwarding configuration shown in README.md:31-34 The README explicitly states: "Docker Desktop will not work for this without modifying the network routing."
If Using Docker Desktop : Network routing modifications are required but not documented in the current codebase. For troubleshooting these issues, see Platform-Specific Issues.
Sources : README.md9
What is NOT Required
Avoided Dependencies
Sources : README.md3
The primary motivation for sshfs-mac-docker is to avoid installing macFUSE on macOS. The traditional approach to using SSHFS on macOS requires:
| Component | Why It's Problematic | How sshfs-mac-docker Avoids It |
|---|---|---|
| macFUSE | Third-party kernel extension | Runs FUSE inside Docker container |
| Kernel Extensions | Requires lowering system security | No host kernel modifications needed |
| System Integrity Protection (SIP) | May require disabling or modifications | Host macOS remains unmodified |
By moving FUSE operations into the Docker container's Linux environment, all kernel-level dependencies are isolated from the macOS host system.
Sources : README.md3
System Capabilities Required
Privileged Container Mode
The container must run in privileged mode using the --privileged flag:
Why Privileged Mode is Required :
- FUSE operations require access to the
/dev/fusedevice - Mounting filesystems requires elevated capabilities within the container
- SSHFS uses FUSE kernel module which needs privileged access
This requirement is specified in README.md31 as part of the container startup command.
Sources : README.md31
Port Availability
The host system must have the following ports available on localhost (127.0.0.1):
| Port | Protocol | Purpose |
|---|---|---|
| 139 | NetBIOS Session Service | SMB legacy compatibility |
| 445 | SMB over TCP | Modern SMB protocol |
Port Forwarding Configuration :
These ports must not be in use by other services on the macOS host. The 127.0.0.1 binding restricts access to localhost only, preventing external network exposure.
Sources : README.md:31-34
Network Access Requirements
Sources : README.md:57-69
The system requires two distinct network paths:
-
macOS → Container (SMB) :
- Finder must be able to reach the container's IP address
- Connection string format:
smb://container-ip - Container IP is discoverable using
docker inspectcommand shown in README.md60
-
Container → Remote Server (SSH) :
- Container must have outbound network access to remote SSH servers
- Standard SSH port (22) must be reachable
- SSH credentials must be available for authentication
Critical Networking Quirk : The localhost address (127.0.0.1 or localhost) does not work for SMB connections from macOS Finder, even though the ports are forwarded to localhost. Finder must connect using the container's internal Docker network IP address.
Sources : README.md:57-60
Platform Requirements Summary
Pre-Flight Checklist
| Requirement | Status Check Command | Expected Result |
|---|---|---|
| Docker Runtime Installed | docker --version | Version information displayed |
| OrbStack Running (if used) | docker info | Shows OrbStack as provider |
| Ports 139/445 Available | lsof -i :139 and lsof -i :445 | No output (ports not in use) |
| Privileged Mode Supported | docker run --rm --privileged alpine ls /dev/fuse | /dev/fuse exists |
| Network Access to Remote | ssh user@host | Successful SSH connection |
Minimum System Profile
Sources : README.md:1-90
Next Steps
Once all prerequisites are satisfied:
- Verify Docker runtime is operational
- Confirm ports 139 and 445 are available
- Proceed to Building the Container
For troubleshooting platform-specific issues during setup, see Platform-Specific Issues.
Sources : README.md:1-90
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Building the Container
Relevant source files
This page documents the process of building the docker-sshfs container image from the Dockerfile. It covers the build command, the components installed during the build, and the filesystem structure created in the resulting image.
For information about running the built container, see Running the Container. For details about individual configuration files, see Configuration Reference.
Build Command
The container image is built using a single Docker command:
This command reads the Dockerfile:1-34 in the repository root and creates a Docker image tagged as docker-sshfs. The build process installs all necessary packages, creates the required directory structure, and configures both SSHFS and Samba services.
Sources: README.md:19-23
Dockerfile Structure
The build process follows a sequential set of instructions that construct the container image from a base Ubuntu system to a fully configured SSHFS/Samba server.
flowchart TD Start["FROM ubuntu:latest"] --> Update["apt-get update"] Update --> Install["apt-get install -y sshfs samba"] Install --> CreateUser["useradd -m sshuser\necho 'sshuser:sshpass' / chpasswd"] CreateUser --> MkdirRemote["mkdir /remote"] MkdirRemote --> MkdirSamba["mkdir /samba-share"] MkdirSamba --> CopyConf["COPY smb.conf /etc/samba/smb.conf"] CopyConf --> ChmodSamba["chmod -R 777 /samba-share"] ChmodSamba --> FuseConf["echo 'user_allow_other' >> /etc/fuse.conf"] FuseConf --> Expose["EXPOSE 139 445"] Expose --> Symlink["ln -s /remote /samba-share/remote"] Symlink --> CMD["CMD smbd --foreground --no-process-group --debug-stdout"] CMD --> End["Image Ready"]
Build Process Flow
Sources: Dockerfile:1-34
Base Image and Package Installation
The build starts from ubuntu:latest and installs two critical packages:
| Package | Purpose | Key Functionality |
|---|---|---|
sshfs | SSH filesystem client | Mounts remote directories via SSH protocol |
samba | SMB/CIFS server | Exposes local directories as network shares |
These installations occur at Dockerfile:2-6:
FROM ubuntu:latest
RUN apt-get update && \
apt-get install -y sshfs samba
The sshfs package provides the FUSE-based SSH filesystem mounting capability, while samba provides the smbd daemon and SMB protocol implementation that macOS can connect to.
Sources: Dockerfile:2-6
User Account Creation
A non-root user account is created for running SSHFS operations:
useradd -m sshuser && echo "sshuser:sshpass" | chpasswd
This command at Dockerfile9 creates:
- Username:
sshuser - Password:
sshpass - Home directory:
/home/sshuser(created with-mflag)
The sshuser account is referenced in the Samba configuration via the force user = sshuser directive, ensuring all file operations through Samba appear to originate from this user.
Sources: Dockerfile:8-9
Directory Structure Creation
The build creates two primary mount point directories:
Directory Purposes
| Path | Created By | Purpose | Permissions |
|---|---|---|---|
/remote | Dockerfile12 | SSHFS mount target directory | Default (755) |
/samba-share | Dockerfile15 | Samba share root directory | 777 (world-writable) |
/samba-share/remote | Dockerfile30 | Symbolic link to /remote | Follows link target |
The /samba-share directory receives chmod -R 777 permissions at Dockerfile21 to ensure the Samba service can write to it regardless of the user context.
Sources: Dockerfile:11-15 Dockerfile:20-21 Dockerfile:29-30
Configuration File Integration
The build copies the Samba configuration file into the container:
COPY smb.conf /etc/samba/smb.conf
This instruction at Dockerfile18 copies the smb.conf:1-19 file from the build context into the container's standard Samba configuration location. The configuration defines the SMB share parameters including guest access, user forcing, and protocol versions. For details about the configuration contents, see Samba Configuration (smb.conf)).
Sources: Dockerfile:17-18
FUSE Configuration
The build modifies the FUSE configuration to enable cross-user filesystem access:
echo "user_allow_other" >> /etc/fuse.conf
This line at Dockerfile24 appends the user_allow_other option to /etc/fuse.conf. This setting is critical because:
- SSHFS mounts are performed by the
sshuseraccount - The Samba daemon (
smbd) runs as a different user - Without
user_allow_other,smbdcannot access SSHFS-mounted filesystems
For more details on this configuration, see FUSE Configuration.
Sources: Dockerfile:23-24
Network Port Exposure
The Dockerfile exposes two ports for Samba connectivity:
EXPOSE 139 445
At Dockerfile27 the build declares:
- Port 139 : NetBIOS Session Service (legacy SMB)
- Port 445 : SMB over TCP/IP (modern SMB)
These ports are later mapped to the host system when running the container. See Running the Container for details on port mapping configuration.
Sources: Dockerfile:26-27
Symbolic Link Creation
A critical integration point is established by creating a symbolic link:
ln -s /remote /samba-share/remote
This command at Dockerfile30 creates the symbolic link /samba-share/remote that points to /remote. This link serves as the bridge between the SSHFS mount point and the Samba share:
flowchart LR SSHFS["SSHFS Mount\n(future operation)"] --> Remote["/remote"] Remote -.->|referenced by| Symlink["/samba-share/remote\n(symbolic link)"] Symlink --> SambaDir["/samba-share"] SambaDir --> Samba["smbd service\n(exposes directory)"] Samba --> Mac["macOS Finder\n(SMB client)"]
When a remote filesystem is mounted to /remote at runtime, it automatically becomes accessible as /samba-share/remote through the symbolic link, allowing Samba to serve the remote files without data duplication.
Sources: Dockerfile:29-30
Container Entry Point
The final build instruction specifies the container's main process:
CMD ["smbd", "--foreground", "--no-process-group", "--debug-stdout"]
At Dockerfile33 this command configures the container to:
- Execute
smbd(Samba daemon) as the primary process - Run in
--foregroundmode (does not daemonize) - Use
--no-process-groupto avoid creating a process group - Enable
--debug-stdoutto send logs to stdout for Docker visibility
When the container starts, this command runs automatically, launching the Samba server and keeping the container alive. The container remains running as long as the smbd process is active.
Sources: Dockerfile:32-34
Build Output Summary
The completed image contains the following components:
The resulting image is self-contained and ready to run. No additional configuration is required at container startup, though SSHFS mounting must be performed manually after the container is running. See Mounting Remote Filesystems for runtime mount procedures.
Sources: Dockerfile:1-34 README.md:19-23
Build Verification
After running docker build -t docker-sshfs ., you can verify the image was created successfully:
The output should display the docker-sshfs image with a recent creation timestamp. The image size typically ranges from 300-400 MB depending on the base Ubuntu image version and installed package versions.
To proceed with using the container, continue to Running the Container.
Sources: README.md:19-23
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Running the Container
Relevant source files
This page explains how to start the docker-sshfs container with the correct privileges and network configuration. It covers the docker run command, required flags, port mappings, and what happens during container startup. For information about building the container image, see Building the Container. For information about using the container to mount remote filesystems, see Mounting Remote Filesystems.
Purpose and Prerequisites
Running the container requires an already-built docker-sshfs image. The container must be started with specific privileges and port configurations to enable FUSE operations and Samba network access. This page assumes Docker or OrbStack is installed and the image has been built as described in Prerequisites and Platform Requirements and Building the Container.
The docker run Command
The container is started using the following command:
This command runs in the foreground and should be executed in a dedicated terminal session, as the Samba daemon (smbd) runs in foreground mode and will occupy the terminal.
Sources: README.md:30-32
Command Flags and Configuration
Container Runtime Configuration
Sources: README.md:30-34 Dockerfile:26-27
Flag Details
| Flag | Purpose | Why Required |
|---|---|---|
--privileged | Grants container access to host devices, specifically /dev/fuse | SSHFS requires FUSE operations which need device access. Without this flag, the sshfs command will fail with permission errors when attempting to mount filesystems |
--name docker-sshfs | Assigns a fixed name to the container | Enables consistent container identification for subsequent docker exec, docker inspect, and docker stop commands |
-p 127.0.0.1:139:139 | Maps container port 139 to host port 139, bound to localhost only | Exposes NetBIOS Session Service for SMB connections. The 127.0.0.1 binding prevents external network access |
-p 127.0.0.1:445:445 | Maps container port 445 to host port 445, bound to localhost only | Exposes SMB over TCP for modern SMB protocol versions (SMB2+). The 127.0.0.1 binding provides security isolation |
docker-sshfs | Image name to instantiate | References the image built in Building the Container |
Sources: README.md:30-34 Dockerfile:26-27
Container Startup Process
Service Initialization Sequence
Sources: README.md:28-34 Dockerfile33
Executed Command
When the container starts, Docker executes the CMD directive specified in the Dockerfile:
Command Flag Breakdown:
| Flag | Purpose |
|---|---|
--foreground | Keeps smbd running in the foreground rather than daemonizing. This is required for Docker containers because the container stops when the main process exits |
--no-process-group | Prevents smbd from creating a new process group, ensuring proper signal handling in the container environment |
--debug-stdout | Sends log output to stdout, making logs visible via docker logs and the terminal running docker run |
Sources: Dockerfile33
Port Binding and Network Access
Port Configuration Map
Sources: README.md:30-34 Dockerfile:26-27
Localhost Binding Rationale
The port mappings use 127.0.0.1: prefix rather than binding to all interfaces (0.0.0.0). This provides security isolation:
- External Access Prevention: Samba ports are not exposed to the host's external network interfaces
- Security Constraint: Only processes running on the macOS host can connect to the mapped ports
- Recommended Practice: Prevents accidental exposure of the Samba share to network-accessible machines
However, there is a known limitation: macOS Finder cannot connect to smb://127.0.0.1 due to how Docker's network bridge interacts with macOS's SMB client. The workaround requires connecting to the container's internal Docker IP address, which is covered in Connecting from macOS.
Sources: README.md34 README.md:57-61
Container State After Startup
Filesystem and Service Availability
Once the container is running, the following state is established:
| Component | State | Details |
|---|---|---|
/dev/fuse | Mounted | Available due to --privileged flag |
/remote directory | Created, empty | Mount point for SSHFS, created during image build |
/samba-share directory | Created, permissions 777 | Samba root directory with symbolic link to /remote |
/samba-share/remote | Symbolic link | Links to /remote, created during image build |
smbd process | Running, foreground | Listening on ports 139 and 445 |
| Samba share | Available | Named "SSHFS Share", serving /samba-share |
| SSHFS mount | Not mounted | Requires manual mounting via docker exec, covered in Mounting Remote Filesystems |
Sources: Dockerfile12 Dockerfile15 Dockerfile21 Dockerfile30 Dockerfile33
Container Lifecycle Management
Starting and Stopping
Sources: README.md:26-34 README.md:73-86
Common Operations
Starting the container:
Stopping the container (from another terminal):
Stopping the container (from the terminal running docker run):
Ctrl+C
Restarting a stopped container:
The -a flag attaches to the container's stdout, allowing you to see smbd logs.
Removing the container:
Sources: README.md:30-32
Verification Steps
After starting the container, verify that services are running correctly:
Check Container Status
Expected output should show docker-sshfs with status "Up" and port mappings 127.0.0.1:139->139/tcp, 127.0.0.1:445->445/tcp.
Check Container Logs
In the terminal running docker run, smbd logs will be displayed due to the --debug-stdout flag. Look for lines indicating successful startup and port binding.
Verify Port Bindings
From another terminal:
Expected output:
139/tcp -> 127.0.0.1:139
445/tcp -> 127.0.0.1:445
Test Container Access
This drops you into a bash shell inside the running container. Exit with exit or Ctrl+D. This verification confirms the container is running and accessible for subsequent SSHFS mount operations described in Mounting Remote Filesystems.
Sources: README.md:40-42
Next Steps
With the container running, proceed to Mounting Remote Filesystems to connect to remote SSH servers via SSHFS, or see Connecting from macOS to access the Samba share from Finder.
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Mounting Remote Filesystems
Relevant source files
Purpose and Scope
This page describes how to mount a remote SSH filesystem inside the docker-sshfs container using the sshfs command. It covers the required mount options (allow_other, uid, gid) and explains why each is necessary for integration with Samba. This assumes the container is already built and running. For container setup, see Building the Container and Running the Container. For connecting from macOS Finder, see Connecting from macOS.
Sources: README.md (lines 36-54)
Executing into the Container
To mount a remote filesystem, you must first execute a bash shell inside the running container:
This command starts an interactive terminal session inside the container named docker-sshfs. Once executed, you will be inside the container environment with access to the sshfs command and the mount point directories.
Sources: README.md (lines 40-44)
The SSHFS Mount Command
The sshfs command mounts a remote SSH filesystem to a local directory inside the container. The basic syntax is:
Replace user@host:path with your actual SSH endpoint. For example:
john@192.168.1.100:/home/john/documentsdeploy@example.com:/var/www/htmladmin@10.0.0.5:~/projects
sequenceDiagram
participant User as "User Terminal"
participant Bash as "Container Bash Session"
participant SSHFS as "sshfs Process"
participant FUSE as "FUSE Layer"
participant Remote as "Remote SSH Server"
participant Samba as "smbd Service"
User->>Bash: docker exec -it docker-sshfs bash
activate Bash
User->>Bash: sshfs -o allow_other,uid=1000,gid=1000\nuser@host:path /samba-share
Bash->>SSHFS: Execute sshfs command
activate SSHFS
SSHFS->>Remote: SSH connection request
Remote-->>SSHFS: Authentication prompt
User->>Remote: Enter SSH password/key
Remote-->>SSHFS: Authentication successful
SSHFS->>FUSE: Register FUSE filesystem
FUSE->>FUSE: Apply mount options:\nallow_other, uid=1000, gid=1000
FUSE-->>SSHFS: Mount registered at /samba-share
SSHFS-->>Bash: Mount successful
Note over FUSE,Samba: allow_other enables cross-user access
Samba->>FUSE: Read /samba-share (as sshuser)
FUSE-->>Samba: Access granted (allow_other)
Bash-->>User: Return to prompt
deactivate SSHFS
deactivate Bash
The mount target is /samba-share, which is the directory that the Samba service exposes to macOS.
Sources: README.md (lines 46-50)
SSHFS Mount Process Flow
Diagram: SSHFS Mount Process - Shows the sequence of operations from command execution through SSH authentication to successful FUSE mount registration and Samba integration.
Sources: README.md (lines 40-54), Dockerfile (lines 23-24)
Critical Mount Options
The mount options specified with -o are essential for the system to function correctly. Omitting any of these options will result in either read-only access or complete inaccessibility from macOS.
| Option | Purpose | Without This Option |
|---|---|---|
allow_other | Permits users other than the mount owner to access the filesystem | The smbd service (running as sshuser) cannot read the mounted filesystem, preventing macOS access |
uid=1000 | Sets the user ID for all files in the mounted filesystem | Files may be owned by the wrong user, causing permission errors |
gid=1000 | Sets the group ID for all files in the mounted filesystem | Files may have incorrect group ownership, preventing write operations |
The combination of uid=1000 and gid=1000 corresponds to the sshuser account created in the container Dockerfile9 This ensures consistent ownership and enables write access. Without these options, the mount will be read-only.
Sources: README.md (lines 52-53)
Container Directory Structure
The container has two primary directories involved in the mount process:
Diagram: Container Directory Structure and Mount Options - Illustrates the relationship between /remote, /samba-share, and the symbolic link, along with two possible mounting approaches.
Sources: Dockerfile (lines 12, 15, 30)
Directory Roles and Relationships
/remote Directory
Created by Dockerfile12 This directory serves as the designated FUSE mount point in the system architecture. While the README documents mounting directly to /samba-share, the container structure includes /remote as an alternative mount location. When used, the symbolic link at /samba-share/remote provides access.
/samba-share Directory
Created by Dockerfile15 and configured with chmod -R 777 permissions Dockerfile21 This directory is the root of the Samba share exposed to macOS. The README documents mounting the remote filesystem directly to this location.
/samba-share/remote Symbolic Link
Created by Dockerfile30 as ln -s /remote /samba-share/remote. This symbolic link connects /remote to /samba-share/remote, enabling access to /remote contents through the Samba share when mounting to /remote instead of /samba-share.
Sources: Dockerfile (lines 12, 15, 21, 30)
FUSE Configuration Requirement
The allow_other mount option requires that FUSE be configured to permit non-root users to use this option. The container enables this by appending user_allow_other to /etc/fuse.conf:
user_allow_other
This line is added during the container build process Dockerfile24 Without this configuration, the sshfs command will fail with an error message stating that allow_other is not permitted.
Sources: Dockerfile (lines 23-24)
graph TB
subgraph "Mount Command Components"
Cmd["sshfs -o allow_other,uid=1000,gid=1000"]
Remote["user@host:path"]
Target["/samba-share"]
end
subgraph "allow_other Effect"
AO1["FUSE allows cross-user access"]
AO2["smbd process can read mount"]
AO3["macOS Finder receives files"]
AO1 -->
AO2 --> AO3
end
subgraph "uid=1000,gid=1000 Effect"
UID1["All files owned by uid=1000"]
UID2["Matches sshuser account"]
UID3["Write operations permitted"]
UID4["No permission errors"]
UID1 -->
UID2 -->
UID3 --> UID4
end
subgraph "Without uid/gid"
NO1["Files owned by root:root"]
NO2["sshuser cannot modify"]
NO3["Read-only access from macOS"]
NO1 -->
NO2 --> NO3
end
Cmd --> AO1
Cmd --> UID1
style AO3 fill:#e1ffe1
style UID4 fill:#e1ffe1
style NO3 fill:#ffe1e1
Mount Options Technical Details
Diagram: Mount Options Impact on Access Permissions - Shows how each mount option affects the accessibility and write permissions of the mounted filesystem.
Sources: README.md (lines 49-53), Dockerfile (line 9)
Verification of Successful Mount
After executing the sshfs command, you can verify the mount succeeded by:
- Check mount point contents:
The directory should display the contents of the remote filesystem.
- Inspect active mounts:
Should display a line showing the SSHFS mount at /samba-share.
- Check FUSE mounts:
Should show the FUSE filesystem mounted at the specified path.
If the mount fails, common issues include:
- Incorrect SSH credentials
- Network connectivity problems to the remote host
- SSH host key verification prompts (may require running
sshfsinteractively first) - Insufficient permissions on the remote filesystem
Sources: README.md (lines 46-54)
Relationship to Samba Service
Once the remote filesystem is mounted at /samba-share, the smbd service (started automatically when the container runs Dockerfile33) serves this directory over SMB protocol. The Samba configuration file smb.conf defines /samba-share as the share path.
The allow_other mount option is critical for this integration because:
- The
sshfsprocess runs as the user executing the command inside the container - The
smbdservice runs as a separate process (assshuserdue toforce userconfiguration) - Without
allow_other, FUSE would denysmbdaccess to the mounted filesystem - With
allow_otherenabled,smbdcan read and serve the mounted files to macOS clients
For details on the Samba configuration, see Samba Configuration (smb.conf)). For connecting from macOS, see Connecting from macOS.
Sources: README.md (lines 52-53), Dockerfile (lines 23-24, 33)
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Connecting from macOS
Relevant source files
Purpose and Scope
This page guides users through connecting to the Samba share from macOS Finder after the container is running and the remote filesystem has been mounted. This is the final step in making remote files accessible through macOS's native file browser.
For information about starting the container, see Running the Container. For information about mounting the remote filesystem via SSHFS, see Mounting Remote Filesystems. For troubleshooting connection issues, see Connection Problems.
Sources : README.md:55-70
Prerequisites
Before connecting from macOS, ensure the following conditions are met:
| Requirement | Verification Command | Expected State |
|---|---|---|
| Container running | docker ps | docker-sshfs appears in list |
| SSHFS mounted | docker exec docker-sshfs ls /remote | Remote files visible |
| Samba service active | docker exec docker-sshfs pgrep smbd | Returns process ID |
If any prerequisite is not met, the connection will fail silently or show authentication errors.
Sources : README.md:26-54
Discovering the Container IP Address
macOS Finder cannot connect to the Samba share using localhost or 127.0.0.1, despite the port forwarding configuration in docker run -p 127.0.0.1:139:139 -p 127.0.0.1:445:445. This is a known limitation of how Docker's port forwarding interacts with macOS's SMB client implementation.
graph TB
subgraph "Attempted Connection (Fails)"
FinderA["macOS Finder"]
LocalhostA["smb://localhost or\nsmb://127.0.0.1"]
PortForwardA["Port Forward\n-p 127.0.0.1:139:139\n-p 127.0.0.1:445:445"]
FailureA["❌ Connection Refused"]
FinderA --> LocalhostA
LocalhostA --> PortForwardA
PortForwardA --> FailureA
end
subgraph "Working Connection (Required)"
FinderB["macOS Finder"]
ContainerIPB["smb://172.17.0.2\n(container IP)"]
DockerNetB["Docker Bridge Network\ndocker0"]
SmbdB["smbd process\nPorts 139/445"]
SambaShareB["/samba-share"]
FinderB --> ContainerIPB
ContainerIPB --> DockerNetB
DockerNetB --> SmbdB
SmbdB --> SambaShareB
end
subgraph "IP Discovery"
InspectCmd["docker inspect\n--format '{{ .NetworkSettings.IPAddress }}'\ndocker-sshfs"]
OutputIP["172.17.0.2"]
InspectCmd --> OutputIP
end
Obtaining the Container IP
Execute the following command to retrieve the container's internal Docker network IP address:
Example output :
flowchart LR
Finder["macOS Finder\nSMB Client"]
Localhost["localhost:445\nPort Forward"]
DockerBridge["Docker Bridge\nNetwork"]
Container["Container IP\n(e.g., 172.17.0.2)"]
Samba["smbd Process\nPorts 139/445"]
Finder -->|❌ Attempts| Localhost
Localhost -.->|Port Forward Incompatibility| Container
Finder -->|✓ Direct Connection| DockerBridge
DockerBridge --> Container
Container --> Samba
172.17.0.2
The IP address is dynamically assigned by Docker and may change between container restarts. Always retrieve the current IP before attempting to connect.
Why Localhost Doesn't Work
Diagram : Network routing showing that Finder must connect directly to the container's Docker bridge IP, bypassing localhost port forwarding.
The port forwarding to 127.0.0.1 serves to restrict external network access (security), but does not enable localhost connectivity for SMB protocol negotiation.
Sources : README.md:57-61
Connecting via Finder
Step-by-Step Connection Process
Diagram : Complete sequence of user interactions and protocol negotiations during the connection process.
Detailed Steps
-
Open Finder on macOS
-
Access Server Connection Dialog
- Press
⌘K(Cmd+K), or - Navigate to menu bar:
Go → Connect to Server
- Press
-
Enter Server Address
- Input:
smb://<container-ip>where<container-ip>is the IP obtained fromdocker inspect - Example:
smb://172.17.0.2 - Click
Connect
- Input:
-
Select Authentication Method
- When prompted, select
Guestradio button - Do not enter username/password
- Click
Connect
- When prompted, select
-
Share Selection
- If multiple shares exist, select
SSHFS Share - Click
OK
- If multiple shares exist, select
The connection process typically completes in 1-3 seconds. If authentication fails, verify the Samba configuration allows guest access (see section below).
Sources : README.md:63-68
Authentication and Guest Access
Guest Authentication Configuration
The Samba service is configured to accept guest connections without password authentication. This configuration is defined in smb.conf:1-20:
| Configuration Parameter | Value | Effect |
|---|---|---|
security | user | Use user-based authentication model |
map to guest | bad user | Map invalid users to guest account |
guest ok | yes | Allow guest access to share |
guest only | yes | Restrict share to guest access only |
force user | sshuser | All operations execute as sshuser |
Authentication Flow
Diagram : Authentication and identity mapping flow from macOS guest connection to remote filesystem operations.
Security Implications
The force user = sshuser directive smb.conf19 ensures all filesystem operations execute with consistent permissions:
- Benefit : Simplifies permission management; no need to coordinate macOS user IDs with remote server IDs
- Limitation : All users connecting to the share have identical access rights
- Mitigation : Port forwarding restricted to
127.0.0.1prevents external network access README.md31
The guest access model is appropriate for development and personal use where the macOS host is trusted. For multi-user or production scenarios, consider implementing authenticated access (requires modifications to smb.conf:3-4).
Sources : smb.conf:1-20 README.md67
Accessing Remote Files in Finder
flowchart LR
subgraph "Finder Locations"
Sidebar["Finder Sidebar\n'Network' Section"]
Network["Network View\nCmd+Shift+K"]
Volumes["Volumes View\n/Volumes/"]
end
subgraph "Share Contents"
SSHFSShare["SSHFS Share\n(Mount Point)"]
RemoteDir["remote/\n(Directory)"]
RemoteFiles["Remote Files\n(via Symbolic Link)"]
end
Sidebar --> SSHFSShare
Network --> SSHFSShare
Volumes --> SSHFSShare
SSHFSShare --> RemoteDir
RemoteDir --> RemoteFiles
ContainerPath["/samba-share/remote\n(Container Path)"]
SymLink["Symbolic Link\n/samba-share/remote → /remote"]
RemoteFiles -.->|Maps to| SymLink
SymLink -.->|Points to| ContainerPath
Mounted Share Location
After successful connection, the Samba share appears in multiple locations within Finder:
Diagram : Filesystem hierarchy showing how remote files appear in macOS Finder through the Samba share and symbolic link integration.
Navigation Path
-
Sidebar Access
- Finder sidebar →
Networksection - Look for container IP (e.g.,
172.17.0.2) - Click to expand
- Finder sidebar →
-
Share Contents
- Top level shows
SSHFS Sharedirectory - Navigate to
remotesubdirectory - Contents are the mounted remote filesystem
- Top level shows
-
Direct Volume Path
- Accessible at
/Volumes/SSHFS Share/remote/ - Usable in Terminal and CLI tools
- Accessible at
Share Mapping
| macOS View | Container Path | Remote Location |
|---|---|---|
/Volumes/SSHFS Share/ | /samba-share/ | Samba root directory |
/Volumes/SSHFS Share/remote/ | /samba-share/remote/ → /remote/ | SSHFS mount point |
/Volumes/SSHFS Share/remote/<file> | /remote/<file> | user@host:path/<file> |
The symbolic link at samba-share/remote (created during container initialization) provides transparent access to the SSHFS mount point at remote
Sources : README.md:69-70
File Operations and Performance
Supported Operations
All standard macOS file operations function through the SMB connection:
| Operation | Support | Notes |
|---|---|---|
| Read files | ✓ Full | No restrictions |
| Write files | ✓ Full | Requires uid=1000,gid=1000 mount option |
| Create directories | ✓ Full | Permissions: 0777 (configurable) |
| Delete files/directories | ✓ Full | Subject to remote server permissions |
| Rename/move | ✓ Full | Within mounted filesystem |
| File metadata | ✓ Partial | Timestamps preserved; ownership mapped to sshuser |
Performance Characteristics
Diagram : Complete data path showing protocol translation layers from macOS application to remote SSH server.
Performance Considerations :
- Latency : Each file operation traverses 6+ layers (SMB → Samba → symlink → FUSE → SSHFS → SSH)
- Throughput : Limited by SSH connection bandwidth and encryption overhead
- Caching : Minimal; most operations query remote server directly
- Best Use Case : Occasional file access and editing, not high-throughput data processing
For large file transfers or high-frequency operations, consider using native SSH tools (scp, rsync) instead of mounting the filesystem.
Sources : README.md:1-5 smb.conf:11-19
Connection Verification
Confirming Successful Connection
After connecting, verify the setup with the following checks:
1. Check Finder Sidebar
2. Verify Mount Point
Expected output :
//GUEST@172.17.0.2/SSHFS Share on /Volumes/SSHFS Share (smbfs, nodev, nosuid, mounted by username)
3. Test File Listing
Should display files from the remote SSH server mounted via SSHFS at remote
4. Test Write Access
If write fails, verify SSHFS was mounted with uid=1000,gid=1000 options (see Mounting Remote Filesystems).
Connection State Diagram
Diagram : State transitions during the macOS connection lifecycle, from initial disconnected state through successful mounting to active use.
Sources : README.md:55-70
Common Connection Parameters
SMB URL Format
smb://<container-ip>/<share-name>
| Component | Description | Example |
|---|---|---|
<container-ip> | Docker bridge network IP | 172.17.0.2 |
<share-name> | Share name from smb.conf10 | SSHFS Share |
Valid URLs :
smb://172.17.0.2(auto-selects share)smb://172.17.0.2/SSHFS Share(explicit share)
Invalid URLs :
smb://localhost(port forwarding incompatibility)smb://127.0.0.1(same issue as localhost)smb://docker-sshfs(DNS name not resolvable)
Protocol Versions
The Samba service enforces SMB2 or later smb.conf:7-8:
This ensures compatibility with modern macOS versions (10.12+) while disabling insecure SMB1 protocol. macOS automatically negotiates the highest supported protocol version during connection.
Sources : README.md65 smb.conf:7-10
Troubleshooting Quick Reference
| Issue | Likely Cause | Solution |
|---|---|---|
| "Connection failed" | Container not running | Verify with docker ps |
| "Connection timed out" | Wrong IP address | Re-run docker inspect |
| "Authentication failed" | Not using Guest | Select "Guest" option, not "Registered User" |
| "Share not found" | SSHFS not mounted | Check Mounting Remote Filesystems |
| Files not writable | Missing mount options | Remount with uid=1000,gid=1000 |
| Share disappears | Container stopped/restarted | Reconnect with new IP address |
For detailed troubleshooting, see Connection Problems and Platform-Specific Issues.
Sources : README.md:55-70
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Unmounting and Cleanup
Relevant source files
Purpose and Scope
This page explains the proper procedures for unmounting remote filesystems and cleaning up the sshfs-mac-docker system. The unmounting process requires specific sequencing due to dependencies between macOS Finder connections, Samba shares, and SSHFS mounts. Failure to follow the correct order results in the common "Device or resource busy" error.
For information about establishing the initial connection, see Connecting from macOS. For detailed troubleshooting of unmounting errors, see Device or Resource Busy Errors.
Sources: README.md:71-86
Unmounting Sequence Overview
The system has multiple interdependent mount points that must be disconnected in a specific order. The macOS Finder connection to the Samba share must be terminated before the underlying SSHFS mount can be unmounted. This dependency exists because active SMB connections hold references to the filesystem.
flowchart TD
FinderMounted["macOS Finder Connected to smb://container-ip"]
SambaServing["Samba serving /samba-share"]
SSHFSMounted["SSHFS mounted at /remote\nsymlinked to /samba-share"]
ContainerRunning["Container docker-sshfs running"]
FinderMounted -->|Must disconnect first| SambaServing
SambaServing -->|Then allow unmount| SSHFSMounted
SSHFSMounted -->|Finally can stop| ContainerRunning
FinderMounted -.->|If skipped: 'Device or resource busy'| SSHFSMounted
style FinderMounted fill:#fff
style SSHFSMounted fill:#fff
style ContainerRunning fill:#fff
Cleanup Dependency Chain
Sources: README.md:71-86
Step 1: Disconnect from macOS
Before unmounting the SSHFS filesystem inside the container, all macOS Finder connections to the Samba share must be terminated. Active SMB sessions maintain locks on the filesystem that prevent clean unmounting.
Disconnecting in Finder
| Method | Steps |
|---|---|
| Finder Sidebar | Right-click the mounted share name → Select "Eject" |
| Finder Menu | File → Eject [share-name] |
| Desktop Icon | Right-click the mounted volume icon → Select "Eject" |
| Keyboard Shortcut | Select the mounted volume → Press ⌘E |
The share name typically appears as the container's IP address (e.g., 172.17.0.2) or as configured in Samba.
Verification
After disconnecting, the share should no longer appear in:
- Finder sidebar under "Locations"
- Finder → Go → Network
- Desktop (if "Connected servers" is enabled in Finder preferences)
Sources: README.md:79-85
Step 2: Unmount SSHFS in Container
Once all macOS connections are terminated, the SSHFS mount can be safely removed from inside the container using the fusermount utility.
Unmount Command
Alternatively, if already in a container shell session:
The target path /samba-share is where the SSHFS mount was established, not the underlying symlink target /remote.
Command Breakdown
| Component | Purpose |
|---|---|
fusermount | FUSE filesystem management utility |
-u | Unmount option |
/samba-share | Mount point to unmount |
Sources: README.md:71-77
Handling "Device or Resource Busy" Error
The most common unmounting error occurs when attempting to unmount the SSHFS filesystem while macOS Finder still has an active connection to the Samba share.
Error Message
fusermount: failed to unmount /samba-share: Device or resource busy
Root Cause
This error indicates that processes are actively using files or directories within the mount point. In sshfs-mac-docker, this is typically caused by:
- Active SMB connections from macOS Finder
- Open file handles from Samba daemon (
smbd) - Working directory set to a path within
/samba-share
flowchart TD
Start["fusermount -u /samba-share fails:\nDevice or resource busy"]
CheckFinder["Check: Is macOS Finder\nconnected to smb://container-ip?"]
EjectFinder["Eject share from\nmacOS Finder"]
RetryUnmount["Retry: fusermount -u /samba-share"]
CheckLsof["Check: lsof /samba-share\nShows open files?"]
KillProcesses["Identify and terminate\nprocesses using mount"]
ForceStop["Force stop container:\ndocker stop docker-sshfs"]
Success["Unmount successful"]
Start --> CheckFinder
CheckFinder -->|Yes| EjectFinder
CheckFinder -->|No| CheckLsof
EjectFinder --> RetryUnmount
RetryUnmount -->|Still fails| CheckLsof
RetryUnmount -->|Success| Success
CheckLsof -->|Yes| KillProcesses
CheckLsof -->|No processes found| ForceStop
KillProcesses --> RetryUnmount
ForceStop --> Success
style Start fill:#fff
style Success fill:#fff
Resolution Flowchart
Sources: README.md:79-85
Diagnostic Commands
When troubleshooting unmounting issues, these commands help identify what is preventing the unmount operation.
Check Open Files
This lists all processes with open file handles in the mount point, including:
- Process ID (PID)
- Process name
- File descriptors
- File paths
Check Mount Status
Verifies whether /samba-share is currently mounted and shows the mount options in use.
Inspect Samba Connections
Displays active SMB sessions, including:
- Connected clients (macOS IP addresses)
- Shared resources in use
- Locked files
Sources: README.md:71-86
sequenceDiagram
actor User
participant Finder as "macOS Finder"
participant Docker as "Docker Engine"
participant Container as "docker-sshfs"
participant SSHFS as "SSHFS Mount\n/samba-share"
participant Remote as "Remote Server"
rect rgb(240, 240, 240)
Note over Finder,Remote: Active State: System in Use
Finder->>Container: SMB connection active
Container->>SSHFS: Serving files via Samba
SSHFS->>Remote: SSH connection active
end
rect rgb(250, 240, 240)
Note over User,Remote: Phase 1: Disconnect macOS Client
User->>Finder: Eject smb://container-ip
Finder-->>Container: Close SMB sessions
Container->>Container: smbd releases file locks
end
rect rgb(240, 250, 240)
Note over User,Remote: Phase 2: Unmount SSHFS
User->>Container: fusermount -u /samba-share
Container->>SSHFS: Unmount request
SSHFS->>Remote: Close SSH connection
Remote-->>SSHFS: Connection terminated
SSHFS-->>Container: Unmount complete
Container-->>User: Success (no output)
end
rect rgb(240, 240, 250)
Note over User,Container: Phase 3: Container Cleanup (Optional)
User->>Docker: docker stop docker-sshfs
Docker->>Container: SIGTERM to smbd
Container->>Container: smbd graceful shutdown
Container-->>Docker: Container stopped
User->>Docker: docker rm docker-sshfs (optional)
Docker-->>User: Container removed
end
Complete Cleanup Sequence
This diagram shows the full lifecycle from active use to complete system shutdown.
Sources: README.md:71-86
Container Lifecycle Management
Beyond unmounting the filesystem, you may need to stop or remove the container itself as part of cleanup operations.
Stopping the Container
This sends a SIGTERM signal to the container's main process (smbd), allowing it to:
- Close active SMB connections gracefully
- Flush buffered data
- Release filesystem locks
The container automatically unmounts all FUSE filesystems during shutdown, though this may fail if macOS connections are still active.
Removing the Container
Removes the stopped container, including:
- Container filesystem layers
- Network configurations
- Port mappings
The Docker image docker-sshfs remains available for creating new containers.
Force Cleanup
If the container cannot be stopped normally (e.g., hung processes), force removal:
This sends SIGKILL, immediately terminating all processes. Use this as a last resort, as it may result in:
- Incomplete file writes on remote server
- Stale SSH connections
- Corrupt cache data
Sources: README.md:26-35
Cleanup Command Reference
Unmounting Operations
| Command | Context | Description |
|---|---|---|
fusermount -u /samba-share | Inside container | Unmount SSHFS filesystem |
docker exec -it docker-sshfs fusermount -u /samba-share | Host shell | Unmount from host without entering container |
lsof /samba-share | Inside container | List processes using the mount |
| `mount | grep samba-share` | Inside container |
Container Lifecycle
| Command | Description |
|---|---|
docker stop docker-sshfs | Gracefully stop container (SIGTERM) |
docker start docker-sshfs | Restart stopped container |
docker restart docker-sshfs | Stop and start container |
docker rm docker-sshfs | Remove stopped container |
docker rm -f docker-sshfs | Force remove (stops if running) |
Image Management
| Command | Description |
|---|---|
| `docker images | grep docker-sshfs` |
docker rmi docker-sshfs | Remove image (no containers must exist) |
docker rmi -f docker-sshfs | Force remove image |
Sources: README.md:71-86 README.md:26-35
stateDiagram-v2
[*] --> Active : Normal operation
state Active {
[*] --> FinderConnected
state FinderConnected {
[*] --> SSHFSMounted : SMB serving files
SSHFSMounted --> [*] : fusermount -u fails (Device busy)
}
}
Active --> FinderDisconnected : Eject from Finder
state FinderDisconnected {
[*] --> SSHFSMounted : No SMB connections
SSHFSMounted --> Unmounted : fusermount -u
Unmounted --> [*] : Clean state
}
FinderDisconnected --> ContainerStopped : docker stop
Active --> ContainerStopped : docker stop -f (forces unmount)
state ContainerStopped {
[*] --> Stopped : smbd terminated
Stopped --> [*] : Can be restarted
}
ContainerStopped --> [*] : docker rm
note right of Active
Attempting fusermount -u
while Finder connected
results in error
end note
note right of ContainerStopped
Container stop automatically
unmounts SSHFS if no
active connections exist
end note
State Transition Diagram
This diagram shows valid state transitions during unmounting and cleanup operations.
Sources: README.md:71-86
Best Practices
Orderly Shutdown Checklist
- Save all open files in macOS applications accessing the remote filesystem
- Close applications that may have file handles open on the remote share
- Eject from Finder using any of the methods described above
- Wait 2-3 seconds for SMB connections to fully terminate
- Execute fusermount inside the container
- Verify unmount using
mount | grep samba-share(should return nothing) - Stop container if no longer needed
Avoiding Common Mistakes
| Mistake | Consequence | Solution |
|---|---|---|
| Unmounting SSHFS before ejecting from Finder | "Device or resource busy" error | Always disconnect macOS first |
| Stopping container with active Finder connection | Finder shows "Connection failed" errors | Eject from Finder before stopping |
| Force-stopping container frequently | Potential data loss on remote server | Use graceful shutdown procedures |
| Not verifying unmount status | Confusion about system state | Check mount status after operations |
Sources: README.md:71-86
Error Recovery Scenarios
Scenario 1: Container Becomes Unresponsive
If the container does not respond to docker exec commands:
This forces a restart, which will:
- Terminate all SSHFS mounts
- Close all Samba connections
- Restart the
smbddaemon
Finder connections must be re-established after restart.
Scenario 2: Unmount Fails Despite No Finder Connection
If fusermount -u fails even after disconnecting from Finder, check for other processes:
Kill any non-Samba processes using the mount:
Then retry unmounting.
Scenario 3: SSH Connection to Remote Server Hangs
If the SSH connection becomes unresponsive, the SSHFS mount may become stale. In this case:
The -l option performs a lazy unmount, detaching the filesystem immediately but cleaning up references when they become idle.
Sources: README.md:71-86
Summary
Proper unmounting requires understanding the dependency chain between macOS Finder, Samba, and SSHFS. The key principle is outside-in cleanup : disconnect external clients first (Finder), then unmount the underlying filesystem (SSHFS), and finally stop the container infrastructure (Docker) if needed.
The "Device or resource busy" error is a symptom of violating this ordering, indicating that active connections still hold references to the filesystem. By following the documented sequence and using the diagnostic commands provided, clean unmounting can be achieved consistently.
Sources: README.md:71-86
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Architecture
Relevant source files
Purpose and Scope
This document provides a detailed examination of the sshfs-mac-docker system architecture, explaining how the three-tier design enables remote filesystem access on macOS without kernel extensions. It covers the protocol translation mechanism, network topology, and security model.
For step-by-step usage instructions, see Getting Started. For implementation details of the Docker container, see Container Implementation. For configuration reference, see Configuration Reference.
3.1 Three-Tier Design
The system implements a three-tier architecture that isolates filesystem operations within a Docker container, avoiding the need for macFUSE or kernel extensions on the macOS host.
Layer Overview
| Layer | Components | Responsibility |
|---|---|---|
| Layer 1: macOS Host | Finder, Docker CLI, User Terminal | Client interface and container orchestration |
| Layer 2: Docker Container | smbd, sshfs, filesystem integration | Protocol translation and filesystem bridging |
| Layer 3: Remote Infrastructure | SSH server with target filesystem | Data source |
Container as Protocol Bridge
The Docker container serves as an isolation boundary where FUSE operations can execute without host kernel modifications. The container contains:
Core Services:
smbdprocess running as foreground service Dockerfile33sshfsclient for SSH filesystem mounting Dockerfile6
Directory Structure:
/remote- SSHFS mount point Dockerfile12/samba-share- Samba share root with777permissions Dockerfile:15-21/samba-share/remote- Symbolic link to/remoteDockerfile30
User Context:
sshuseraccount with UID 1000 Dockerfile9- Used by both SSHFS mounts and Samba file operations smb.conf19
Architectural Decision: Why Containerization
Design Rationale: By moving FUSE operations into a Linux container where FUSE is a standard kernel feature, the system avoids the security and compatibility issues of third-party kernel extensions on macOS.
Sources: README.md:3-5 Dockerfile:1-6
3.2 Protocol Translation
The system bridges SSH and SMB protocols through a filesystem-based integration using SSHFS mounts and symbolic links.
flowchart LR
subgraph Remote["Remote SSH Server"]
RemoteFS["Remote Filesystem\nuser@host:path"]
end
subgraph Container["docker-sshfs Container"]
direction TB
subgraph SSHLayer["SSH/SSHFS Layer"]
sshfsCmd["sshfs command\n-o allow_other\n-o uid=1000\n-o gid=1000"]
remoteMnt["/remote mount point"]
end
subgraph FSLayer["Filesystem Integration"]
symlink["/samba-share/remote\nsymbolic link"]
sambaShare["/samba-share\nchmod 777"]
end
subgraph SMBLayer["Samba Layer"]
smbConf["smb.conf\nforce user = sshuser"]
smbd["smbd process\nports 139/445"]
end
sshfsCmd -->|Mounts| remoteMnt
remoteMnt -.->|ln -s /remote| symlink
symlink --> sambaShare
smbConf -->|Configures| smbd
smbd -->|Serves| sambaShare
end
subgraph macOS["macOS Host"]
Finder["Finder SMB Client\nsmb://container-ip"]
end
RemoteFS -->|SSH Protocol| sshfsCmd
smbd -->|SMB Protocol| Finder
Protocol Flow
Critical Integration Points
SSHFS Mount Command:
| Option | Purpose | Consequence if Omitted |
|---|---|---|
allow_other | Permits access by users other than mount owner | Samba cannot read mounted files |
uid=1000 | Sets file owner to match sshuser UID | Write operations fail (read-only) |
gid=1000 | Sets file group to match sshuser GID | Write operations fail (read-only) |
Symbolic Link Integration:
The link at Dockerfile30 connects the SSHFS mount point to the Samba share:
This creates a zero-copy integration where files are not duplicated. The symbolic link allows Samba to serve files that exist only in the FUSE mount.
FUSE Configuration:
The user_allow_other setting Dockerfile24 modifies /etc/fuse.conf to enable the allow_other mount option. Without this, FUSE would reject the option even if specified.
sequenceDiagram
participant Finder as "macOS Finder"
participant smbd as "smbd process"
participant fs as "/samba-share"
participant link as "Symbolic Link"
participant remote as "/remote (FUSE)"
participant sshfs as "sshfs client"
participant ssh as "Remote SSH Server"
Note over Finder,ssh: Read Operation
Finder->>smbd: SMB read request
smbd->>fs: Access /samba-share/remote/file.txt
fs->>link: Follow symlink
link->>remote: Read from /remote/file.txt
remote->>sshfs: FUSE read operation
sshfs->>ssh: SSH SFTP read
ssh-->>sshfs: File data
sshfs-->>remote: Return data
remote-->>link: Return data
link-->>fs: Return data
fs-->>smbd: Return data
smbd-->>Finder: SMB read response
Note over Finder,ssh: Write Operation
Finder->>smbd: SMB write request
smbd->>fs: Write to /samba-share/remote/file.txt\n(as sshuser via force user)
fs->>link: Follow symlink
link->>remote: Write to /remote/file.txt\n(uid=1000 allows write)
remote->>sshfs: FUSE write operation
sshfs->>ssh: SSH SFTP write
ssh-->>sshfs: Write confirmation
sshfs-->>remote: Write complete
remote-->>link: Write complete
link-->>fs: Write complete
fs-->>smbd: Write complete
smbd-->>Finder: SMB write response
Bidirectional Data Flow
Write Access Requirements:
- SSHFS mount must include
uid=1000,gid=1000README.md53 - Samba must use
force user = sshusersmb.conf19 - Directory permissions must be
777Dockerfile21
Without all three, writes from macOS will fail with permission errors.
Sources: README.md:46-53 Dockerfile:23-30 smb.conf:10-19
graph TB
subgraph HostNetwork["macOS Host (127.0.0.1)"]
direction TB
HostPort139["Host Port 139\nNetBIOS Session"]
HostPort445["Host Port 445\nSMB over TCP"]
FinderClient["Finder SMB Client"]
DockerEngine["Docker Engine"]
end
subgraph DockerNetwork["Docker Bridge Network"]
ContainerIP["Container IP\n(e.g., 172.17.0.2)\nDynamic Assignment"]
subgraph Container["docker-sshfs Container"]
ContainerPort139["Container Port 139\nEXPOSE 139"]
ContainerPort445["Container Port 445\nEXPOSE 445"]
smbd["smbd --foreground"]
end
end
subgraph External["External Network"]
RemoteSSH["Remote SSH Server\nPort 22"]
end
DockerEngine -->|-p 127.0.0.1:139:139| HostPort139
DockerEngine -->|-p 127.0.0.1:445:445| HostPort445
HostPort139 -.->|Forwarded| ContainerPort139
HostPort445 -.->|Forwarded| ContainerPort445
ContainerPort139 --> smbd
ContainerPort445 --> smbd
FinderClient -.->|Must use Container IP NOT localhost| ContainerIP
ContainerIP --> smbd
Container -->|Outbound SSH Port 22| RemoteSSH
3.3 Network Architecture
The network topology uses port forwarding to expose Samba services while maintaining localhost-only access from the host.
Port Mapping Configuration
Port Forwarding Analysis
Docker Run Command:
| Flag | Purpose | Security Implication |
|---|---|---|
--privileged | Allows FUSE operations | Container has elevated privileges |
-p 127.0.0.1:139:139 | Forward NetBIOS port to localhost only | Not accessible from external network |
-p 127.0.0.1:445:445 | Forward SMB port to localhost only | Not accessible from external network |
Exposed Ports in Dockerfile:
These declarations document the ports but do not create actual port mappings. The mappings are created by the -p flags in the docker run command.
Localhost Connection Limitation
Issue: Despite port forwarding to 127.0.0.1, Finder cannot connect using smb://localhost or smb://127.0.0.1.
Reason: The SMB protocol implementation in macOS Finder requires discovery of the container's actual IP address within the Docker bridge network.
Discovery Command:
Connection String:
smb://172.17.0.2 # Example IP, varies per container
Network Topology Table
| Network Element | Address/Port | Access Scope | Configuration Source |
|---|---|---|---|
| Samba NetBIOS | Container:139 | Container internal | Dockerfile27 |
| Samba SMB | Container:445 | Container internal | Dockerfile27 |
| Host NetBIOS | 127.0.0.1:139 | Localhost only | README.md31 |
| Host SMB | 127.0.0.1:445 | Localhost only | README.md31 |
| Container IP | 172.17.0.0/16 | Docker bridge network | Docker runtime |
| Remote SSH | Remote:22 | Internet/VPN | User specified |
Sources: README.md:31-65 Dockerfile27
graph TB
subgraph Boundary["Security Boundary: Docker Container"]
direction TB
subgraph Access["Access Control Layer"]
GuestAuth["Guest Authentication\nmap to guest = bad user\nguest ok = yes"]
ForceUser["Force User Mapping\nforce user = sshuser\nAll operations as UID 1000"]
end
subgraph Privilege["Privilege Layer"]
PrivContainer["Privileged Container\n--privileged flag\nRequired for FUSE"]
SSHUserCtx["sshuser Context\nUID=1000 GID=1000\nPassword: sshpass"]
end
subgraph Protocol["Protocol Security"]
SMB2["SMB2+ Only\nclient min protocol = SMB2\nserver min protocol = SMB2"]
SSHEncrypt["SSH Encryption\nSFTP operations"]
end
subgraph FileSystem["Filesystem Permissions"]
Dir777["/samba-share\nchmod 777\nWorld writable"]
FUSEOpts["SSHFS Options\nuid=1000 gid=1000\nForce ownership"]
end
end
subgraph Threats["Mitigated Threats"]
NoAuth["Unauthenticated Access\nfrom macOS"]
MultiUser["Multiple User Identities"]
Legacy["Legacy SMB1 Protocol"]
end
GuestAuth -.->|Mitigates| NoAuth
ForceUser -.->|Mitigates| MultiUser
SMB2 -.->|Mitigates| Legacy
subgraph Accepted["Accepted Risks"]
PrivEsc["Container Privilege Escalation"]
GuestAccess["Guest Access Model"]
LocalOnly["Localhost Trust Model"]
end
PrivContainer -.->|Accepts| PrivEsc
GuestAuth -.->|Accepts| GuestAccess
3.4 Security Model
The system implements a multi-layer security model balancing convenience (guest access) with controlled access through user identity enforcement.
Security Architecture
Guest Authentication Model
Samba Configuration:
| Setting | Value | Effect |
|---|---|---|
security | user | Requires user-level authentication |
map to guest | bad user | Invalid usernames map to guest account |
guest ok | yes | Allow guest connections |
guest only | yes | Force all connections to use guest account |
Workflow: When Finder connects with any credentials (or no credentials), Samba maps the connection to the guest account, which is then forced to operate as sshuser.
Forced User Identity
Configuration:
Purpose: All file operations, regardless of the connecting user's identity, execute as sshuser (UID 1000). This ensures:
- Consistent ownership of created files
- Write permissions match SSHFS mount options
- No privilege escalation beyond container boundaries
User Creation:
Privileged Container Requirement
Flag:
Justification: FUSE operations require device access (/dev/fuse) and the ability to perform mount operations, which are restricted in unprivileged containers.
Risk: The privileged flag grants the container extensive capabilities. Compromise of the container could affect the host system.
Mitigation:
- Localhost-only port binding (
127.0.0.1) README.md31 - No direct host filesystem mounts
- Isolated network namespace (Docker bridge)
Protocol Enforcement
SMB Protocol Version:
Rationale: SMB1 has known vulnerabilities. Enforcing SMB2 minimum ensures secure communication between Finder and the container.
Filesystem Permission Model
Samba Share Permissions:
| Permission | User | Group | Others |
|---|---|---|---|
| Read | ✓ | ✓ | ✓ |
| Write | ✓ | ✓ | ✓ |
| Execute | ✓ | ✓ | ✓ |
Why 777: The directory must be writable by the Samba process (running as smbd) and accessible through the symbolic link to the FUSE mount. Combined with force user, all writes ultimately occur as sshuser.
SSHFS Mount Ownership:
Forces all files in the remote mount to appear owned by UID 1000 (sshuser), enabling write operations.
Security Boundaries Summary
| Boundary | Protection | Threat Model |
|---|---|---|
| Docker isolation | Container → Host | Limits impact of container compromise |
| Localhost binding | External → Container | Prevents network exposure |
| Forced user identity | Client → Filesystem | Prevents privilege escalation via SMB |
| SMB2 enforcement | Client → Server | Prevents SMB1 vulnerabilities |
| SSH encryption | Container → Remote | Protects data in transit |
Limitations:
- Guest authentication provides no user accountability
- Privileged container has extensive host capabilities
- No authentication on SMB connection
- Password stored in image (
sshpass) Dockerfile9
Sources: smb.conf:1-19 Dockerfile:9-21 README.md31
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Three-Tier Design
Relevant source files
Purpose and Scope
This page documents the fundamental three-tier architecture that enables sshfs-mac-docker to provide SSHFS functionality on macOS without requiring macFUSE or kernel extensions. The design isolates complex filesystem operations within a Docker container while exposing results through standard network protocols.
For details on how SSH and SMB protocols are bridged within the container, see Protocol Translation. For network configuration and port mapping specifics, see Network Architecture.
Architectural Overview
The system implements a three-tier design where each tier has distinct responsibilities and runs in different execution environments:
| Tier | Environment | Primary Role | Key Technologies |
|---|---|---|---|
| Tier 1 | macOS Host | User interface and container management | Finder, Docker CLI |
| Tier 2 | Docker Container | Protocol bridging and filesystem operations | SSHFS, Samba, FUSE |
| Tier 3 | Remote Server | Data storage and SSH authentication | SSH daemon, remote filesystem |
This separation is the core innovation that eliminates the need for macFUSE installation on macOS. All FUSE operations are contained within the Docker container's Linux environment, where FUSE is a native, well-supported technology.
Sources: README.md:1-5
The Three-Tier Structure
Diagram: Three-Tier Architecture with Code Entity Mapping
This diagram maps the architectural tiers to specific code artifacts, showing how the Dockerfile constructs each component.
Sources: README.md:1-90 Dockerfile:1-34
Tier 1: macOS Host Layer
The macOS host layer is intentionally minimal and avoids any kernel-level modifications. This tier's responsibilities are limited to:
Responsibilities
- Container Lifecycle Management : Building and running the Docker container via
docker buildanddocker runcommands - User Interaction : Providing the terminal interface for executing commands within the container via
docker exec - File Access : Mounting the SMB share through macOS Finder's native SMB client
What Does NOT Run Here
Critically, this tier does not include:
- FUSE kernel extensions (no macFUSE required)
- SSHFS client software
- Any SSH connection handling to remote servers
- Filesystem mounting operations
By excluding these components from the macOS host, the system avoids the primary pain point that sshfs-mac-docker was designed to solve: the requirement to install macFUSE and accept kernel extension modifications.
Sources: README.md:1-3
Tier 2: Docker Container Layer
The container layer is the system's core, acting as a protocol bridge and isolation boundary. This tier contains all complex filesystem operations within a controlled Linux environment.
Container Build-Time Setup
The Dockerfile:1-34 constructs this tier through several distinct phases:
| Build Phase | Lines | Purpose | Key Artifacts |
|---|---|---|---|
| Base Image | 2 | Provides Ubuntu Linux environment | ubuntu:latest |
| Package Installation | 4-6 | Installs SSHFS and Samba | sshfs, samba packages |
| User Creation | 8-9 | Creates non-root user for SSHFS | sshuser account |
| Directory Structure | 11-15 | Establishes mount points | /remote, /samba-share |
| Configuration | 17-24 | Sets up Samba and FUSE configs | smb.conf, fuse.conf |
| Integration | 29-30 | Links SSHFS mount to Samba share | Symbolic link |
| Service Launch | 32-33 | Starts Samba daemon | smbd process |
Runtime Components
Diagram: Container Internal Structure Mapping to Dockerfile
This diagram shows how runtime components correspond to build-time Dockerfile instructions.
Directory Integration
The symbolic link created at Dockerfile30 is the critical integration point:
This creates /samba-share/remote as a symbolic link pointing to /remote, where SSHFS will mount the remote filesystem. The Samba server serves /samba-share Dockerfile21 making the SSHFS-mounted content accessible via SMB without data duplication.
Sources: Dockerfile:1-34
Tier 3: Remote Infrastructure Layer
The remote infrastructure tier is external to the Docker environment and consists of:
- Remote SSH Server : The target system that the user specifies as
user@host:pathin thesshfscommand README.md49 - Remote Filesystem : The actual files and directories being accessed
- SSH Authentication : Standard SSH protocol authentication (password or key-based)
This tier is completely unaware that access is being proxied through a Docker container and SMB protocol. From the remote server's perspective, it's handling a standard SSHFS connection.
Sources: README.md:36-53
Isolation Strategy and Kernel Extension Avoidance
The macFUSE Problem
On macOS, SSHFS traditionally requires macFUSE (formerly OSXFUSE), which necessitates:
- Installing a kernel extension (kext)
- Allowing system extensions in Security & Privacy settings
- Potential compatibility issues with macOS updates
- Security concerns about third-party kernel code
Sources: README.md3
The Container Solution
The three-tier design solves this by:
Diagram: Comparison of Approaches - Traditional vs Container-Based
Why This Works
| Aspect | Traditional SSHFS | sshfs-mac-docker |
|---|---|---|
| FUSE Location | macOS kernel space | Docker container (Linux userspace) |
| Requires macFUSE | Yes | No |
| Kernel Extensions | Required on macOS | Only within container (isolated) |
| macOS System Modifications | Yes | None |
| SSH Client | Runs on macOS | Runs in container |
| File Access Protocol | Direct FUSE | SMB (native macOS support) |
The Docker container provides a Linux environment where FUSE is natively supported Dockerfile:5-6 The container's --privileged flag README.md31 allows FUSE operations within the container without affecting the host macOS kernel.
Sources: README.md:1-31 Dockerfile:5-6
Component Distribution Across Tiers
The following table maps specific components and files to their respective tiers:
| Component | Tier | File/Path | Created By | Purpose |
|---|---|---|---|---|
| Docker CLI | 1 | N/A | macOS | Container management |
| Finder | 1 | N/A | macOS | SMB client |
ubuntu:latest | 2 | N/A | Dockerfile:2 | Base image |
sshfs package | 2 | N/A | Dockerfile:6 | SSHFS client |
samba package | 2 | N/A | Dockerfile:6 | SMB server |
sshuser account | 2 | N/A | Dockerfile:9 | Non-root user |
/remote directory | 2 | /remote | Dockerfile:12 | SSHFS mount point |
/samba-share directory | 2 | /samba-share | Dockerfile:15 | Samba share root |
smb.conf | 2 | /etc/samba/smb.conf | Dockerfile:18 | Samba configuration |
fuse.conf | 2 | /etc/fuse.conf | Dockerfile:24 | FUSE permissions |
| Symbolic link | 2 | /samba-share/remote | Dockerfile:30 | Integration point |
smbd process | 2 | N/A | Dockerfile:33 | SMB daemon |
| Remote SSH Server | 3 | user@host | External | SSH daemon |
| Remote Filesystem | 3 | user@host:path | External | Data source |
Sources: Dockerfile:1-34 README.md:49-60
Data Flow Across Tiers
The three tiers collaborate to enable seamless file access:
Diagram: Cross-Tier Data Flow for File Operations
Each tier maintains clear boundaries:
- Tier 1 handles only SMB protocol communication
- Tier 2 translates between SMB and SSH/FUSE protocols
- Tier 3 provides the actual file data via SSH
Sources: README.md:46-69
Summary
The three-tier design achieves its primary goal of avoiding macFUSE by:
- Isolating FUSE operations within a Docker container (Tier 2) where Linux provides native FUSE support
- Exposing the remote filesystem through SMB, which macOS (Tier 1) natively supports
- Delegating actual file storage and SSH operations to the remote server (Tier 3)
This architecture requires no modifications to the macOS host system, making it a non-invasive solution for accessing remote filesystems via SSHFS on macOS.
Sources: README.md:1-90 Dockerfile:1-34
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Protocol Translation
Relevant source files
Purpose and Scope
This document explains how sshfs-mac-docker performs protocol translation to bridge SSH and SMB protocols. The system translates between SSH-based remote filesystem access (via SSHFS) and SMB-based local network sharing (via Samba) using filesystem operations and symbolic links within the Docker container.
For information about the overall three-tier architecture, see Three-Tier Design. For networking details including port mappings, see Network Architecture. For filesystem layout details, see Filesystem Layout.
Protocol Translation Overview
The system performs protocol translation by layering three distinct technologies—SSH, FUSE/SSHFS, and SMB/Samba—and connecting them through filesystem primitives. The translation occurs entirely within the Docker container's userspace, avoiding any kernel modifications on the macOS host.
graph TB
subgraph "Remote Source"
RemoteSSH["Remote SSH Server\nuser@host:path"]
end
subgraph "Container Protocol Bridge"
SSHLayer["SSH Protocol Layer"]
FUSELayer["FUSE Filesystem Layer"]
FileSystem["Filesystem Integration"]
SMBLayer["SMB Protocol Layer"]
SSHLayer -->|FUSE operations| FUSELayer
FUSELayer -->|mounts to /remote| FileSystem
FileSystem -->|symlink to /samba-share/remote| SMBLayer
end
subgraph "macOS Consumer"
FinderSMB["macOS Finder\nSMB Client"]
end
RemoteSSH -->|SSH port 22| SSHLayer
SMBLayer -->|SMB ports 139/445| FinderSMB
Protocol Translation Layers
Diagram: Protocol Translation Stack
The translation chain transforms SSH-based remote access into SMB-based network share access through intermediate filesystem representations.
Sources: README.md, Dockerfile, smb.conf
SSH to FUSE Translation
The first translation occurs when SSHFS converts SSH protocol operations into FUSE filesystem operations, mounting the remote filesystem at /remote within the container.
SSHFS Mount Process
The SSHFS mount command translates remote SSH access into local filesystem operations:
This command is executed inside the container via docker exec -it docker-sshfs bash README.md41
SSHFS Mount Options Mapping
Diagram: SSHFS Mount Option Translation
Critical Mount Options
| Mount Option | Purpose | Without It |
|---|---|---|
allow_other | Allows users other than the mounting user to access the mount | Samba service cannot access files mounted by sshuser |
uid=1000 | Sets file ownership to UID 1000 (sshuser) | Write operations fail; filesystem appears read-only |
gid=1000 | Sets group ownership to GID 1000 (sshuser group) | Write operations fail; filesystem appears read-only |
The allow_other option requires the user_allow_other configuration in /etc/fuse.conf Dockerfile24:
user_allow_other
This configuration is added during container build to enable cross-user filesystem access between the SSHFS mount (owned by sshuser) and the Samba service (running as a different process).
Sources: README.md:49-53, Dockerfile:24
graph TB
subgraph "SSHFS Mount"
RemoteDir["/remote\nFUSE mount point\nCreated: Dockerfile:12"]
RemoteFiles["Remote files\nvia SSHFS"]
end
subgraph "Samba Share Root"
SambaDir["/samba-share\nSamba root directory\nCreated: Dockerfile:15\nPermissions: 777"]
SymLink["/samba-share/remote\nSymbolic link\nCreated: Dockerfile:30"]
end
RemoteFiles --> RemoteDir
RemoteDir -->|ln -s /remote /samba-share/remote| SymLink
SymLink -->|part of| SambaDir
Filesystem Integration Layer
The integration between the SSHFS mount at /remote and the Samba share at /samba-share occurs through a symbolic link, created during container build.
Symbolic Link Integration
Diagram: Symbolic Link Integration Point
The symbolic link is created during the Docker image build process Dockerfile30:
This creates /samba-share/remote as a symbolic link pointing to /remote. The symbolic link is permanent—created once during image build and persisting across container restarts.
Directory Creation Sequence
The directories and symbolic link are created in this order during build:
/remotedirectory created Dockerfile12/samba-sharedirectory created Dockerfile15/samba-sharepermissions set to777Dockerfile21- Symbolic link
/samba-share/remote → /remotecreated Dockerfile30
The 777 permissions on /samba-share Dockerfile21 ensure that:
- The Samba service can access the directory
- Files created through SMB inherit permissive access
- The symbolic link remains accessible to all processes
Sources: Dockerfile:12, Dockerfile:15, Dockerfile:21, Dockerfile:30
graph TB
subgraph "Filesystem Layer"
SambaShare["/samba-share\nServed directory"]
SymLink["/samba-share/remote\n→ /remote"]
RemoteMount["/remote\nSSHFS mount"]
end
subgraph "Samba Configuration"
ShareDef["[SSHFS Share]\nsmb.conf:10-19"]
PathConf["path = /samba-share\nsmb.conf:11"]
GuestConf["guest ok = yes\nsmb.conf:13"]
ForceUser["force user = sshuser\nsmb.conf:19"]
WritableConf["writable = yes\nsmb.conf:12"]
end
subgraph "SMB Protocol"
SMBPorts["Ports 139/445\nDockerfile:27"]
SMBClient["macOS SMB Client"]
end
ShareDef --> PathConf
ShareDef --> GuestConf
ShareDef --> ForceUser
ShareDef --> WritableConf
PathConf --> SambaShare
SambaShare --> SymLink
SymLink -.->|follows link| RemoteMount
ForceUser -.->|all operations as| sshuser["sshuser\nUID 1000"]
sshuser -.->|matches| RemoteMount
ShareDef --> SMBPorts
SMBPorts --> SMBClient
FUSE to SMB Translation
The final translation occurs when Samba serves the /samba-share directory (which contains the symbolic link to the SSHFS mount) over the SMB protocol.
Samba Configuration for Protocol Bridge
Diagram: Samba Configuration for Protocol Translation
The Samba share is defined in smb.conf smb.conf:10-19:
[SSHFS Share]
path = /samba-share
writable = yes
guest ok = yes
guest only = yes
read only = no
browseable = yes
create mask = 0777
directory mask = 0777
force user = sshuser
Critical Configuration Elements
| Configuration | Purpose | Impact on Protocol Translation |
|---|---|---|
path = /samba-share | Sets root directory for SMB share | Exposes directory containing symbolic link to SSHFS mount |
force user = sshuser | Forces all file operations to run as sshuser | Matches UID/GID from SSHFS mount options, enabling write access |
guest ok = yes | Allows guest authentication | Simplifies macOS client connection, avoiding credential management |
writable = yes | Enables write operations | Allows bidirectional protocol translation (read and write) |
The force user = sshuser smb.conf19 is critical because:
- SSHFS mounts files with
uid=1000,gid=1000(matchingsshuser) README.md49 - Samba operations run as
sshuser, matching the file ownership - This alignment enables write access through the protocol bridge
Sources: smb.conf:10-19, README.md:49
sequenceDiagram
participant Finder as "macOS Finder"
participant SMB as "Samba Service\n(smbd)"
participant FS as "Filesystem Layer\n(/samba-share)"
participant Link as "Symbolic Link\n(/samba-share/remote)"
participant Mount as "FUSE Mount\n(/remote)"
participant SSHFS as "SSHFS Process"
participant Remote as "Remote SSH Server"
Finder->>SMB: SMB read request
SMB->>FS: Access /samba-share/remote/file.txt\n(as sshuser)
FS->>Link: Follow symbolic link
Link->>Mount: Resolve to /remote/file.txt
Mount->>SSHFS: FUSE read operation
SSHFS->>Remote: SSH file read
Remote-->>SSHFS: File contents
SSHFS-->>Mount: FUSE response
Mount-->>Link: File data
Link-->>FS: File data
FS-->>SMB: File data (as sshuser)
SMB-->>Finder: SMB response
Bidirectional Data Flow
The protocol translation supports both read and write operations, with data flowing through the complete translation stack in both directions.
Read Operation Flow
Diagram: Read Operation Protocol Translation Flow
Write Operation Flow
Diagram: Write Operation Protocol Translation Flow
Write Access Requirements
Write operations succeed because of aligned user identity across the translation layers:
- SSHFS Mount: Files owned by
uid=1000,gid=1000README.md49 - Samba Configuration:
force user = sshusersmb.conf19 - User Account:
sshuserhas UID 1000, GID 1000 Dockerfile9
Without this alignment, write operations fail. The uid=1000,gid=1000 mount options are noted as required for write access README.md53:
uid=1000,gid=1000 is needed for write-access (otherwise, it will be read-only if omitted)
Sources: README.md:49-53, smb.conf:19, Dockerfile:9
Protocol Translation Component Mapping
The following table maps high-level protocol translation concepts to specific code entities and configuration files:
| Translation Stage | Input Protocol | Output Interface | Code Entity | Configuration |
|---|---|---|---|---|
| Remote to SSH | File I/O | SSH protocol | user@host:path (mount target) | SSH credentials |
| SSH to FUSE | SSH protocol | Filesystem operations | sshfs command README.md49 | -o allow_other,uid=1000,gid=1000 |
| FUSE to Filesystem | FUSE operations | VFS operations | /remote mount point Dockerfile12 | user_allow_other Dockerfile24 |
| Filesystem Integration | VFS operations | VFS operations | /samba-share/remote symlink Dockerfile30 | chmod 777 /samba-share Dockerfile21 |
| Filesystem to SMB | VFS operations | SMB protocol | smbd process Dockerfile33 | smb.conf smb.conf:1-19 |
| SMB to Network | SMB protocol | TCP/IP | Ports 139/445 Dockerfile27 | -p 127.0.0.1:139:139 -p 127.0.0.1:445:445 README.md31 |
Sources: README.md:31, README.md:49, Dockerfile:12, Dockerfile:21, Dockerfile:24, Dockerfile:27, Dockerfile:30, Dockerfile:33, smb.conf:1-19
graph TB
subgraph "FUSE Configuration"
FuseConf["user_allow_other\n/etc/fuse.conf\nDockerfile:24"]
end
subgraph "SSHFS Mount Options"
AllowOther["allow_other\nREADME.md:49"]
UIDMap["uid=1000\nREADME.md:49"]
GIDMap["gid=1000\nREADME.md:49"]
end
subgraph "User Account"
SSHUser["sshuser\nUID 1000, GID 1000\nDockerfile:9"]
end
subgraph "Samba Configuration"
ForceUser["force user = sshuser\nsmb.conf:19"]
GuestOK["guest ok = yes\nsmb.conf:13"]
Writable["writable = yes\nsmb.conf:12"]
end
subgraph "Filesystem"
SharePerms["chmod 777 /samba-share\nDockerfile:21"]
SymLink["ln -s /remote /samba-share/remote\nDockerfile:30"]
end
FuseConf -->|enables| AllowOther
AllowOther -->|allows cross-user access| ForceUser
UIDMap -->|sets ownership| SSHUser
GIDMap -->|sets ownership| SSHUser
SSHUser -->|identity matches| ForceUser
ForceUser -->|runs operations as| SharePerms
SharePerms -->|writable by| SSHUser
GuestOK -->|simplifies auth| ForceUser
Writable -->|enables writes| ForceUser
SymLink -->|integrates| SharePerms
Critical Configuration Dependencies
The protocol translation requires specific configuration elements that work together to enable cross-protocol access:
Configuration Dependency Graph
Diagram: Configuration Dependency Graph for Protocol Translation
All these configuration elements must be correctly set for the protocol translation to function:
user_allow_otherin fuse.conf enables theallow_othermount optionallow_othermount option allows Samba to access SSHFS-mounted filesuid=1000,gid=1000mount options set file ownership to matchsshuserforce user = sshuserin Samba config aligns with SSHFS file ownershipchmod 777 /samba-shareensures permissive access to the share directory- Symbolic link connects the Samba share to the SSHFS mount point
If any single element is missing or misconfigured, the protocol translation fails—typically resulting in permission denied errors or read-only access.
Sources: Dockerfile:9, Dockerfile:21, Dockerfile:24, Dockerfile:30, README.md:49, smb.conf:12-13, smb.conf:19
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Network Architecture
Relevant source files
Purpose and Scope
This page explains the network configuration of the sshfs-mac-docker system, including port mappings, container networking, and the critical limitation that prevents localhost-based SMB connections. For information about the protocols themselves (SSH and SMB), see Protocol Translation. For security implications of the network design, see Security Model.
Port Mapping Configuration
The container exposes Samba services through explicit port forwarding defined in the docker run command:
These port mappings bind the container's Samba ports to the host's localhost interface only.
Port Definitions
| Port | Protocol | Purpose |
|---|---|---|
| 139 | NetBIOS Session Service | Legacy SMB over NetBIOS |
| 445 | SMB over TCP/IP | Modern SMB direct over TCP |
Both ports are mapped to ensure compatibility with different macOS versions and SMB client configurations. The 127.0.0.1 prefix restricts access to the host machine only, preventing external network exposure.
Sources: README.md31 README.md34 Dockerfile27
Docker Bridge Network Topology
The container operates on Docker's default bridge network, receiving a dynamically assigned IP address in the 172.17.0.0/16 subnet (typical default). This creates a two-tier network architecture:
Diagram: Docker Bridge Network Topology
graph TB
subgraph HostNetwork["macOS Host Network Space"]
HostLoopback["127.0.0.1\n(localhost)"]
HostPhysical["Physical Network Interfaces"]
FinderClient["Finder SMB Client"]
end
subgraph DockerBridge["Docker Bridge Network (172.17.0.0/16)"]
ContainerIP["Container IP\n(e.g., 172.17.0.2)\nDynamically Assigned"]
subgraph Container["docker-sshfs Container"]
Port139["Port 139\n(NetBIOS)"]
Port445["Port 445\n(SMB/TCP)"]
SmbdProcess["smbd process"]
end
end
subgraph ExternalNetwork["External Network"]
RemoteSSH["Remote SSH Server\nPort 22"]
end
HostLoopback -.->|Port Forwarding -p 127.0.0.1:139:139| Port139
HostLoopback -.->|Port Forwarding -p 127.0.0.1:445:445| Port445
Port139 --> SmbdProcess
Port445 --> SmbdProcess
FinderClient -->|Direct Connection smb://172.17.0.2| ContainerIP
ContainerIP --> SmbdProcess
Container -->|Outbound SSH Port 22| RemoteSSH
HostPhysical -.->|Docker NAT| DockerBridge
The container exists in an isolated network namespace with its own IP address. Port forwarding creates a mapping from 127.0.0.1 on the host to the container's internal ports, but this forwarding has limitations (see next section).
Sources: README.md:57-61
The Localhost Limitation
A critical quirk of this system is that macOS Finder cannot connect to the Samba share usingsmb://127.0.0.1 or smb://localhost, despite the port forwarding configuration. Instead, clients must use the container's internal Docker IP address.
Discovery of Container IP
The container's IP address must be discovered dynamically using Docker's inspection command:
This command queries the container's network settings and returns the assigned IP address (e.g., 172.17.0.2).
Technical Explanation
The localhost limitation exists because:
- Port forwarding operates at Layer 4 (transport layer), forwarding TCP connections from
127.0.0.1to the container - SMB protocol negotiation includes hostname/IP checks at the application layer
- macOS's SMB client validates the target address during connection establishment
- When connecting to
127.0.0.1, the SMB client receives responses from a different IP address (the container's bridge IP), causing address mismatch errors
The workaround is to bypass port forwarding entirely and connect directly to the container's IP address on the Docker bridge network. This works because:
- Docker's default bridge network is accessible from the host
- The container's ports (139, 445) are directly reachable on the bridge network
- No address translation occurs, avoiding SMB protocol conflicts
Platform Differences
The README notes that OrbStack is highly recommended over Docker Desktop for this reason. OrbStack provides better network transparency between the host and container networks, making direct container IP connections more reliable.
Sources: README.md:57-58 README.md:60-61 README.md9
Connection Types and Directions
The system handles two distinct types of network connections with different security profiles:
Diagram: Bidirectional Network Connections
graph LR
subgraph Inbound["Inbound Connections (SMB)"]
MacOS["macOS Finder"]
Direction1["Direction: IN"]
Destination1["Container Port 139/445"]
Security1["Security: Localhost-only\nvia port forwarding"]
end
subgraph Container["Container Network"]
SmbdSvc["smbd service"]
SSHFSClient["sshfs client"]
end
subgraph Outbound["Outbound Connections (SSH)"]
RemoteHost["Remote SSH Server"]
Direction2["Direction: OUT"]
Source2["Container (any port)"]
Security2["Security: Encrypted SSH\nNo firewall required"]
end
MacOS -->|smb://172.17.0.2| SmbdSvc
SSHFSClient -->|Port 22| RemoteHost
Direction1 -.-> Destination1
Direction2 -.-> Source2
Security1 -.-> MacOS
Security2 -.-> RemoteHost
Inbound (SMB)
- Source: macOS Finder on the host
- Destination: Container ports 139/445
- Protocol: SMB/CIFS
- Security: Port forwarding limits access to
127.0.0.1, but clients connect via container IP - Configuration: Defined in
docker runcommand
Outbound (SSH)
- Source: Container's
sshfsclient process - Destination: Remote SSH server, port 22 (standard SSH)
- Protocol: SSH with FUSE extensions
- Security: Encrypted SSH tunnel, uses standard SSH authentication
- Configuration: Specified in
sshfsmount command arguments
Sources: README.md31 README.md49
flowchart TD
subgraph macOS["macOS Host (127.0.0.1)"]
Finder["Finder Client"]
DockerCLI["docker inspect command"]
end
subgraph DockerNet["Docker Bridge Network"]
ContainerNS["Container Network Namespace\n(172.17.0.x)"]
subgraph Ports["Exposed Ports"]
P139["Port 139"]
P445["Port 445"]
end
subgraph Services["Running Services"]
Smbd["smbd\n(Samba daemon)"]
SSHFS["sshfs\n(FUSE client)"]
end
subgraph Storage["Filesystem"]
SambaDir["/samba-share"]
RemoteDir["/remote"]
SymLink["Symbolic Link\n/samba-share/remote"]
end
end
subgraph External["External Network"]
RemoteServer["Remote SSH Server\nuser@host:path"]
end
DockerCLI -->|Query container IP| ContainerNS
ContainerNS -->|Return 172.17.0.x| DockerCLI
Finder -->|1. SMB Connection smb://172.17.0.x| P445
P445 --> Smbd
Smbd -->|2. Serve files from| SambaDir
SambaDir -.->|3. Contains symlink| SymLink
SymLink -.->|4. Points to| RemoteDir
RemoteDir -->|5. Mounted by| SSHFS
SSHFS -->|6. SSH Protocol Port 22| RemoteServer
RemoteServer -.->|7. File data returns| SSHFS
SSHFS -.->|8. FUSE operations| RemoteDir
RemoteDir -.->|9. Via symlink| SambaDir
SambaDir -.->|10. SMB protocol| Smbd
Smbd -.->|11. Network packets| P445
P445 -.->|12. To macOS| Finder
Complete Network Flow
The following diagram shows the complete data path from macOS Finder to the remote SSH server:
Diagram: Complete Network Data Flow
Flow Stages
- IP Discovery: User runs
docker inspectto find container IP - SMB Connection: Finder connects to
smb://172.17.0.x:445 - Request Routing:
smbdreceives request for files in/samba-share - Symbolic Link Traversal: Path resolves to
/remotevia symlink - FUSE Intercept: SSHFS intercepts filesystem operations on
/remote - SSH Transport: Request forwarded to remote server over SSH
- Remote Execution: Remote server performs actual filesystem operation
- Response Return: Data travels back through SSHFS → symlink → Samba → macOS
This multi-hop architecture explains why the system requires careful configuration of permissions (allow_other in SSHFS, force user in Samba) to maintain consistent access across all layers.
Sources: README.md:57-69 README.md49 Dockerfile30
Network Security Model
The network configuration implements a principle of least exposure :
| Component | Network Exposure | Justification |
|---|---|---|
| Samba ports (139/445) | Localhost only (127.0.0.1 binding) | Prevents external network access to file shares |
| Container IP | Docker bridge network only | Not exposed to external networks without additional Docker configuration |
| SSH client | Outbound only | No inbound SSH access to container; connects to remote servers |
The --privileged flag required for the container is for FUSE operations, not network access. Network restrictions are maintained through port binding and Docker's default firewall rules.
For detailed security implications, including authentication and permission models, see Security Model.
Sources: README.md31 README.md34
Platform-Specific Considerations
OrbStack vs Docker Desktop
The README strongly recommends OrbStack over Docker Desktop due to networking differences:
- OrbStack: Provides seamless network integration between host and container, making direct container IP connections reliable
- Docker Desktop: May require additional network route modifications for SMB to function properly
The exact networking behavior depends on Docker Desktop's version and configuration, particularly its VM-based networking on macOS.
For more details on platform-specific setup, see Prerequisites and Platform Requirements. For troubleshooting Docker Desktop networking issues, see Platform-Specific Issues.
Sources: README.md9
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Security Model
Relevant source files
Purpose and Scope
This document describes the security architecture of sshfs-mac-docker, including container privileges, authentication mechanisms, network isolation, and access control policies. The system adopts a defense-in-depth approach with multiple security boundaries, though some boundaries are intentionally relaxed to enable the core functionality of bridging remote SSH filesystems to macOS via SMB.
For configuration details of specific security settings, see Configuration Reference. For troubleshooting connection issues related to security restrictions, see Troubleshooting.
Sources : README.md:1-90 Dockerfile:1-34 smb.conf:1-20
Security Boundaries and Trust Model
The system establishes three distinct security boundaries with different trust levels and access control policies:
Key Trust Assumptions :
graph TB
subgraph External["External Network - Untrusted"]
Internet["Internet"]
RemoteSSH["Remote SSH Server\nAuthentication: SSH keys/password"]
end
subgraph HostBoundary["macOS Host - Trusted Local System"]
HostNetwork["127.0.0.1\nLocalhost Only"]
DockerEngine["Docker Engine"]
Finder["macOS Finder\nAuthentication: Guest"]
end
subgraph ContainerBoundary["Docker Container - Isolated Execution"]
SambaService["smbd\nPorts: 139, 445"]
SSHFSClient["sshfs\nRuns as: sshuser"]
SambaShare["/samba-share\nPermissions: 777"]
RemoteMount["/remote\nMounted via FUSE"]
end
Internet -.->|Blocked by Port Binding| SambaService
HostNetwork -->|Port Forward -p 127.0.0.1:139:139| SambaService
HostNetwork -->|Port Forward -p 127.0.0.1:445:445| SambaService
Finder -->|SMB Protocol Guest Access| SambaService
SambaService -->|Force User: sshuser| SambaShare
SambaShare -.->|Symbolic Link| RemoteMount
SSHFSClient -->|SSH Protocol Authenticated| RemoteSSH
SSHFSClient -->|FUSE Mount allow_other| RemoteMount
DockerEngine -->|--privileged Required for FUSE| ContainerBoundary
- macOS Host : Fully trusted - any local user can connect as guest
- Docker Container : Isolated but privileged execution environment
- Remote SSH Server : External system secured by SSH authentication
Sources : README.md:31-34 README.md:57-68 smb.conf:1-20
Container Privilege Requirements
The container must run with --privileged flag to enable FUSE filesystem operations inside the container.
Privileged Mode Necessity
| Capability | Required For | Alternative |
|---|---|---|
| FUSE device access | Mounting SSHFS filesystems via /dev/fuse | None - FUSE requires kernel privileges |
| System call filtering bypass | FUSE mount/umount operations | None - standard containers block these syscalls |
| Device node access | Character device /dev/fuse | Adding specific --device and --cap-add flags |
Container Runtime Configuration
Security Implications :
- Container has nearly full access to host kernel capabilities
- Compromise of container could lead to host system access
- Mitigated by: running on trusted local system, not exposing ports externally
Sources : README.md31 Dockerfile:1-34
flowchart TB
subgraph External["External Network"]
ExternalClient["External Client\nIP: x.x.x.x"]
end
subgraph HostNetwork["macOS Host Network"]
Localhost["127.0.0.1\nLoopback Interface"]
DockerBridge["docker0 Bridge\n172.17.0.0/16"]
ContainerIP["Container IP\ne.g., 172.17.0.2"]
end
subgraph Container["Container Network Namespace"]
Port139["139/tcp\nNetBIOS"]
Port445["445/tcp\nSMB"]
end
ExternalClient -.->|BLOCKED Not bound to 0.0.0.0| Localhost
Localhost -->|Port Forward 127.0.0.1:139| Port139
Localhost -->|Port Forward 127.0.0.1:445| Port445
ContainerIP -->|Direct Access From Host Only| Port139
ContainerIP -->|Direct Access From Host Only| Port445
DockerBridge -.->|Container Reachable| ContainerIP
Network Isolation Model
The system implements localhost-only binding to prevent external network access to the SMB service.
Port Binding Strategy
The -p flag explicitly binds to 127.0.0.1 rather than 0.0.0.0:
- Configuration :
-p 127.0.0.1:139:139 -p 127.0.0.1:445:445 - Effect : Ports 139 and 445 only accept connections from localhost
- External Access : Blocked - remote clients cannot reach the SMB service
- Docker Network Access : Container IP (e.g.,
172.17.0.2) remains reachable from host
Network Access Quirk
Despite port forwarding to 127.0.0.1, macOS Finder must connect using the container's internal Docker IP address. This is a known limitation documented in README.md:57-61
Sources : README.md:31-34 README.md:57-61 Dockerfile:26-27
Authentication and Authorization Model
The system implements a two-stage authentication model with different authentication mechanisms at each boundary.
sequenceDiagram
participant Finder as "macOS Finder"
participant Samba as "smbd\n(Samba Server)"
participant Filesystem as "/samba-share\n(Force User: sshuser)"
participant SSHFS as "sshfs Client"
participant Remote as "Remote SSH Server"
rect rgb(240, 240, 240)
Note over Finder,Samba: Stage 1: macOS to Container
Finder->>Samba: SMB Connection Request
Samba->>Samba: Check: security = user
Samba->>Samba: Check: map to guest = bad user
Samba->>Samba: Result: Map to guest access
Samba-->>Finder: Access Granted (Guest)
Finder->>Samba: File Operation Request
Samba->>Samba: Apply: force user = sshuser
Samba->>Filesystem: Access as sshuser (UID 1000)
end
rect rgb(245, 245, 245)
Note over SSHFS,Remote: Stage 2: Container to Remote
SSHFS->>Remote: SSH Connection Request
Remote->>Remote: Verify SSH key or password
Remote-->>SSHFS: Authentication Success
SSHFS->>Remote: File Operation Request
Remote->>Remote: Apply remote permissions
Remote-->>SSHFS: File Operation Result
end
Authentication Flow
Stage 1: macOS to Container (SMB)
Authentication Mechanism : Guest access with forced user identity
Configuration : smb.conf:1-20
security = user # Authentication mode
map to guest = bad user # Map failed auth to guest
guest ok = yes # Allow guest connections
guest only = yes # Force guest mode
force user = sshuser # All operations as sshuser
Authorization :
- Identity : All macOS clients mapped to
sshuser(UID 1000) - Access Level : Full read/write to
/samba-share - Enforcement Point : Samba service
Stage 2: Container to Remote (SSH)
Authentication Mechanism : SSH key or password (configured by user)
Command : README.md49
Authorization :
- Identity : Remote user credentials provided during mount
- Access Level : Determined by remote server permissions
- Enforcement Point : Remote SSH server
Sources : smb.conf:1-20 README.md:49-53
User Identity and Permissions
The system uses a forced user identity model to ensure consistent file ownership across all access paths.
User Account Configuration
The container creates a dedicated non-root user for SSHFS operations:
User Creation : Dockerfile9
| Property | Value | Purpose |
|---|---|---|
| Username | sshuser | Consistent identity for all operations |
| Password | sshpass | Not used - authentication via SSH to remote |
| UID | 1000 | Default first user UID on most Linux systems |
| GID | 1000 | Default first user GID on most Linux systems |
| Home Directory | /home/sshuser | Created by -m flag |
File Permission Model
Directory Permissions
Samba Share : Dockerfile21
- Owner : root (implicit)
- Permissions :
rwxrwxrwx(777) - Rationale : Allows any user to access, but
force userensures all operations assshuser
Remote Mount Point : Dockerfile12
- Initial Owner : root
- Runtime Owner :
sshuserafter SSHFS mount withuid=1000,gid=1000
Permission Enforcement Points
- macOS → Samba : No enforcement (guest access)
- Samba → Filesystem :
force user = sshuserapplied by Samba - SSHFS → Remote :
uid=1000,gid=1000ensures files created as UID/GID 1000 - Remote → Files : Remote server's normal permission checks
Sources : Dockerfile9 Dockerfile21 smb.conf19 README.md:49-53
FUSE Security Configuration
The system modifies FUSE's default security policy to enable cross-user filesystem access.
user_allow_other Configuration
Setting : Dockerfile24
Default FUSE Behavior :
- FUSE mounts are only accessible by the mounting user
- Other users (including system services) cannot access the mounted filesystem
- Security measure to prevent unauthorized access to user-mounted filesystems
Why It's Required :
Mount Option Interaction
The user_allow_other setting in /etc/fuse.conf permits use of the allow_other mount option:
SSHFS Command : README.md49
| Option | Purpose | Security Impact |
|---|---|---|
allow_other | Permit access by users other than mount owner | Allows smbd (root) to access sshuser-mounted filesystem |
uid=1000 | Set file owner to UID 1000 | Files appear owned by sshuser |
gid=1000 | Set file group to GID 1000 | Files appear grouped by sshuser |
Without These Settings :
- Samba server cannot read files from SSHFS mount
- macOS Finder would receive "Permission Denied" errors
- System would be non-functional
Sources : Dockerfile24 README.md:49-53
Protocol Security
The system enforces minimum protocol versions to prevent use of insecure legacy protocols.
SMB Protocol Enforcement
Configuration : smb.conf:7-8
client min protocol = SMB2
server min protocol = SMB2
Protocol Security Comparison :
| Protocol Version | Status | Security Issues | Allowed |
|---|---|---|---|
| SMB1 | Deprecated | No encryption, vulnerable to exploits (EternalBlue) | ❌ Blocked |
| SMB2 | Legacy | Basic security, limited encryption | ✓ Minimum |
| SMB3 | Modern | Full encryption support, integrity checking | ✓ Allowed |
Enforcement Points :
- Server : Rejects connections attempting to negotiate SMB1
- Client : Would not initiate SMB1 connections (if acting as client)
SSH Protocol Security
Mechanism : Delegated to OpenSSH client and remote server
Security Considerations :
- Encryption : All SSH traffic encrypted by OpenSSH
- Authentication : SSH keys or passwords (user-configured)
- Host Verification : SSH host key checking (user must accept)
- Protocol Version : Determined by OpenSSH client and remote server negotiation
No Container Configuration : The container does not enforce specific SSH protocol settings. Security depends on:
- Remote server SSH configuration
- OpenSSH client defaults (installed via
apt-get install sshfs) - User-provided credentials and host keys
Sources : smb.conf:7-8 Dockerfile6
graph TB
Feature["Feature: macOS Access to Remote SSH Filesystem"]
Feature --> Tradeoff1["Trade-off 1:\nPrivileged Container"]
Feature --> Tradeoff2["Trade-off 2:\nGuest Access"]
Feature --> Tradeoff3["Trade-off 3:\n777 Permissions"]
Feature --> Tradeoff4["Trade-off 4:\nallow_other"]
Tradeoff1 --> Risk1["Risk: Container escape → host access"]
Tradeoff2 --> Risk2["Risk: No authentication to SMB"]
Tradeoff3 --> Risk3["Risk: Any process can write /samba-share"]
Tradeoff4 --> Risk4["Risk: Cross-user FUSE access"]
Risk1 --> Mitigation1["Mitigation: Localhost-only binding"]
Risk2 --> Mitigation2["Mitigation: Port bound to 127.0.0.1"]
Risk3 --> Mitigation3["Mitigation: Container isolation"]
Risk4 --> Mitigation4["Mitigation: force user = sshuser"]
Security Trade-offs and Limitations
The system makes explicit security trade-offs to achieve its functionality of exposing remote SSH filesystems to macOS without kernel extensions.
Intentional Security Relaxations
Known Security Limitations
| Limitation | Description | Impact | Mitigation |
|---|---|---|---|
| No SMB authentication | Any local user can access | Local users can read/write all files | Intended for single-user systems |
| Privileged container | Container has host kernel access | Container compromise = host compromise | Use only on trusted local machine |
| 777 permissions | World-writable directory | Any container process can modify files | Container is single-purpose |
| Guest-only access | No user identity tracking | Cannot audit which macOS user performed action | Audit at remote SSH server level |
Recommended Security Posture
Appropriate Use Cases :
- Single-user macOS development workstation
- Trusted local network environment
- Non-production/non-sensitive data access
- Personal remote file access scenarios
Inappropriate Use Cases :
- Multi-user shared systems
- Production server environments
- Access to highly sensitive data
- Systems exposed to untrusted networks
Sources : README.md:1-90 smb.conf:1-20 Dockerfile:1-34
Security Configuration Summary
Complete Security Configuration Map
Configuration File References
| Security Control | File | Line(s) | Value |
|---|---|---|---|
| Privileged mode | - | - | Docker run flag --privileged |
| Port binding | - | - | Docker run flag -p 127.0.0.1:139:139 -p 127.0.0.1:445:445 |
| User account | Dockerfile9 | 9 | sshuser:sshpass |
| Share permissions | Dockerfile21 | 21 | chmod 777 /samba-share |
| FUSE cross-user | Dockerfile24 | 24 | user_allow_other |
| Security mode | smb.conf3 | 3 | security = user |
| Guest mapping | smb.conf4 | 4 | map to guest = bad user |
| Guest access | smb.conf:13-14 | 13-14 | guest ok = yes, guest only = yes |
| Force user | smb.conf19 | 19 | force user = sshuser |
| SMB protocol | smb.conf:7-8 | 7-8 | min protocol = SMB2 |
| SSHFS options | - | - | allow_other,uid=1000,gid=1000 |
Sources : README.md31 README.md49 Dockerfile9 Dockerfile21 Dockerfile24 smb.conf:1-20
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Container Implementation
Relevant source files
This page documents the Docker container's internal structure, build process, and runtime behavior. It explains how the docker-sshfs image is constructed from a base Ubuntu system and configured to run both SSHFS and Samba services within a single container.
For step-by-step instructions on building and running the container, see Getting Started. For detailed line-by-line Dockerfile analysis, see Dockerfile Breakdown. For configuration file details, see Configuration Reference.
Purpose and Scope
The docker-sshfs container serves as an isolated environment that bridges SSH and SMB protocols without requiring macFUSE installation on the host macOS system. This page covers:
- Container base image and package dependencies
- Build-time configuration and setup
- Internal directory structure and symbolic links
- User accounts and permission model
- Service startup and process management
The container is built from Dockerfile:1-34 and configured to run as a single-process service with smbd as the main foreground process.
Sources: Dockerfile:1-34 README.md:1-90
Container Base Architecture
Diagram: Container Build Stages and Component Relationships
The container is constructed in layers, with each build stage producing artifacts used by subsequent stages. The Dockerfile2 specifies ubuntu:latest as the base image, which provides the Linux kernel interface and standard utilities. Package installation at Dockerfile:5-6 adds SSHFS and Samba along with their dependencies (FUSE libraries, OpenSSH client).
Sources: Dockerfile:1-34
Build Process Overview
| Build Stage | Dockerfile Lines | Action | Artifacts Created |
|---|---|---|---|
| Base Image | Dockerfile2 | Pull ubuntu:latest | Linux OS environment |
| Package Install | Dockerfile:5-6 | apt-get install sshfs samba | SSHFS client, Samba server binaries |
| User Creation | Dockerfile9 | Create sshuser account | Non-root user (UID 1000) |
| Directory Setup | Dockerfile:12-15 | Create /remote and /samba-share | Mount points |
| Config Copy | Dockerfile18 | Copy smb.conf to /etc/samba/ | Samba configuration |
| Permissions | Dockerfile21 | chmod 777 /samba-share | World-writable share |
| FUSE Config | Dockerfile24 | Enable user_allow_other | Cross-user mount access |
| Port Exposure | Dockerfile27 | EXPOSE 139 445 | SMB port metadata |
| Symbolic Link | Dockerfile30 | ln -s /remote /samba-share/remote | Integration point |
| Entry Point | Dockerfile33 | Set CMD for smbd | Process definition |
The build process is deterministic and produces an image containing all necessary components. The container does not require external volume mounts for its core functionality, as all configuration is baked into the image at build time.
Sources: Dockerfile:1-34
Component Integration Map
Diagram: Runtime Component Interactions
The container implements a two-process model where smbd runs as the main container process (PID 1) and sshfs is spawned manually by users via docker exec. The symbolic link at Dockerfile30 creates the integration point between these processes, allowing smbd to serve files that sshfs has mounted from the remote server.
Sources: Dockerfile:1-34 README.md:41-50
Privileged Container Requirements
The container must run with the --privileged flag as specified in README.md31 This requirement stems from FUSE's need for device access:
| Capability | Required For | Alternative |
|---|---|---|
| Device Access | FUSE /dev/fuse device | None - FUSE requires device access |
| Mount Operations | Creating mount points in container namespace | None - fundamental to SSHFS |
| Process Management | FUSE background processes | None - needed for daemon operation |
The README.md31 command docker run --privileged grants the container access to host devices, specifically /dev/fuse, which SSHFS requires to implement the FUSE filesystem driver. Without privileged mode, SSHFS mount operations fail with permission errors.
While this increases the container's privileges, the security risk is mitigated by:
- Port binding to
127.0.0.1only (README.md31) - Guest-only access configuration in
smb.conf - No sensitive data stored in the container itself
Sources: README.md31 Dockerfile:1-34
Build-Time vs Runtime Operations
Diagram: Build vs Runtime Operations Timeline
A critical design decision is that SSHFS mounting happens at runtime , not build time. The Dockerfile:1-34 only creates the infrastructure (directories, symbolic links, configurations), while actual remote filesystem mounting occurs via user commands after the container is running (README.md:41-50). This separation allows:
- Dynamic remote server configuration (no hardcoded endpoints)
- Multiple mount/unmount cycles without container restart
- SSH credential handling outside the container image
The CMD at Dockerfile33 defines smbd --foreground --no-process-group --debug-stdout as the container's main process, ensuring Samba starts automatically but SSHFS requires manual invocation.
Sources: Dockerfile33 README.md:30-50
Key Implementation Details
Non-Root User (sshuser)
The Dockerfile9 line useradd -m sshuser && echo "sshuser:sshpass" | chpasswd creates a non-root user for SSHFS operations. This user:
- Has UID 1000 and GID 1000 (standard first non-system user)
- Owns SSHFS mount processes
- Is forced by Samba via
force user = sshuserinsmb.conf - Has password
sshpass(for internal use if needed)
The UID/GID values (1000) are referenced in mount commands at README.md49 as -o uid=1000,gid=1000, ensuring file ownership consistency between SSHFS-mounted files and Samba-served files.
World-Writable Share Directory
The Dockerfile21 command chmod -R 777 /samba-share makes the Samba root directory world-writable. This permission is necessary because:
smbdruns as root but accesses files assshuser(viaforce user)sshfsmounts files owned by UID 1000 (sshuser)- Without 777 permissions, Samba cannot traverse the directory
The commented-out line Dockerfile20 shows an alternative approach using chown, which was replaced with the more permissive chmod 777.
FUSE Configuration
The Dockerfile24 line echo "user_allow_other" >> /etc/fuse.conf modifies the system-wide FUSE configuration to enable the allow_other mount option. Without this:
- SSHFS mounts would only be accessible to the mounting user
smbd(running as root, accessing as sshuser) couldn't read the mounted files- The symbolic link integration would fail
This setting is referenced in mount commands at README.md49 as -o allow_other.
Symbolic Link Strategy
The Dockerfile30 command ln -s /remote /samba-share/remote creates a symbolic link before any SSHFS mount exists. This pre-created link:
- Points to an empty
/remotedirectory initially - Becomes populated when SSHFS mounts to
/remote - Allows Samba to serve SSHFS-mounted content without reconfiguration
This is a zero-copy integration technique - files are not duplicated, just made accessible through multiple paths.
Sources: Dockerfile9 Dockerfile:20-21 Dockerfile24 Dockerfile30 README.md49
Port Exposure and Networking
Diagram: Port Configuration Flow
The Dockerfile27 EXPOSE 139 445 directive is metadata only - it does not actually bind or forward ports. Actual port mapping occurs at runtime via README.md31 with -p 127.0.0.1:139:139 -p 127.0.0.1:445:445, which:
- Maps container ports 139 and 445 to host localhost
- Restricts access to
127.0.0.1(not0.0.0.0) - Requires container IP for actual connections (localhost forwarding doesn't work with macOS SMB client)
Port 139 provides NetBIOS session service, while port 445 handles direct SMB over TCP. Both are required for full Samba functionality.
Sources: Dockerfile27 README.md31
Service Lifecycle and Process Management
The Dockerfile33 CMD directive defines the container's main process:
CMD ["smbd", "--foreground", "--no-process-group", "--debug-stdout"]
| Flag | Purpose | Consequence |
|---|---|---|
--foreground | Run in foreground (don't daemonize) | Container stays alive |
--no-process-group | Don't create new process group | Proper signal handling |
--debug-stdout | Log to stdout instead of files | Docker logs capture output |
This configuration ensures smbd runs as PID 1 and the container lifecycle matches the Samba service lifecycle. When smbd exits, the container stops. The process handles SIGTERM for graceful shutdown.
Unlike typical system deployments where Samba runs as a background daemon, this container runs Samba as the primary foreground process, following Docker best practices for single-service containers.
Sources: Dockerfile33
Image Layers and Size
The Docker image consists of multiple layers corresponding to Dockerfile instructions:
- Base
ubuntu:latestlayer (~30-80 MB depending on version) apt-get updatemetadata layer (~40 MB)sshfsandsambapackage layers (~80 MB combined)- User creation layer (minimal)
- Directory creation layers (minimal)
- Configuration file layers (< 1 MB)
- Permission modification layers (minimal)
The final image size is approximately 200-250 MB. Most space is consumed by:
- Ubuntu base system
- Samba binaries and libraries
- FUSE and SSHFS utilities
- OpenSSH client dependencies
The Dockerfile:5-6 package installation is the largest layer due to dependency trees for both sshfs (requiring libfuse2, openssh-client) and samba (requiring numerous libraries for SMB protocol implementation).
Sources: Dockerfile:1-34
Summary
The docker-sshfs container is a purpose-built environment that combines SSHFS and Samba in a single image. Key characteristics:
- Base : Ubuntu Linux with standard package manager
- Services : SSHFS (user-invoked) and Samba (auto-start)
- Integration : Symbolic link + FUSE
allow_other+ forced user identity - Security : Privileged mode for FUSE, localhost-only ports
- Lifecycle : Foreground
smbdas PID 1, manual SSHFS mounting
The implementation prioritizes simplicity over optimization - all configuration is static except the remote mount target. This design allows the container to work without environment variables, config mounts, or complex orchestration.
For detailed analysis of each Dockerfile instruction, see Dockerfile Breakdown. For internal directory structure details, see Filesystem Layout. For user and permission specifics, see User and Permissions. For service startup details, see Service Lifecycle.
Sources: Dockerfile:1-34 README.md:1-90
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Dockerfile Breakdown
Relevant source files
Purpose and Scope
This page provides a line-by-line analysis of the Dockerfile that constructs the docker-sshfs container image. The Dockerfile builds a complete SSHFS/Samba bridge server starting from a base Ubuntu image, installing necessary packages, configuring users and permissions, and setting up the critical filesystem integration that enables remote SSH filesystems to be accessed via SMB on macOS.
For runtime directory structure details, see Filesystem Layout. For Samba configuration specifics, see Samba Configuration (smb.conf)). For FUSE settings, see FUSE Configuration. For how the container starts and runs, see Service Lifecycle.
Sources: Dockerfile:1-34
Build Process Overview
The Dockerfile consists of distinct build stages that layer functionality progressively. Each RUN command creates a new image layer that contributes to the final container.
graph TD
BaseImage["ubuntu:latest\n(Base Layer)"]
PackageLayer["apt-get update && install\nsshfs + samba\n(Package Layer)"]
UserLayer["useradd sshuser\necho sshuser:sshpass\n(User Layer)"]
DirLayer["mkdir /remote\nmkdir /samba-share\n(Directory Layer)"]
ConfigLayer["COPY smb.conf\n(Configuration Layer)"]
PermLayer["chmod 777 /samba-share\n(Permissions Layer)"]
FuseLayer["echo user_allow_other >> fuse.conf\n(FUSE Layer)"]
ExposeLayer["EXPOSE 139 445\n(Port Declaration)"]
LinkLayer["ln -s /remote /samba-share/remote\n(Integration Layer)"]
CmdLayer["CMD smbd --foreground\n(Startup Layer)"]
BaseImage --> PackageLayer
PackageLayer --> UserLayer
UserLayer --> DirLayer
DirLayer --> ConfigLayer
ConfigLayer --> PermLayer
PermLayer --> FuseLayer
FuseLayer --> ExposeLayer
ExposeLayer --> LinkLayer
LinkLayer --> CmdLayer
Dockerfile Build Layers
Each layer builds on the previous, with dependencies flowing from top to bottom. The symbolic link layer is the final filesystem modification before container startup.
Sources: Dockerfile:2-33
Base Image Selection
Dockerfile2 specifies ubuntu:latest as the base image. Ubuntu is chosen because:
- Provides mature, well-tested FUSE implementation
- Includes
apt-getpackage manager with comprehensive SSHFS and Samba packages - Widely used base with extensive community support
- Contains necessary Linux kernel features for FUSE mounts
The latest tag automatically pulls the most recent Ubuntu LTS release, ensuring up-to-date packages and security patches.
Sources: Dockerfile2
Package Installation Layer
Dockerfile:5-6 performs a single-layer installation of both core packages:
| Package | Purpose | Provides |
|---|---|---|
sshfs | Remote filesystem client | SSHFS binary, FUSE dependencies, SSH client |
samba | SMB server | smbd daemon, SMB protocol implementation |
graph LR
AptGet["apt-get install"]
subgraph "sshfs Package"
SSHFSBin["sshfs binary"]
FuseDeps["libfuse2/libfuse3"]
SSHClient["openssh-client"]
end
subgraph "samba Package"
SMBDBin["smbd binary"]
SMBLibs["libsmbclient"]
SMBConf["Default /etc/samba/smb.conf"]
end
AptGet --> SSHFSBin
AptGet --> FuseDeps
AptGet --> SSHClient
AptGet --> SMBDBin
AptGet --> SMBLibs
AptGet --> SMBConf
SSHFSBin --> FuseDeps
SSHFSBin --> SSHClient
SMBDBin --> SMBLibs
The packages are installed in a single RUN command to minimize layer count. The -y flag auto-accepts prompts, enabling non-interactive installation.
Package Dependencies
The sshfs package automatically pulls FUSE userspace libraries and SSH client components. The samba package installs the smbd daemon along with protocol libraries.
Sources: Dockerfile:5-6
User Account Creation
Dockerfile9 creates a non-root user account with specific credentials:
- Username:
sshuser - Password:
sshpass - Home Directory:
/home/sshuser(created via-mflag)
Why Non-Root User
The sshuser account is critical for two reasons:
-
SSHFS Mount Ownership: SSHFS mounts are owned by the user who initiates the mount. Running SSHFS as
sshuserensures consistent file ownership (UID 1000, GID 1000). -
Samba Force User: The
smb.confforces all SMB operations to run assshuser, ensuring that file permissions match between SSHFS and Samba regardless of how macOS authenticates.
This account is used for interactive operations (via docker exec -it) but not for running the smbd daemon itself, which runs as root but operates on behalf of sshuser due to the force user directive.
Sources: Dockerfile9
Directory Structure Setup
Dockerfile12 and Dockerfile15 create the two critical directories in the container's filesystem:
| Directory | Purpose | Mount Role |
|---|---|---|
/remote | SSHFS mount point | Target for sshfs user@host:path /remote |
/samba-share | Samba root directory | Served as SMB share; contains symlink to /remote |
graph TB
RemoteDir["/remote\n(SSHFS mount target)"]
SambaDir["/samba-share\n(Samba root)"]
SymLink["/samba-share/remote\n(symbolic link)"]
RemoteDir -.->|linked by| SymLink
SambaDir -->|contains| SymLink
SSHFSMount["sshfs command\n(runtime)"]
SMBDServe["smbd daemon\n(serving)"]
SSHFSMount -->|mounts to| RemoteDir
SMBDServe -->|serves| SambaDir
These directories are initially empty. The /remote directory receives the SSHFS mount at runtime, while /samba-share is immediately accessible to Samba.
Directory Relationship
The symbolic link (created later in the Dockerfile) connects these directories, enabling files mounted at /remote to be accessible via /samba-share/remote.
Sources: Dockerfile12 Dockerfile15
Configuration File Integration
Dockerfile18 copies the custom Samba configuration file from the build context into the container. This replaces the default smb.conf installed by the samba package.
The custom configuration includes:
- Guest access enabled (
map to guest = Bad User) - Force user directive (
force user = sshuser) - Protocol restrictions (minimum SMB2)
- Share definition for
/samba-share
The build fails if smb.conf is not present in the build context. For complete configuration details, see Samba Configuration (smb.conf)).
Sources: Dockerfile18
Permission Configuration
Dockerfile:20-21 sets permissions on the Samba share directory. Note that line 20 is commented out, indicating a design decision:
- Commented:
chown -R sshuser:sshuser /samba-share(ownership change) - Active:
chmod -R 777 /samba-share(permission change)
graph LR
FileOps["File Operations"]
subgraph "Without 777"
SMBAsRoot["smbd (root)"]
SMBForce["force user: sshuser"]
FUSEOwner["FUSE mount owner: sshuser"]
NoAccess["Permission denied"]
end
subgraph "With 777"
SMBAsRoot2["smbd (root)"]
SMBForce2["force user: sshuser"]
FUSEOwner2["FUSE mount owner: sshuser"]
WriteAccess["Write successful"]
end
FileOps --> SMBAsRoot
SMBAsRoot --> SMBForce
SMBForce --> FUSEOwner
FUSEOwner --> NoAccess
FileOps --> SMBAsRoot2
SMBAsRoot2 --> SMBForce2
SMBForce2 --> FUSEOwner2
FUSEOwner2 --> WriteAccess
Permission Strategy
The 777 permission (read/write/execute for all users) is chosen over specific ownership for a critical reason:
The 777 permissions ensure that regardless of the complex UID/GID mapping between smbd (running as root but forced to sshuser) and the SSHFS mount, write operations succeed. This is a pragmatic trade-off favoring functionality over strict permissions within the isolated container environment.
Sources: Dockerfile:20-21
FUSE Configuration
Dockerfile24 modifies the FUSE configuration to enable cross-user filesystem access. This appends the user_allow_other directive to /etc/fuse.conf.
Why user_allow_other is Critical
By default, FUSE mounts are only accessible to the user who created the mount. Without user_allow_other:
sshuserruns:sshfs user@host:path /remote- Mount succeeds, files owned by
sshuser smbd(running as root) tries to access/samba-share/remote- Access denied - different user
With user_allow_other enabled, SSHFS mounts can be accessed by any user, including the smbd process. The allow_other option must also be passed to the sshfs command at runtime (see SSHFS Mount Options).
This is the critical enabler for Samba to serve SSHFS-mounted files.
Sources: Dockerfile24
Port Exposure Declaration
Dockerfile27 declares the ports used by the SMB protocol:
| Port | Protocol | Purpose |
|---|---|---|
| 139 | NetBIOS Session Service | Legacy SMB over NetBIOS |
| 445 | SMB over TCP | Direct SMB (modern) |
The EXPOSE directive is documentation only - it does not actually publish ports. Actual port forwarding is configured at container runtime via docker run -p 127.0.0.1:139:139 -p 127.0.0.1:445:445. See Running the Container for runtime port mapping.
Sources: Dockerfile27
Symbolic Link Creation
Dockerfile30 creates the critical integration point between SSHFS and Samba:
- Source:
/remote(SSHFS mount point) - Link:
/samba-share/remote(symlink visible to Samba)
Symbolic Link Integration
This symlink is created at build time, before any SSHFS mount exists. When the SSHFS mount occurs at runtime, the symlink automatically provides access to the mounted files through the Samba share.
This is a zero-copy integration - no data is duplicated. Reads and writes pass directly through the symlink to the FUSE mount.
Sources: Dockerfile30
Container Startup Command
Dockerfile33 defines the container's default startup command:
| Flag | Purpose |
|---|---|
--foreground | Run smbd in foreground instead of daemonizing |
--no-process-group | Prevent process group creation |
--debug-stdout | Send log output to stdout |
Why Foreground Mode
Docker containers require a foreground process as PID 1. When the foreground process exits, the container stops. Running smbd --foreground ensures:
- Container remains running while
smbdis active - Docker can monitor the process health
- Logs are visible via
docker logs - Graceful shutdown when container stops
The --no-process-group flag prevents smbd from creating additional process groups, simplifying container lifecycle management. The --debug-stdout flag routes log output to stdout, making it accessible via Docker's logging subsystem.
Sources: Dockerfile33
graph TB
subgraph "Build Context"
DockerfileSrc["Dockerfile"]
SmbConfSrc["smb.conf"]
end
subgraph "Base Layer"
UbuntuImage["ubuntu:latest"]
end
subgraph "Package Layer"
SSHFSPkg["sshfs package"]
SambaPkg["samba package"]
FuseLib["FUSE libs"]
SSHClient["openssh-client"]
SMBDBinary["smbd binary"]
end
subgraph "User Layer"
SSHUser["sshuser:1000"]
SSHPass["password: sshpass"]
HomeDir["/home/sshuser"]
end
subgraph "Filesystem Layer"
RemoteDir["/remote"]
SambaShareDir["/samba-share"]
SymbolicLink["/samba-share/remote -> /remote"]
end
subgraph "Configuration Layer"
SmbConfFile["/etc/samba/smb.conf"]
FuseConfFile["/etc/fuse.conf"]
PermSambaShare["chmod 777 /samba-share"]
end
subgraph "Runtime Layer"
SMBDProcess["smbd --foreground"]
Port139["Port 139"]
Port445["Port 445"]
end
DockerfileSrc --> UbuntuImage
UbuntuImage --> SSHFSPkg
UbuntuImage --> SambaPkg
SSHFSPkg --> FuseLib
SSHFSPkg --> SSHClient
SambaPkg --> SMBDBinary
SSHFSPkg --> SSHUser
SambaPkg --> SSHUser
SSHUser --> SSHPass
SSHUser --> HomeDir
SSHUser --> RemoteDir
SSHUser --> SambaShareDir
RemoteDir --> SymbolicLink
SambaShareDir --> SymbolicLink
SmbConfSrc --> SmbConfFile
SSHUser --> FuseConfFile
SambaShareDir --> PermSambaShare
SmbConfFile --> SMBDProcess
PermSambaShare --> SMBDProcess
SMBDBinary --> SMBDProcess
SMBDProcess --> Port139
SMBDProcess --> Port445
Complete Build Dependency Graph
This diagram maps Dockerfile components to their code entities and runtime dependencies:
This graph shows how each Dockerfile instruction contributes to the final container image, from base packages through configuration to the running smbd process.
Sources: Dockerfile:2-33
Build Layer Size Implications
Each RUN command creates a new image layer. The Dockerfile uses several optimization patterns:
- Combined Package Installation: Dockerfile:5-6 installs both
sshfsandsambain a singleRUNto minimize layers - Separate Directory Creation: Dockerfile12 and Dockerfile15 create directories in separate commands (could be optimized)
- Single Configuration Writes: Dockerfile24 appends to
fuse.confin one operation
The largest layers are:
- Package installation (100-200 MB including dependencies)
- Base Ubuntu image (~70 MB)
- Configuration and directory operations (<1 MB each)
Sources: Dockerfile:5-6 Dockerfile12 Dockerfile15 Dockerfile24
Summary
The Dockerfile constructs a complete SSHFS/Samba bridge server through 10 distinct build stages:
- Ubuntu base image selection
- Package installation (SSHFS + Samba)
- User account creation (
sshuser) - Directory structure (
/remote,/samba-share) - Samba configuration deployment
- Permission configuration (
777on/samba-share) - FUSE configuration (
user_allow_other) - Port exposure declaration (139, 445)
- Symbolic link creation (integration point)
- Startup command specification (
smbd --foreground)
The resulting image contains all necessary components for protocol translation between SSH and SMB, with the symbolic link at /samba-share/remote serving as the critical zero-copy integration mechanism.
Sources: Dockerfile:1-34
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Filesystem Layout
Relevant source files
This page documents the internal directory structure of the docker-sshfs container, including the purpose of each directory, the symbolic link integration point, and how these filesystem components enable the protocol bridge between SSHFS and Samba. For configuration details about permissions and the sshuser account, see User and Permissions. For information about how these directories are created during the build process, see Dockerfile Breakdown.
Directory Structure Overview
The container maintains a minimal filesystem layout with three key directories that form the core of the SSHFS-to-Samba bridge. The directory structure is established during the Docker image build process and remains static throughout the container's lifetime.
graph TB
root["/"]
remote["/remote"]
sambaShare["/samba-share"]
sambaRemote["/samba-share/remote"]
etc["/etc"]
sambaConf["/etc/samba/smb.conf"]
fuseConf["/etc/fuse.conf"]
root --> remote
root --> sambaShare
root --> etc
sambaShare -.->|symbolic link| sambaRemote
sambaRemote -.->|ln -s /remote| remote
etc --> sambaConf
etc --> fuseConf
style sambaRemote fill:#f9f9f9
style remote fill:#f9f9f9
style sambaShare fill:#f9f9f9
Container Directory Tree
Sources: Dockerfile12 Dockerfile15 Dockerfile30
Root-Level Mount Directories
/remote - SSHFS Mount Point
The /remote directory serves as the primary mount point for SSHFS operations. This directory is created during the Docker build process and remains empty until a user executes the sshfs command to mount a remote filesystem.
| Property | Value |
|---|---|
| Path | /remote |
| Created By | Dockerfile12 |
| Purpose | SSHFS mount target |
| Default State | Empty directory |
| Permissions | Inherited from parent |
| Typical Mount Command | sshfs -o allow_other,uid=1000,gid=1000 user@host:path /remote |
The /remote directory is referenced in the SSHFS mount command but is not directly served by Samba. Instead, it is accessed through the symbolic link integration point.
Sources: Dockerfile12
/samba-share - Samba Root Directory
The /samba-share directory functions as the root directory served by the Samba service. This is the directory that appears to macOS clients when they connect to the SMB share. The directory is configured with world-writable permissions to ensure compatibility with the Samba guest access model.
| Property | Value |
|---|---|
| Path | /samba-share |
| Created By | Dockerfile15 |
| Purpose | Samba share root directory |
| Permissions | 777 (world-writable) |
| Permission Command | Dockerfile21 |
| Samba Configuration | Referenced in smb.conf as path = /samba-share |
The 777 permission mode is critical for write access from macOS clients connecting as guests. Without these permissive settings, write operations would fail despite the writable = yes Samba configuration.
Sources: Dockerfile15 Dockerfile21
The Symbolic Link Integration Point
/samba-share/remote → /remote
The symbolic link at /samba-share/remote is the critical integration point that bridges the SSHFS mount with the Samba share. This zero-copy mechanism allows remote files mounted at /remote to be accessible through the Samba share without data duplication.
Sources: Dockerfile30
graph LR
macOS["macOS Finder Client"]
smbd["smbd process"]
sambashare["/samba-share\n(Samba root)"]
symlink["/samba-share/remote\n(symbolic link)"]
remotedir["/remote\n(SSHFS mount)"]
sshfs["sshfs process"]
remoteServer["Remote SSH Server"]
macOS -->|SMB protocol| smbd
smbd -->|serves| sambashare
sambashare -->|contains| symlink
symlink -.->|ln -s /remote| remotedir
remotedir -->|mounted by| sshfs
sshfs -->|SSH protocol| remoteServer
| Property | Value |
|---|---|
| Symlink Path | /samba-share/remote |
| Target Path | /remote |
| Created By | ln -s /remote /samba-share/remote at Dockerfile30 |
| Purpose | Make SSHFS-mounted files accessible via Samba |
| Visibility | Appears as a folder named remote in Finder |
Symbolic Link Behavior
The symbolic link is created during the Docker build process using the ln -s command. Key behavioral characteristics:
- Cross-Service Access : The symlink allows the
smbdprocess (running as root ornobody) to access files in/remotewhich are mounted by thesshuseraccount - FUSE Requirement : The symbolic link only functions correctly if the SSHFS mount uses the
allow_otheroption, enabled byuser_allow_otherin/etc/fuse.conf - Transparent Operation : macOS clients see
/samba-share/remoteas a normal directory, not as a symbolic link - Zero-Copy : Files are not duplicated; the symlink provides direct access to the FUSE-mounted filesystem
Sources: Dockerfile30 Dockerfile24
Filesystem Integration Architecture
The following diagram illustrates how the directory structure integrates with the SSHFS and Samba services to create the protocol bridge:
Sources: Dockerfile12 Dockerfile15 Dockerfile30 Dockerfile24 Dockerfile18
graph TB
subgraph "External"
remote_server["Remote SSH Server\nuser@host:path"]
mac_client["macOS Finder Client"]
end
subgraph "Container Processes"
sshfs_proc["sshfs process\n(runs as sshuser)"]
smbd_proc["smbd process\n(runs in foreground)"]
end
subgraph "Filesystem Layer"
remote_dir["/remote\nFUSE mount point\n(empty until mounted)"]
samba_dir["/samba-share\nSamba root\n(permissions: 777)"]
symlink_dir["/samba-share/remote\nsymbolic link\n(ln -s /remote)"]
end
subgraph "Configuration"
fuse_conf["/etc/fuse.conf\nuser_allow_other"]
smb_conf["/etc/samba/smb.conf\npath = /samba-share"]
end
remote_server -->|SSH connection| sshfs_proc
sshfs_proc -->|mounts to| remote_dir
symlink_dir -.->|points to| remote_dir
samba_dir -->|contains| symlink_dir
smbd_proc -->|serves| samba_dir
smbd_proc -->|reads config| smb_conf
sshfs_proc -->|reads config| fuse_conf
mac_client -->|SMB connection| smbd_proc
Mount Point Usage Patterns
Standard Mount Pattern (Using /remote)
The canonical usage pattern mounts the remote filesystem to /remote, making it accessible via the symbolic link:
After mounting, the directory structure becomes:
/remote/ <- SSHFS mount point (contains remote files)
/samba-share/ <- Samba root (empty except for symlink)
/samba-share/remote/ <- Symbolic link to /remote (provides access)
Sources: Dockerfile30
Alternative Mount Pattern (Direct to /samba-share)
Users may alternatively mount directly to /samba-share, bypassing the symbolic link mechanism:
After mounting, the directory structure becomes:
/remote/ <- Empty (not used)
/samba-share/ <- SSHFS mount point (contains remote files)
/samba-share/remote/ <- Hidden by mount (symbolic link inaccessible)
When mounting directly to /samba-share, the symbolic link at /samba-share/remote becomes inaccessible because it exists inside the mount point. This pattern is simpler but eliminates the separation between the SSHFS mount and Samba share.
Sources: README.md49
stateDiagram-v2
[*] --> ContainerStart : docker run
ContainerStart --> DirectoriesEmpty : smbd starts
state DirectoriesEmpty {
[*] --> RemoteEmpty : /remote is empty
[*] --> SambaShareEmpty : /samba-share contains only symlink
}
DirectoriesEmpty --> RemoteMounted : sshfs user@host - path /remote
state RemoteMounted {
[*] --> RemotePopulated : /remote contains remote files
[*] --> SymlinkActive : /samba-share/remote accessible
}
RemoteMounted --> DirectoriesEmpty : fusermount -u /remote
note right of RemoteMounted
/remote : populated with remote files /samba-share - contains symlink /samba-share/remote - provides access
end note
note right of DirectoriesEmpty
/remote : empty directory /samba-share - contains only symlink /samba-share/remote - symlink exists but points nowhere
end note
Directory State Lifecycle
The filesystem directories transition through different states during container operation:
Sources: Dockerfile12 Dockerfile15 Dockerfile30 README.md49 README.md76
Filesystem Permissions Summary
| Path | Owner | Permissions | Purpose |
|---|---|---|---|
/remote | root:root | 755 (default) | SSHFS mount target |
/samba-share | root:root | 777 (world-writable) | Samba share root |
/samba-share/remote | root:root | 777 (symlink) | Integration point |
/etc/samba/smb.conf | root:root | 644 (default) | Samba configuration |
/etc/fuse.conf | root:root | 644 (default) | FUSE configuration |
The 777 permissions on /samba-share are set explicitly at Dockerfile21 For detailed information about user identity and permission management, see User and Permissions.
Sources: Dockerfile21
Related Configuration Files
The filesystem layout is tightly coupled with configuration files that control access permissions:
/etc/fuse.conf: Containsuser_allow_othersetting (Dockerfile24), which enables the symbolic link to function across different user contexts/etc/samba/smb.conf: Definespath = /samba-shareas the share root and configures guest access and forced user identity
For complete configuration reference, see Samba Configuration) and FUSE Configuration.
Sources: Dockerfile24 Dockerfile18
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
User and Permissions
Relevant source files
Purpose and Scope
This page documents the user account and permission model used within the docker-sshfs container. It explains how the sshuser account is configured, why the /samba-share directory has world-writable permissions (777), and how FUSE's user_allow_other option enables cross-service filesystem access between SSHFS and Samba processes.
For information about SSHFS mount options that interact with these permissions, see 5.3. For details about the Samba configuration that enforces user identity, see 5.1).
The sshuser Account
The container creates a dedicated non-root user account specifically for running SSHFS mount operations. This account is created during the Docker image build process.
Account Specifications
| Property | Value | Location |
|---|---|---|
| Username | sshuser | Dockerfile9 |
| Password | sshpass | Dockerfile9 |
| User Type | Non-root with home directory | Dockerfile9 |
| UID | 1000 (default for first non-root user) | Assigned by useradd |
| GID | 1000 (default primary group) | Assigned by useradd |
The account is created using the useradd command with the -m flag to create a home directory:
useradd -m sshuser && echo "sshuser:sshpass" | chpasswd
Sources : Dockerfile9
Role in the System
The sshuser account serves three critical functions:
- SSHFS Mount Owner : When users manually execute
sshfscommands inside the container, they typically do so assshuserviadocker exec - File Operation Identity : All files accessed through Samba are forced to use
sshuseras the effective user (viaforce user = sshuserin smb.conf19) - Permission Boundary : This account provides a consistent identity layer between the guest-authenticated SMB clients and the remote SSH server
Sources : Dockerfile9 smb.conf19
Directory Permissions
The container's filesystem uses deliberately permissive settings to enable cross-service access between SSHFS mounts and Samba shares.
/samba-share Directory Permissions
The /samba-share directory is configured with 777 permissions (read, write, execute for all users):
chmod -R 777 /samba-share
Permission Breakdown :
| Octal | Binary | Owner | Group | Other |
|---|---|---|---|---|
| 777 | 111 111 111 | rwx | rwx | rwx |
This configuration is set at Dockerfile21 Notably, line 20 contains a commented-out alternative approach:
# RUN chown -R sshuser:sshuser /samba-share
This indicates that the design evolved from ownership-based access control to permission-based access control, choosing broad permissions over restricted ownership.
Sources : Dockerfile:20-21
Why 777 Permissions Are Used
The 777 permission mode is necessary because:
- Multiple Process Access : Both the
smbddaemon and SSHFS mount processes need write access to directories under/samba-share - Symbolic Link Traversal : The symbolic link at
/samba-share/remote(created at Dockerfile30) points to/remote, and both services must traverse this link - Unknown Client UIDs : macOS clients connecting as "guest" might have their requests mapped to various internal UIDs by Samba
- Write-Through Requirements : Files created by macOS clients must be writable through the SSHFS mount to the remote SSH server
Sources : Dockerfile21 Dockerfile30
FUSE user_allow_other Configuration
The most critical permission setting for cross-service integration is the FUSE user_allow_other option, enabled in /etc/fuse.conf.
Configuration Line
The Dockerfile appends this setting to the FUSE configuration:
echo "user_allow_other" >> /etc/fuse.conf
This occurs at Dockerfile24
Sources : Dockerfile24
What user_allow_other Does
By default, FUSE mounts are only accessible by the user who performed the mount operation. When sshuser mounts a remote filesystem via SSHFS to /remote, only sshuser can read or write files at that mount point.
graph TD
subgraph "Without user_allow_other"
SSHFS1["sshfs mount by sshuser\nMounted at: /remote"]
SambaProc1["smbd process\nRunning as: root or other user"]
Access1["File Access Denied"]
SSHFS1 -->|Access check| Access1
SambaProc1 -->|Attempts to read /remote| SSHFS1
end
subgraph "With user_allow_other"
SSHFS2["sshfs mount by sshuser\nOptions: allow_other\nMounted at: /remote"]
SambaProc2["smbd process\nRunning as: root or other user"]
Access2["File Access Granted"]
SSHFS2 -->|Access check allow_other active| Access2
SambaProc2 -->|Attempts to read /remote| SSHFS2
end
The user_allow_other setting changes this behavior:
Permission Flow :
- Without
user_allow_other: FUSE kernel module checks if requesting process UID matches mount owner UID - With
user_allow_other+allow_othermount option: FUSE allows access from any user, delegating permission checks to the underlying filesystem
Sources : Dockerfile24
Interaction with SSHFS Mount Options
The /etc/fuse.conf setting enables the capability , but users must also specify -o allow_other when mounting with SSHFS:
Without the system-level user_allow_other configuration in /etc/fuse.conf, the allow_other mount option would be rejected with an error like:
fusermount: option allow_other only allowed if 'user_allow_other' is set in /etc/fuse.conf
Sources : Dockerfile24
Samba Force User Mechanism
The Samba configuration enforces a consistent user identity for all file operations, regardless of how clients authenticate.
Configuration Settings
The [SSHFS Share] section in smb.conf contains:
This appears at smb.conf19 alongside guest access settings:
Sources : smb.conf:13-14 smb.conf19
graph LR
subgraph "macOS Client Request Flow"
MacOS["macOS Finder\nConnects as: Guest"]
SMBAuth["Samba Authentication\nmap to guest = bad user"]
ForceUser["Force User Transform\nforce user = sshuser"]
FileOp["Filesystem Operation\nEffective UID: 1000 (sshuser)"]
end
MacOS -->|SMB connection| SMBAuth
SMBAuth -->|Guest credentials| ForceUser
ForceUser -->|All operations as sshuser| FileOp
Force User Behavior
The force user directive overrides the authenticated user identity for all filesystem operations:
Effective Result : Whether the macOS client connects as "Guest" (which it must, given guest only = yes), or even if future configurations allowed authenticated users, all file operations would execute as sshuser (UID 1000).
Sources : smb.conf4 smb.conf:13-14 smb.conf19
flowchart TB
subgraph "macOS Client Layer"
Finder["macOS Finder\nUser identity: Varies"]
end
subgraph "Samba Layer (Container)"
SMB["smbd process\nConfiguration applies"]
GuestMap["map to guest = bad user\nGuest authentication"]
ForceUser["force user = sshuser\nUID override to 1000"]
end
subgraph "Filesystem Layer (Container)"
SambaShare["/samba-share\nPermissions: 777\nAccessible by all"]
SymLink["/samba-share/remote\nSymbolic link"]
RemoteMount["/remote\nSSHFS mount point"]
end
subgraph "FUSE Layer (Container)"
FUSEConf["user_allow_other\nin /etc/fuse.conf"]
MountOpts["SSHFS mount options:\nallow_other\nuid=1000\ngid=1000"]
end
subgraph "Remote Layer"
SSH["SSH connection\nAuthenticated as sshuser"]
RemoteFS["Remote filesystem\nFiles owned by remote user"]
end
Finder -->|SMB protocol| SMB
SMB --> GuestMap
GuestMap --> ForceUser
ForceUser -->|UID: 1000| SambaShare
SambaShare --> SymLink
SymLink --> RemoteMount
RemoteMount --> FUSEConf
FUSEConf -->|Allows cross-user access| MountOpts
MountOpts -->|Files appear as UID 1000| SSH
SSH --> RemoteFS
Complete Permission Model
The following diagram maps the complete permission flow from macOS client through Samba and SSHFS to the remote server:
Cross-Service Permission Flow
Sources : smb.conf4 smb.conf19 Dockerfile21 Dockerfile24 Dockerfile30
Permission Requirements by Operation
Different operations in the system have specific permission requirements:
| Operation | Required Permissions | Configuration Source |
|---|---|---|
| Samba reads /samba-share | World-readable (r--) | Dockerfile21 |
| Samba writes to /samba-share | World-writable (-w-) | Dockerfile21 |
| Samba traverses symlink | World-executable (--x) | Dockerfile21 |
| Samba accesses /remote | user_allow_other + allow_other | Dockerfile24 |
| SSHFS mounts remote files | User sshuser credentials | Manual sshfs command |
| Files created via Samba | Owned by UID 1000 (forced) | smb.conf19 |
| File permission on creation | 0777 (create_mask) | smb.conf17 |
| Directory permission on creation | 0777 (directory_mask) | smb.conf18 |
Sources : Dockerfile21 Dockerfile24 smb.conf:17-19
graph TD
subgraph "Identity Layers"
Layer1["Layer 1: macOS Client\nIdentity: Guest (varies)"]
Layer2["Layer 2: Samba Process\nForced Identity: sshuser (UID 1000)"]
Layer3["Layer 3: SSHFS Mount\nFile Owner: UID 1000"]
Layer4["Layer 4: Remote SSH Server\nIdentity: SSH user (varies)"]
end
Layer1 -->|force user = sshuser| Layer2
Layer2 -->|Filesystem operations| Layer3
Layer3 -->|uid=1000 mount option| Layer4
UID/GID Mapping
The system uses consistent UID/GID values to maintain identity across service boundaries:
Identity Mapping Table
Key Points :
- UID 1000 : The numeric user ID for
sshuser, used consistently across Samba and SSHFS - GID 1000 : The primary group ID for
sshuser - SSHFS uid/gid options : When mounting with
uid=1000,gid=1000, files on the remote server appear to be owned by UID 1000 locally, enabling write access
Sources : Dockerfile9 smb.conf19
Why This Permission Model Works
The permission architecture solves a complex cross-service access problem:
The Core Challenge
Two separate processes with different identities need to access the same mounted filesystem:
- The
smbddaemon (running as root or dedicated samba user) - The SSHFS mount (owned by
sshuser)
The Solution Components
| Component | Purpose | Why It's Necessary |
|---|---|---|
sshuser account | Consistent identity | Provides a known UID (1000) for both Samba and SSHFS |
777 on /samba-share | Permissive directory access | Allows any process to traverse and write |
user_allow_other in fuse.conf | Enable cross-user FUSE access | Allows smbd to read sshuser's mounts |
allow_other mount option | Activate cross-user access | Must be specified when mounting |
force user = sshuser | Identity enforcement | Ensures Samba operations use UID 1000 |
uid=1000,gid=1000 mount options | File ownership mapping | Makes remote files appear owned by sshuser |
Sources : Dockerfile9 Dockerfile21 Dockerfile24 smb.conf19
Security Implications
The permission model prioritizes functionality over strict security , which is appropriate given the container's isolation:
Relaxed Security Elements
- World-Writable Directories : The
777permissions on/samba-shareallow any process in the container to modify files - Guest-Only Access : No authentication required to connect via SMB (smb.conf:13-14)
- Hardcoded Password : The
sshuserpassword issshpass, visible in the Dockerfile - Privileged Container : The container typically runs with
--privilegedflag to enable FUSE
Mitigating Factors
- Container Isolation : Permissions are isolated within the container namespace
- Localhost Port Binding : SMB ports are bound to
127.0.0.1only (see 3.3) - Single-Purpose Container : No other services or users exist in the container
- Controlled Environment : Users explicitly choose to run this container and trust its purpose
Sources : Dockerfile9 Dockerfile21 smb.conf:13-14
Code-to-System Entity Mapping
The following table maps permission-related identifiers in code to their system-level meanings:
| Code Identifier | File | Line(s) | System Entity | Purpose |
|---|---|---|---|---|
sshuser | Dockerfile | 9 | Linux user account | SSHFS mount owner |
sshpass | Dockerfile | 9 | Password string | Authentication credential |
useradd -m | Dockerfile | 9 | Command | User creation with home dir |
777 | Dockerfile | 21 | Octal permission | World-readable/writable/executable |
user_allow_other | Dockerfile | 24 | FUSE config option | Enable cross-user mount access |
/etc/fuse.conf | Dockerfile | 24 | Config file path | FUSE system configuration |
force user | smb.conf | 19 | Samba directive | Override client user identity |
guest ok | smb.conf | 13 | Samba directive | Allow guest connections |
guest only | smb.conf | 14 | Samba directive | Enforce guest authentication |
map to guest | smb.conf | 4 | Samba directive | Guest authentication trigger |
create mask | smb.conf | 17 | Samba directive | New file permissions |
directory mask | smb.conf | 18 | Samba directive | New directory permissions |
Sources : Dockerfile9 Dockerfile21 Dockerfile24 smb.conf4 smb.conf:13-14 smb.conf:17-19
Summary
The docker-sshfs container implements a permission model that uses:
- A dedicated
sshuseraccount (UID 1000) as the consistent identity layer - World-writable permissions (
777) on/samba-sharefor cross-process access - FUSE's
user_allow_otherconfiguration to enable Samba access to SSHFS mounts - Samba's
force userdirective to normalize all client operations tosshuser - Consistent UID/GID mapping (1000:1000) across SSHFS mount options
This architecture trades strict security controls for operational simplicity, relying on Docker's container isolation to maintain security boundaries. The result is a system where macOS clients can seamlessly access remote SSH filesystems through Samba without encountering permission-denied errors.
Sources : Dockerfile9 Dockerfile21 Dockerfile24 smb.conf4 smb.conf:13-14 smb.conf:17-19
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Service Lifecycle
Relevant source files
This page describes how the Samba service (smbd) operates as the container's main process, the sequence of events during container startup and shutdown, and the runtime behavior of the service. For details on SSHFS mount operations and options, see Mounting Remote Filesystems. For Samba configuration details, see Samba Configuration).
Container Process Model
The docker-sshfs container runs smbd (Samba daemon) as its PID 1 process , making it the container's main process. This is defined in the Dockerfile's CMD directive Dockerfile33
Key Process Flags
| Flag | Purpose |
|---|---|
--foreground | Prevents smbd from daemonizing, keeping it attached to the terminal so Docker can monitor it |
--no-process-group | Disables process group creation, ensuring proper signal handling in the container environment |
--debug-stdout | Redirects log output to stdout, making logs visible via docker logs |
The foreground operation is critical because Docker monitors PID 1 to determine container health. If smbd were to daemonize (default behavior), the container would immediately exit after startup.
Sources: Dockerfile33
Startup Sequence
The following diagram shows the complete startup sequence when docker run is invoked:
sequenceDiagram
participant User
participant Docker as "Docker Engine"
participant Container as "Container Process"
participant FS as "Filesystem"
participant Smbd as "smbd Process"
User->>Docker: docker run --privileged -p 127.0.0.1:139:139 -p 127.0.0.1:445:445 docker-sshfs
rect rgb(240, 240, 240)
Note over Docker,Container: Container Initialization Phase
Docker->>Container: Create container from docker-sshfs image
Container->>FS: Mount root filesystem
Container->>FS: Verify /remote directory exists
Container->>FS: Verify /samba-share directory exists
Container->>FS: Verify symbolic link /samba-share/remote -> /remote
Container->>FS: Apply permissions: chmod 777 /samba-share
end
rect rgb(240, 240, 240)
Note over Container,Smbd: Service Startup Phase
Container->>Smbd: Execute CMD: smbd --foreground --no-process-group --debug-stdout
Smbd->>FS: Read /etc/samba/smb.conf
Smbd->>Smbd: Parse global configuration (workgroup, security=user, map to guest)
Smbd->>Smbd: Load share definition [SSHFS Share] at /samba-share
Smbd->>Smbd: Bind to ports 139 (NetBIOS) and 445 (SMB)
Smbd-->>Container: Service ready, listening on ports
end
rect rgb(240, 240, 240)
Note over Docker,User: Container Running
Docker-->>User: Container ID returned
User->>Docker: docker logs docker-sshfs (optional)
Docker-->>User: smbd output via --debug-stdout
end
Startup Phases
Phase 1: Container Initialization
- Docker creates the container from the
docker-sshfsimage - Filesystem structure is verified:
/remotedirectory Dockerfile12/samba-sharedirectory Dockerfile15- Symbolic link from
/samba-share/remoteto/remoteDockerfile30
- Permissions are applied:
/samba-shareis set to777Dockerfile21
Phase 2: Service Startup
smbdprocess launches as PID 1 Dockerfile33- Configuration is loaded from
/etc/samba/smb.confDockerfile18 - Network services bind to ports 139 and 445 Dockerfile27
- Service enters ready state, waiting for SMB connections
At this point, the container is ready but the /remote directory is empty (no SSHFS mount yet). The Samba service is fully operational and serving the /samba-share directory, but it only contains the symbolic link to the unmounted /remote directory.
Sources: Dockerfile12 Dockerfile15 Dockerfile18 Dockerfile21 Dockerfile27 Dockerfile30 Dockerfile33 README31
Runtime Service States
The container transitions through multiple states during its lifecycle:
State Descriptions
| State | Description | Key Characteristics |
|---|---|---|
| ContainerCreated | Container exists but smbd has not started | No network services active |
| SmbdStarting | smbd process initializing | Reading configuration, binding ports |
| SmbdReady/NoRemoteMount | Samba service operational, no SSHFS mount | /samba-share accessible but /remote empty |
| SmbdReady/RemoteMounted | Both Samba and SSHFS operational | Remote files accessible via /samba-share/remote |
| SambaIdle | No active SMB client connections | Service listening, no file operations |
| SambaActive | macOS Finder connected and accessing files | File I/O operations in progress |
| SmdbStopping | Container shutting down | Ports being released, connections closed |
Sources: Dockerfile33 README31 README49 README76
Service Initialization Details
When smbd starts, it performs the following initialization sequence:
Configuration Loading
The smbd process reads its configuration from /etc/samba/smb.conf, which is copied into the image during build Dockerfile18 Key configuration directives that affect service behavior:
- Guest Access :
map to guest = Bad Userallows unauthenticated connections - Forced User Identity :
force user = sshuserensures all operations run as UID 1000 - Protocol Version :
min protocol = SMB2enforces modern SMB protocol - Write Permissions :
writable = yeswithcreate mask = 0777allows full read/write access
Sources: Dockerfile18 Dockerfile33
SSHFS Mount Integration
While smbd runs continuously as the main process, SSHFS mounts are performed interactively by executing commands inside the running container:
sequenceDiagram
participant User
participant Container as "Container (PID 1: smbd)"
participant Shell as "bash (docker exec)"
participant SSHFS as "sshfs Process"
participant Remote as "Remote SSH Server"
participant FS as "/remote Mount Point"
Note over Container: smbd running in foreground
User->>Container: docker exec -it docker-sshfs bash
Container->>Shell: Launch interactive bash shell
User->>Shell: sshfs -o allow_other,uid=1000,gid=1000 user@host:path /remote
Shell->>SSHFS: Fork sshfs process
SSHFS->>SSHFS: Read /etc/fuse.conf (user_allow_other enabled)
SSHFS->>Remote: Establish SSH connection
Remote-->>SSHFS: Authenticate and establish tunnel
SSHFS->>FS: Mount remote filesystem at /remote
FS-->>SSHFS: FUSE mount successful
SSHFS->>SSHFS: Run as daemon (background)
SSHFS-->>Shell: Return control
Shell-->>User: Mount complete
Note over Container,FS: /samba-share/remote -> /remote now has files
User->>Shell: exit
Note over Container: smbd continues serving /samba-share with mounted content\nNote over SSHFS: sshfs daemon continues running in background
Mount Process Details
The SSHFS mount operation occurs outside the main smbd process lifecycle:
- User opens an interactive shell via
docker execREADME41 - SSHFS command is executed manually README49
- Mount options are critical:
- SSHFS daemonizes and runs in background
- The symbolic link
/samba-share/remotenow points to populated directory Dockerfile30 smbdcontinues serving, now with access to remote files
Note that the mount target is /remote, not /samba-share directly. The symbolic link provides the integration point Dockerfile30
Sources: Dockerfile30 README41 README49 README52 README53
Shutdown Sequence
Container shutdown follows Docker's standard signal handling:
Shutdown Phases
Phase 1: Signal Delivery
- Docker sends
SIGTERMto PID 1 (smbd) - By default, Docker waits 10 seconds for graceful shutdown
- If
smbddoesn't exit, Docker sendsSIGKILL
Phase 2: Samba Cleanup
smbdcloses listening sockets on ports 139 and 445- Active SMB client connections are disconnected
- Lock files and temporary files are removed
Phase 3: SSHFS Cleanup
- Docker sends
SIGTERMto all container processes, including backgroundsshfsdaemons sshfsunmounts the remote filesystem from/remote- SSH connections are closed
Phase 4: Container Termination
- All processes have exited
- Container enters "stopped" state
- Port mappings are released on the host
Forced Shutdown
If SSHFS mount is busy (e.g., macOS Finder still accessing files), unmounting may fail README:82-83 In this case:
- User must manually unmount the share from macOS Finder first
- Then retry
fusermount -u /remoteREADME76 - Or simply
docker stopwill forcefully unmount after timeout
Sources: README76 README:82-83
Process Tree
The running container has the following process structure:
Container (docker-sshfs)
│
├── PID 1: smbd --foreground --no-process-group --debug-stdout
│ ├── Child: smbd worker processes (created on-demand for client connections)
│ └── Child: smbd housekeeping processes
│
└── PID N: sshfs user@host:path /remote -o allow_other,uid=1000,gid=1000
└── (FUSE kernel module connection)
Process Relationships
| Process | PID | Parent | Purpose |
|---|---|---|---|
smbd | 1 | None (init) | Main Samba service, container's PID 1 |
smbd workers | Dynamic | 1 | Handle individual SMB client connections |
sshfs | Variable | 1 | FUSE daemon for remote filesystem mount |
The --no-process-group flag Dockerfile33 prevents smbd from creating a separate process group, ensuring that Docker's signal delivery works correctly. Without this flag, SIGTERM might not reach all Samba worker processes.
Sources: Dockerfile33
Lifecycle Summary
The complete lifecycle can be summarized as:
| Phase | Duration | Key Process | State |
|---|---|---|---|
| Build | One-time | Docker build | Image created with smbd as CMD |
| Startup | ~1-2 seconds | smbd initialization | Ports bound, configuration loaded |
| Ready (No Mount) | Indefinite | smbd serving | /samba-share accessible but empty |
| Runtime (Mounted) | Indefinite | smbd + sshfs | Full functionality with remote files |
| Shutdown | ~1-10 seconds | Signal handling | Graceful or forced termination |
The service is designed for continuous operation with interactive SSHFS mount/unmount operations performed on-demand without restarting the container.
Sources: Dockerfile33 README31 README41 README49 README76
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Configuration Reference
Relevant source files
Purpose and Scope
This page provides a comprehensive reference for all configuration settings used by sshfs-mac-docker. The system requires three distinct types of configuration: Samba server configuration (for SMB protocol), FUSE configuration (for filesystem access permissions), and SSHFS mount options (for remote filesystem behavior).
For detailed explanations of specific configuration files and their parameters, see:
This page focuses on the relationships between configurations, their locations in the system, and how they interact to enable the protocol bridge between SSH and SMB.
Configuration Overview
The system uses a layered configuration approach with distinct build-time and runtime settings:
| Configuration Type | Applied At | Purpose | Modifiable at Runtime |
|---|---|---|---|
| Samba Configuration | Build-time | Defines SMB share behavior, authentication, and file permissions | No (requires image rebuild) |
| FUSE Configuration | Build-time | Enables cross-user filesystem access | No (requires image rebuild) |
| SSHFS Mount Options | Runtime | Controls remote mount behavior, permissions, and ownership | Yes (per mount operation) |
The build-time configurations are embedded in the Docker image via Dockerfile:17-24 while runtime options are specified when executing the sshfs command inside the container.
Sources: Dockerfile:17-24 README.md:49-53
Configuration File Locations
Configuration File Mapping
Diagram: Configuration File Locations and Build Process
The diagram shows how configuration files move from the repository into the container filesystem during the build process, and how they control different aspects of the system at runtime.
Sources: Dockerfile:1-34 smb.conf:1-20
Configuration Hierarchy and Application Points
Build-Time vs Runtime Configuration
Diagram: Configuration Application Timeline
This diagram shows the temporal relationship between configuration application points. Build-time configurations are immutable once the image is created, while runtime configurations can be changed with each mount operation.
Sources: Dockerfile:1-34 README.md:19-53
Configuration Dependencies
Inter-Configuration Dependencies
The three configuration types have strict dependencies that must be satisfied for the system to function:
Diagram: Configuration Dependency Graph
graph TB
subgraph "FUSE Configuration Dependencies"
FuseConf["user_allow_other\n/etc/fuse.conf:last line"]
AllowOther["allow_other option\nsshfs mount parameter"]
CrossUserAccess["Samba can access\nSSHFS-mounted files"]
end
subgraph "User and Permission Dependencies"
SSHUser["sshuser:sshpass\nUID 1000, GID 1000"]
ForceUser["force user = sshuser\nsmb.conf:19"]
UidGidOptions["uid=1000,gid=1000\nsshfs mount parameters"]
FileOwnership["Consistent file ownership\nacross protocols"]
end
subgraph "Directory Permission Dependencies"
SambaSharePerm["chmod 777 /samba-share\nDockerfile:21"]
Writable["writable = yes\nsmb.conf:12"]
CreateMask["create mask = 0777\nsmb.conf:17"]
WriteAccess["Write access\nfrom macOS"]
end
subgraph "Guest Access Dependencies"
MapToGuest["map to guest = bad user\nsmb.conf:4"]
GuestOk["guest ok = yes\nsmb.conf:13"]
GuestOnly["guest only = yes\nsmb.conf:14"]
NoAuth["No password required\nfrom macOS"]
end
FuseConf -->|Enables| AllowOther
AllowOther -->|Required for| CrossUserAccess
SSHUser -->|Identity enforced by| ForceUser
SSHUser -->|UID/GID matched by| UidGidOptions
ForceUser --> FileOwnership
UidGidOptions --> FileOwnership
SambaSharePerm -->|Base permissions| WriteAccess
Writable -->|Samba-level permissions| WriteAccess
CreateMask -->|New file permissions| WriteAccess
MapToGuest -->|Maps unknown users| NoAuth
GuestOk -->|Allows guest login| NoAuth
GuestOnly -->|Forces guest mode| NoAuth
This diagram illustrates critical dependencies between configuration parameters. If any dependency is missing, specific functionality (cross-user access, write access, or guest authentication) will fail.
Sources: smb.conf:1-20 Dockerfile:9-24 README.md:49-53
Critical Configuration Parameters
Parameter Reference Table
| Parameter | File Location | Line Number | Value | Required | Purpose |
|---|---|---|---|---|---|
user_allow_other | /etc/fuse.conf | Appended | N/A | Yes | Allows non-mounting users (Samba) to access FUSE filesystems |
allow_other | sshfs command | Runtime option | N/A | Yes | Enables cross-user access to SSHFS mount |
uid=1000 | sshfs command | Runtime option | 1000 | Yes | Sets file ownership to sshuser for write access |
gid=1000 | sshfs command | Runtime option | 1000 | Yes | Sets group ownership to sshuser for write access |
force user | /etc/samba/smb.conf | 19 | sshuser | Yes | Forces all Samba operations to use sshuser identity |
map to guest | /etc/samba/smb.conf | 4 | bad user | Yes | Maps unknown/invalid users to guest account |
guest ok | /etc/samba/smb.conf | 13 | yes | Yes | Allows guest access to share |
guest only | /etc/samba/smb.conf | 14 | yes | Yes | Forces guest mode even if credentials provided |
writable | /etc/samba/smb.conf | 12 | yes | Yes | Enables write access through Samba |
create mask | /etc/samba/smb.conf | 17 | 0777 | No | Permissions for newly created files |
directory mask | /etc/samba/smb.conf | 18 | 0777 | No | Permissions for newly created directories |
client min protocol | /etc/samba/smb.conf | 7 | SMB2 | No | Minimum SMB protocol version |
server min protocol | /etc/samba/smb.conf | 8 | SMB2 | No | Minimum SMB protocol version |
Configuration Omission Impact
Diagram: Configuration Omission Impact
This diagram shows the direct consequences of missing critical configuration parameters. Each omission results in a specific failure mode.
Sources: Dockerfile24 README.md:49-53 smb.conf19
Configuration Interaction Model
Protocol Bridge Configuration Flow
Diagram: Configuration Interaction During Write Operation
This diagram traces how configuration parameters interact when a file write operation flows from macOS Finder through Samba, the filesystem layer, SSHFS, and finally to the remote server. Each layer consults its configuration to validate and process the operation.
Sources: smb.conf:1-20 Dockerfile:21-30 README.md:49-53
Configuration Validation Checklist
Use this checklist to verify that all configurations are correctly applied:
Build-Time Configuration Verification
Runtime Configuration Verification
Sources: Dockerfile:1-34 smb.conf:1-20 README.md:19-53
Configuration Modification Guidelines
Which Configurations Can Be Changed
| Configuration | Modifiable After Build | How to Modify | Requires Container Restart |
|---|---|---|---|
Samba settings (smb.conf) | No | Rebuild image with modified smb.conf | N/A (new container) |
FUSE settings (fuse.conf) | No | Rebuild image with modified Dockerfile | N/A (new container) |
| SSHFS mount options | Yes | Use different options in sshfs command | No (remount required) |
User credentials (sshuser:sshpass) | No | Rebuild image with modified Dockerfile | N/A (new container) |
Directory permissions (/samba-share) | No | Rebuild image with modified Dockerfile | N/A (new container) |
| Port mappings | No | Stop and restart with docker run | Yes (container restart) |
| Privileged mode | No | Stop and restart with docker run | Yes (container restart) |
Changing SSHFS Mount Options
SSHFS mount options are the only runtime-configurable settings. To change them:
- Unmount the existing mount:
fusermount -u /samba-share - Mount with new options:
sshfs -o [new-options] user@host:path /samba-share
Common option modifications:
- Change ownership : Modify
uid=andgid=values - Add compression : Include
compression=yes - Adjust timeout : Include
ServerAliveInterval=15 - Enable debug : Include
debug,sshfs_debug,loglevel=debug
Sources: README.md:49-77
Configuration Files Summary
Complete File Reference
Diagram: Configuration Files and Their Relationships
This diagram maps the repository files to the container filesystem files they create or modify. All configuration originates from the two repository files: smb.conf and Dockerfile.
Sources: Dockerfile:1-34 smb.conf:1-20
For detailed documentation of specific configuration files and their parameters, continue to:
- 5.1 Samba Configuration (smb.conf)) - Complete reference for all
smb.confparameters - 5.2 FUSE Configuration - FUSE access control and
user_allow_othersetting - 5.3 SSHFS Mount Options - Runtime mount options including
allow_other,uid, andgid
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Samba Configuration (smb.conf)
Relevant source files
Purpose and Scope
This document provides a complete reference for the smb.conf file, which configures the Samba server running inside the docker-sshfs container. The configuration establishes the SMB/CIFS protocol bridge that makes remote SSH filesystems accessible to macOS Finder through guest-authenticated network shares.
For information about FUSE configuration that enables Samba to access SSHFS mounts, see FUSE Configuration. For details about SSHFS mount options that work with this configuration, see SSHFS Mount Options. For the broader security model, see Security Model.
Sources : smb.conf:1-20
Configuration File Structure
The smb.conf file contains two distinct sections that control different aspects of the Samba server:
| Section | Lines | Purpose |
|---|---|---|
[global] | smb.conf:1-8 | Server-wide settings including security model, protocol versions, and network identity |
[SSHFS Share] | smb.conf:10-19 | Share-specific settings for the /samba-share directory including permissions and user mapping |
Sources : smb.conf:1-20
Global Configuration Section
The [global] section at smb.conf:1-8 defines server-wide behavior for the Samba daemon. These settings apply to all shares and control how the server identifies itself on the network and handles client connections.
graph LR
subgraph "Configuration"
workgroup["workgroup = WORKGROUP\nLine 2"]
netbios["netbios name = sambaserver\nLine 6"]
serverstring["server string = Samba Server %v\nLine 5"]
end
subgraph "macOS Finder Discovery"
BrowseList["Network Browser\nShows: sambaserver"]
ConnectionDialog["Connection Dialog\nWorkgroup: WORKGROUP"]
end
subgraph "SMB Protocol"
NetBIOS["NetBIOS Name Resolution\nPort 139"]
DirectTCP["Direct TCP/IP\nPort 445"]
end
workgroup --> ConnectionDialog
netbios --> BrowseList
netbios --> NetBIOS
netbios --> DirectTCP
serverstring --> BrowseList
Network Identity Settings
| Directive | Value | Line | Purpose |
|---|---|---|---|
workgroup | WORKGROUP | smb.conf2 | Sets Windows workgroup name; matches default macOS SMB client expectations |
netbios name | sambaserver | smb.conf6 | Defines the server's NetBIOS identity for network browsing and name resolution |
server string | Samba Server %v | smb.conf5 | Human-readable description displayed in network browsers; %v expands to Samba version |
Sources : smb.conf:2-6
Security and Authentication Model
The security configuration at smb.conf:3-4 implements a guest-only access model that eliminates password requirements while maintaining consistent file ownership:
graph TB
subgraph "Configuration Directives"
security["security = user\nLine 3"]
mapguest["map to guest = bad user\nLine 4"]
end
subgraph "Client Connection Flow"
MacOSClient["macOS Finder Client\nConnect as Guest"]
BadPassword["Any Username\nInvalid/No Password"]
end
subgraph "Authentication Processing"
UserCheck["Samba Authentication Check"]
GuestMap["Map to Guest Account"]
ForceUserApply["Apply force user = sshuser"]
end
subgraph "Resulting Access"
SSHUserAccess["All Operations as sshuser\nUID 1000, GID 1000"]
FilesystemOps["Filesystem Operations\nVia /samba-share symlink"]
end
security --> UserCheck
mapguest --> GuestMap
MacOSClient --> BadPassword
BadPassword --> UserCheck
UserCheck -->|User doesn't exist| GuestMap
GuestMap --> ForceUserApply
ForceUserApply --> SSHUserAccess
SSHUserAccess --> FilesystemOps
| Directive | Value | Line | Behavior |
|---|---|---|---|
security | user | smb.conf3 | Requires user-level authentication (not share-level); enables per-user access control but overridden by map to guest |
map to guest | bad user | smb.conf4 | Converts any connection with an invalid username into a guest connection; effectively disables password authentication |
The bad user mapping combined with guest only = yes in the share definition creates a passwordless authentication flow where any connection attempt succeeds as guest access, then force user ensures all operations execute as sshuser.
Sources : smb.conf:3-4
Protocol Version Enforcement
Lines smb.conf:7-8 enforce minimum SMB protocol versions, eliminating support for the deprecated SMB1 protocol:
graph LR
subgraph "Protocol Configuration"
ClientMin["client min protocol = SMB2\nLine 7"]
ServerMin["server min protocol = SMB2\nLine 8"]
end
subgraph "Supported Protocols"
SMB2["SMB 2.0\n✓ Supported"]
SMB21["SMB 2.1\n✓ Supported"]
SMB3["SMB 3.x\n✓ Supported"]
end
subgraph "Rejected Protocols"
SMB1["SMB 1.0 / CIFS\n✗ Rejected"]
end
ClientMin --> SMB2
ServerMin --> SMB2
ClientMin --> SMB21
ServerMin --> SMB21
ClientMin --> SMB3
ServerMin --> SMB3
ClientMin -.->|Blocks| SMB1
ServerMin -.->|Blocks| SMB1
subgraph "Security Benefits"
NoSMB1Vulns["Eliminates SMB1\nSecurity Vulnerabilities"]
end
SMB1 --> NoSMB1Vulns
| Directive | Value | Line | Effect |
|---|---|---|---|
client min protocol | SMB2 | smb.conf7 | Prevents Samba from acting as client using SMB1 (not directly relevant for this server-only use case) |
server min protocol | SMB2 | smb.conf8 | Rejects client connections attempting to use SMB1; enforces modern protocol versions |
Modern macOS versions default to SMB2+ for new connections, making this configuration transparent for most users while preventing potential SMB1-related security issues.
Sources : smb.conf:7-8
Share Configuration Section
The [SSHFS Share] section at smb.conf:10-19 defines the actual network share that macOS clients connect to. This configuration establishes the critical link between the Samba server and the SSHFS-mounted filesystem.
graph TB
subgraph "Share Configuration"
ShareName["[SSHFS Share]\nLine 10"]
Path["path = /samba-share\nLine 11"]
Browseable["browseable = yes\nLine 16"]
end
subgraph "Filesystem Structure"
SambaShareDir["/samba-share Directory\nchmod 777\nCreated by Dockerfile"]
SymLink["/samba-share/remote\nSymbolic Link"]
RemoteMount["/remote Mount Point\nSSHFS Target"]
end
subgraph "macOS Access"
FinderBrowser["Finder Network Browser\nShows: SSHFS Share"]
MountPoint["Mounted Volume\nsmb://container-ip/SSHFS Share"]
end
ShareName --> Path
Path --> SambaShareDir
Browseable --> FinderBrowser
SambaShareDir --> SymLink
SymLink -.->|Links to| RemoteMount
FinderBrowser --> MountPoint
MountPoint --> SambaShareDir
Share Path and Visibility
| Directive | Value | Line | Purpose |
|---|---|---|---|
| Share name | SSHFS Share | smb.conf10 | Display name shown in macOS Finder network browser; spaces are preserved in SMB share names |
path | /samba-share | smb.conf11 | Root directory served by this share; contains the symbolic link to /remote SSHFS mount point |
browseable | yes | smb.conf16 | Makes share visible in network browsing; allows discovery without knowing share name in advance |
The share name SSHFS Share (with space) appears verbatim in macOS Finder. The directory at /samba-share must exist with appropriate permissions (set to 777 by the Dockerfile) before Samba starts.
Sources : smb.conf:10-16
Write Access Configuration
The configuration at smb.conf:12-18 enables full read-write access with permissive file creation masks:
| Directive | Value | Line | Effect |
|---|---|---|---|
writable | yes | smb.conf12 | Enables write operations (create, modify, delete); opposite of read only |
read only | no | smb.conf15 | Explicitly disables read-only mode; redundant with writable = yes but included for clarity |
create mask | 0777 | smb.conf17 | Sets permission mask for newly created files to full access (owner/group/other all have rwx) |
directory mask | 0777 | smb.conf18 | Sets permission mask for newly created directories to full access |
The 0777 masks are unusually permissive but necessary for this use case because:
- The
force userdirective causes all operations to run assshuser - SSHFS mount options set
uid=1000,gid=1000matchingsshuser - Files created must be accessible regardless of how they're accessed (via SMB or direct container shell)
Sources : smb.conf:12-18
Guest Access Enforcement
Lines smb.conf:13-14 enforce guest-only access at the share level, complementing the global map to guest setting:
graph TB
subgraph "Global Settings"
GlobalSecurity["security = user\nLine 3"]
MapGuest["map to guest = bad user\nLine 4"]
end
subgraph "Share Settings"
GuestOK["guest ok = yes\nLine 13"]
GuestOnly["guest only = yes\nLine 14"]
end
subgraph "Connection Attempts"
ValidUser["Valid Username\n+ Correct Password"]
InvalidUser["Invalid Username\n+ Any Password"]
Guest["Explicit Guest Connection"]
end
subgraph "Authentication Result"
AllGuest["All Connections\nAuthenticated as Guest"]
end
GlobalSecurity --> MapGuest
MapGuest --> GuestOK
GuestOnly --> AllGuest
ValidUser --> MapGuest
InvalidUser --> MapGuest
Guest --> GuestOK
MapGuest --> AllGuest
GuestOK --> AllGuest
| Directive | Value | Line | Behavior |
|---|---|---|---|
guest ok | yes | smb.conf13 | Allows guest connections to this share; permits access without valid credentials |
guest only | yes | smb.conf14 | Forces all connections to use guest access even if valid credentials are provided; prevents authenticated access |
The combination of guest ok = yes and guest only = yes creates a mandatory guest access policy. Even if a user provides valid credentials, Samba ignores them and processes the connection as guest. This works in conjunction with force user to ensure consistent identity.
Sources : smb.conf:13-14
User Identity Mapping
The force user directive at smb.conf19 is the critical configuration that ensures filesystem operations have correct ownership:
graph TB
subgraph "Configuration Chain"
GuestAuth["Guest Authentication\nLines 13-14"]
ForceUser["force user = sshuser\nLine 19"]
SSHUserAccount["sshuser Account\nUID 1000, GID 1000"]
end
subgraph "SSHFS Mount"
MountOptions["sshfs -o uid=1000,gid=1000\nMount Options"]
RemoteMount["/remote Mount Point\nOwned by UID 1000"]
end
subgraph "Filesystem Operations"
SambaWrite["SMB Write Request\nfrom macOS"]
ActualWrite["Filesystem Write\nas sshuser UID 1000"]
SSHFSWrite["SSHFS Operation\nto Remote Server"]
end
subgraph "Permission Matching"
UIDMatch["UID 1000 = UID 1000\nWrite Permission Granted"]
end
GuestAuth --> ForceUser
ForceUser --> SSHUserAccount
SSHUserAccount --> ActualWrite
MountOptions --> RemoteMount
RemoteMount --> UIDMatch
ActualWrite --> UIDMatch
SambaWrite --> ActualWrite
ActualWrite --> SSHFSWrite
| Directive | Value | Line | Purpose |
|---|---|---|---|
force user | sshuser | smb.conf19 | Overrides the authenticated user identity; all filesystem operations execute as sshuser regardless of client credentials |
Why This Matters : Without force user = sshuser, writes would fail because:
- Guest connections typically map to the
nobodyuser - The
/remoteSSHFS mount is owned bysshuser(UID 1000) due to mount options - FUSE by default only allows the mounting user to access mounted filesystems
- Even with
allow_other, write permissions depend on matching UIDs
The force user directive ensures that Samba's effective UID matches the SSHFS mount's ownership, enabling write access through the protocol bridge.
Sources : smb.conf19
graph TB
subgraph "smb.conf Configuration"
direction TB
Global["[global] Section"]
Share["[SSHFS Share] Section"]
MapGuest["map to guest = bad user"]
ForceUser["force user = sshuser"]
Path["path = /samba-share"]
MinProtocol["server min protocol = SMB2"]
CreateMask["create mask = 0777"]
end
subgraph "Dockerfile Setup"
SSHUserCreate["RUN useradd -m -u 1000 sshuser"]
SambaShareMkdir["RUN mkdir -p /samba-share"]
SambaShareChmod["RUN chmod 777 /samba-share"]
SymlinkCreate["RUN ln -s /remote /samba-share/remote"]
end
subgraph "FUSE Configuration"
AllowOther["user_allow_other\nin /etc/fuse.conf"]
end
subgraph "Runtime Operations"
SSHFSMount["sshfs -o allow_other,uid=1000,gid=1000"]
SmbdProcess["smbd -F -S"]
MacOSConnection["macOS Finder\nSMB Connection"]
end
subgraph "Filesystem Access"
SambaRead["Read Operations\nvia /samba-share"]
SambaWrite["Write Operations\nas sshuser UID 1000"]
FUSEAccess["FUSE Mount Access\nallow_other enables"]
end
%% Configuration dependencies
ForceUser -.->|Requires user exists| SSHUserCreate
Path -.->|Requires directory| SambaShareMkdir
CreateMask -.->|Directory must be writable| SambaShareChmod
Path -.->|Contains symlink| SymlinkCreate
%% Runtime flow
Global --> SmbdProcess
Share --> SmbdProcess
MinProtocol --> MacOSConnection
MapGuest --> MacOSConnection
SSHFSMount --> FUSEAccess
AllowOther --> FUSEAccess
SmbdProcess --> SambaRead
SmbdProcess --> SambaWrite
FUSEAccess --> SambaRead
FUSEAccess --> SambaWrite
%% Critical path
ForceUser --> SambaWrite
SSHFSMount -.->|UID must match| SambaWrite
Configuration Integration Map
This diagram shows how smb.conf directives integrate with other system components:
Sources : smb.conf:1-20
Security Implications
The smb.conf configuration implements a convenience-over-security model appropriate for local development but not production environments:
Security Trade-offs Table
| Configuration Choice | Security Impact | Rationale |
|---|---|---|
map to guest = bad user | ❌ No authentication required | Eliminates password friction for local development; container ports bound to 127.0.0.1 limits exposure |
guest only = yes | ❌ Valid credentials ignored | Ensures consistent behavior; prevents confusion when credentials are provided but unused |
create mask = 0777 | ❌ World-writable files | Required for UID/GID consistency across SMB and direct container access |
directory mask = 0777 | ❌ World-writable directories | Prevents permission issues when accessing via different methods |
force user = sshuser | ⚠️ Single user identity | Enables write access through SSHFS mount; loses audit trail of actual user |
server min protocol = SMB2 | ✅ Blocks SMB1 vulnerabilities | Modern protocol enforcement reduces attack surface |
| Ports bound to 127.0.0.1 | ✅ Localhost-only access | Prevents external network exposure despite guest access (requires Docker run configuration) |
Mitigating Factors
The security model is acceptable for local development because:
- Network isolation : Docker
runcommand binds ports to127.0.0.1, preventing LAN access - Temporary containers : Development containers are typically short-lived and frequently rebuilt
- Single-user context : Developer's workstation is single-user; no multi-tenant concerns
- Controlled environment : User has root access to host system anyway; container provides convenience not security boundary
Production Considerations
For production use, this configuration would require modifications:
- Implement user authentication (remove
map to guest) - Use restrictive create masks (0644 for files, 0755 for directories)
- Configure per-user
force userbased on authenticated identity - Enable SMB signing and encryption
- Use certificate-based authentication for SMB3+
Sources : smb.conf:3-19
graph TB
subgraph "Required Alignments"
direction TB
Alignment1["force user = sshuser\n↕\nuseradd -u 1000 sshuser\n↕\nsshfs -o uid=1000"]
Alignment2["path = /samba-share\n↕\nmkdir -p /samba-share\n↕\nln -s /remote /samba-share/remote"]
Alignment3["create mask = 0777\n↕\nchmod 777 /samba-share\n↕\nsshfs -o allow_other"]
Alignment4["guest ok = yes\n↕\nmap to guest = bad user\n↕\nmacOS Connect as Guest"]
end
subgraph "Failure Modes"
direction TB
Failure1["UID Mismatch\n→ Permission denied on write"]
Failure2["Missing Directory\n→ smbd fails to start"]
Failure3["Permission Conflict\n→ FUSE denies Samba access"]
Failure4["Auth Mismatch\n→ Connection rejected"]
end
Alignment1 -.->|If misaligned| Failure1
Alignment2 -.->|If misaligned| Failure2
Alignment3 -.->|If misaligned| Failure3
Alignment4 -.->|If misaligned| Failure4
Critical Configuration Dependencies
Certain smb.conf settings must align with other system configurations or functionality breaks:
Dependency Matrix
| smb.conf Setting | Must Match | Location | Verification Command |
|---|---|---|---|
force user = sshuser | User account with UID 1000 | Dockerfile | id sshuser should show uid=1000 |
force user = sshuser | SSHFS mount uid=1000 | Mount command | `mount |
path = /samba-share | Existing directory | Dockerfile | ls -ld /samba-share should exist with 777 |
path = /samba-share | Contains symlink to /remote | Dockerfile | ls -l /samba-share/remote should show symlink |
create mask = 0777 | Directory permissions 777 | Dockerfile | stat -c %a /samba-share should return 777 |
guest ok = yes | map to guest = bad user in [global] | smb.conf4 | Both directives must be present |
server min protocol = SMB2 | macOS SMB client version | macOS 10.11+ | Modern macOS defaults to SMB2+ |
Sources : smb.conf:1-20
sequenceDiagram
participant smbd as smbd Process
participant config as smb.conf
participant fs as Filesystem
participant client as macOS Client
participant mount as SSHFS Mount
Note over smbd: Container starts
smbd->>config: Parse /etc/samba/smb.conf
smbd->>fs: Verify /samba-share exists
fs-->>smbd: Directory found (777 permissions)
smbd->>smbd: Bind ports 139, 445
smbd->>smbd: Enable SMB2+ only
Note over smbd: Ready to accept connections
client->>smbd: SMB connection request
smbd->>config: Check [global] security settings
config-->>smbd: map to guest = bad user
smbd->>smbd: Authenticate as guest
client->>smbd: Browse shares
smbd->>config: Read [SSHFS Share] section
config-->>smbd: browseable = yes
smbd-->>client: Return share list: SSHFS Share
client->>smbd: Mount "SSHFS Share"
smbd->>config: Get path directive
config-->>smbd: path = /samba-share
smbd->>config: Get force user directive
config-->>smbd: force user = sshuser
smbd->>fs: Access /samba-share as sshuser
fs-->>smbd: Access granted (UID 1000)
smbd-->>client: Mount success
client->>smbd: Write file request
smbd->>config: Check writable setting
config-->>smbd: writable = yes
smbd->>config: Get create mask
config-->>smbd: create mask = 0777
smbd->>fs: Create file as sshuser (0777)
fs->>mount: Write propagates to SSHFS
mount-->>fs: Write complete
fs-->>smbd: File created
smbd-->>client: Write success
Runtime Behavior
When the Samba daemon starts with this configuration, it produces the following behavior:
Sources : smb.conf:1-20
graph TB
subgraph "Layer 3: Remote Server"
RemoteFS["Remote Filesystem\nSSH Server"]
end
subgraph "Layer 2: Docker Container"
direction TB
SSHFS["SSHFS Client\nuid=1000,gid=1000\nallow_other"]
RemoteMount["/remote Mount Point\nFUSE Filesystem"]
Symlink["/samba-share/remote\nSymbolic Link"]
SambaDir["/samba-share\nRoot Directory\nchmod 777"]
SmbConf["smb.conf\nConfiguration File"]
Smbd["smbd Daemon\nServing /samba-share"]
end
subgraph "Layer 1: macOS Host"
Finder["macOS Finder\nSMB Client"]
end
%% Connections
RemoteFS -->|SSH Protocol| SSHFS
SSHFS -->|Mounts to| RemoteMount
RemoteMount -.->|Linked by| Symlink
Symlink -->|Points to| SambaDir
SambaDir -->|Contains| Symlink
SmbConf -->|Configures| Smbd
SmbConf -.->|Defines path = /samba-share| SambaDir
SmbConf -.->|Defines force user = sshuser| SSHFS
Smbd -->|Serves| SambaDir
Finder -->|SMB Protocol| Smbd
style SmbConf fill:#fff4e1
Relationship to System Architecture
The smb.conf file sits at a critical integration point in the three-tier architecture:
The configuration file's path directive connects Samba to the filesystem layer, while force user ensures UID alignment with SSHFS mount ownership. Without correct smb.conf settings, the protocol bridge fails even if SSHFS and Samba are both operational.
Sources : smb.conf:11-19
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
FUSE Configuration
Relevant source files
Purpose and Scope
This page documents the FUSE (Filesystem in Userspace) configuration used by sshfs-mac-docker, specifically the modifications made to /etc/fuse.conf. The primary focus is the user_allow_other setting and its critical role in enabling cross-service filesystem access between SSHFS and Samba.
For Samba-specific configuration settings, see Samba Configuration (smb.conf)). For the SSHFS mount options that depend on this FUSE configuration, see SSHFS Mount Options.
FUSE Overview
FUSE (Filesystem in Userspace) is a software interface that allows non-privileged users to create their own file systems without modifying kernel code. SSHFS is built on top of FUSE, enabling remote directories to be mounted as if they were local filesystems through FUSE operations.
In sshfs-mac-docker, FUSE operations occur entirely within the Docker container, avoiding the need for macFUSE or kernel extensions on the macOS host.
Sources: README.md, Dockerfile
The /etc/fuse.conf File
The /etc/fuse.conf file controls system-wide FUSE behavior. It is a simple text configuration file that defines security policies for FUSE mounts.
| Property | Value |
|---|---|
| File Path | /etc/fuse.conf |
| Format | Plain text, one directive per line |
| Ownership | root:root |
| Default State | Contains commented-out directives |
| Modification Method | Appended during Docker image build |
Sources: Dockerfile:23-24
The user_allow_other Setting
Syntax and Meaning
user_allow_other
This single-line directive enables non-root users to specify the allow_other mount option when creating FUSE mounts.
Default Behavior Without This Setting
By default, FUSE restricts the allow_other mount option to the root user for security reasons. If a non-root user attempts to mount a FUSE filesystem with allow_other without this setting enabled, the mount operation fails with a permission error:
fuse: option allow_other only allowed if 'user_allow_other' is set in /etc/fuse.conf
Security Implications
The allow_other option permits users other than the one who performed the mount to access the filesystem. This represents a security relaxation, as it bypasses FUSE's default isolation model. The user_allow_other setting delegates the decision to use this option to individual users rather than requiring root privileges for every mount that needs cross-user access.
Sources: Dockerfile:23-24, README.md:52
Configuration in sshfs-mac-docker
Dockerfile Implementation
The FUSE configuration is established during the Docker image build process:
This command appends the user_allow_other directive to the existing /etc/fuse.conf file. The >> operator ensures the line is added without overwriting any default content.
Build-Time vs. Runtime
| Aspect | Details |
|---|---|
| When Applied | During docker build execution |
| Persistence | Baked into the Docker image |
| Modification Scope | Container filesystem only, not host |
| Requires Rebuild | Yes, if this line is removed from Dockerfile |
Sources: Dockerfile:23-24
Why user_allow_other is Essential
The Cross-Service Access Problem
The sshfs-mac-docker architecture requires two separate services to access the same filesystem:
- SSHFS Client : Mounts the remote filesystem (runs as
sshuser) - Samba Server : Serves the mounted filesystem to macOS (runs as
rootor configured user)
graph TB
subgraph "Without user_allow_other"
SSHFS1["sshfs mount by sshuser"]
FUSE1["FUSE Kernel Module"]
Mount1["/remote mount point"]
Samba1["smbd service (different user)"]
Access1["❌ Permission Denied"]
SSHFS1 --> FUSE1
FUSE1 --> Mount1
Samba1 -.->|Attempts access| Mount1
Mount1 -.->|Blocked by FUSE| Access1
end
subgraph "With user_allow_other + allow_other mount option"
SSHFS2["sshfs -o allow_other"]
FUSE2["FUSE Kernel Module\n(user_allow_other enabled)"]
Mount2["/remote mount point"]
Samba2["smbd service (different user)"]
Access2["✓ Access Granted"]
SSHFS2 --> FUSE2
FUSE2 --> Mount2
Samba2 -->|Can access| Mount2
Mount2 --> Access2
end
Without user_allow_other and the corresponding allow_other mount option, Samba cannot access files in the SSHFS mount because FUSE's default behavior restricts access to only the user who performed the mount (sshuser).
Permission Flow Diagram
Sources: Dockerfile:23-24, README.md:49-53
Relationship to SSHFS Mount Options
Configuration Chain
The user_allow_other setting in /etc/fuse.conf is the prerequisite that enables the allow_other mount option in the SSHFS command:
Sources: Dockerfile:23-24, README.md:49-53
sequenceDiagram
participant Build as docker build
participant FuseConf as /etc/fuse.conf
participant User as Container User
participant SSHFS as sshfs command
participant FUSE as FUSE Module
participant Mount as /remote
Build->>FuseConf: Append "user_allow_other"
Note over FuseConf: Setting persisted in image
User->>SSHFS: sshfs -o allow_other user@host /remote
SSHFS->>FuseConf: Check if allow_other permitted
FuseConf-->>SSHFS: user_allow_other is set ✓
SSHFS->>FUSE: Mount with allow_other flag
FUSE->>Mount: Create mount point with cross-user access
Note over Mount: Other users can now access
Dependency Table
| Layer | Component | Configuration | Dependency |
|---|---|---|---|
| Build Time | /etc/fuse.conf | user_allow_other | None (base setting) |
| Runtime | sshfs command | -o allow_other | Requires user_allow_other in fuse.conf |
| Effect | Samba access | Cross-service file access | Requires allow_other mount option |
Sources: Dockerfile:23-24, README.md:49-53
Cross-Service Access Flow
This diagram shows how the FUSE configuration enables the complete data flow from macOS through Samba to the SSHFS-mounted remote filesystem:
Sources: Dockerfile:23-24, Dockerfile:30, README.md:49-53
graph LR
subgraph "macOS Client"
Finder["macOS Finder"]
end
subgraph "Docker Container"
subgraph "Service Layer"
Smbd["smbd process\n(different UID)"]
end
subgraph "FUSE Layer"
FuseConf["/etc/fuse.conf\nuser_allow_other"]
FuseModule["FUSE Module"]
end
subgraph "Filesystem Layer"
SambaShare["/samba-share/"]
Symlink["symbolic link"]
RemoteMount["/remote/\n(SSHFS mount)"]
end
subgraph "SSHFS Layer"
SSHFSProc["sshfs process\n(sshuser UID 1000)\n-o allow_other"]
end
end
subgraph "Remote"
RemoteFS["Remote SSH Server"]
end
Finder -->|SMB read/write| Smbd
Smbd -->|Access files| SambaShare
SambaShare --> Symlink
Symlink -.->|Points to| RemoteMount
FuseConf -.->|Enables| SSHFSProc
SSHFSProc -->|Mount with allow_other| FuseModule
FuseModule -->|Creates| RemoteMount
RemoteMount -->|Cross-user access allowed| Smbd
SSHFSProc <-->|SSH protocol| RemoteFS
Verification
Checking the Configuration
To verify that user_allow_other is properly configured in a running container:
Expected output should include:
user_allow_other
Testing Cross-User Access
When SSHFS is mounted with allow_other, verify that Samba can access the mount:
Both commands should successfully list files, confirming cross-user access is working.
Sources: README.md:41-50, Dockerfile:23-24
Configuration Summary
| Configuration Element | Location | Value | Purpose |
|---|---|---|---|
| FUSE policy directive | /etc/fuse.conf | user_allow_other | Permit non-root use of allow_other |
| Set during | Docker build | Dockerfile24 | Persistent in image |
| Enables mount option | sshfs command | -o allow_other | Cross-user file access |
| Benefits | Runtime | Samba ↔ SSHFS access | Protocol bridge functionality |
| Security trade-off | System-wide | Relaxed isolation | Required for multi-service architecture |
Sources: Dockerfile:23-24, README.md:49-53
Common Issues
Mount Fails with "option allow_other only allowed" Error
Symptom: SSHFS mount command fails when using -o allow_other
Cause: The user_allow_other line is missing from /etc/fuse.conf
Solution: Rebuild the Docker image, ensuring Dockerfile24 is present and executed
Samba Cannot Access Mounted Files
Symptom: Samba share appears empty or shows permission denied errors
Cause: SSHFS mount was performed without -o allow_other flag
Solution: Remount using the complete command from README.md49
Sources: README.md:49-53, Dockerfile:23-24
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
SSHFS Mount Options
Relevant source files
Purpose and Scope
This page documents the critical mount options required when mounting remote filesystems via SSHFS inside the docker-sshfs container. These options are essential for enabling Samba to access the mounted filesystem and for providing write permissions. Without these specific options, the remote filesystem will either be inaccessible to the Samba service or will be read-only from the macOS client.
For information about the Samba configuration that consumes the mounted filesystem, see Samba Configuration (smb.conf)). For details about the FUSE configuration that enables cross-user access, see FUSE Configuration.
Required Mount Options
The SSHFS mount command must include three critical options to function correctly with the Samba integration:
| Option | Value | Purpose | Consequence if Omitted |
|---|---|---|---|
allow_other | (flag) | Permits non-owner users (including Samba) to access mounted files | Samba cannot read the mounted filesystem; mount is unusable |
uid | 1000 | Sets the user ID for all files in the mount | Files may have incorrect ownership; write operations may fail |
gid | 1000 | Sets the group ID for all files in the mount | Write access will be read-only from macOS |
Sources: README.md:49-53
Complete Mount Command
The full SSHFS mount command as executed inside the container:
Where:
user@host:pathis the remote SSH endpoint (user, host, and remote directory path)/samba-shareis the local mount point inside the container
Note: This command mounts directly to /samba-share (not to /remote), making the remote filesystem immediately available to Samba. The /remote directory exists for alternative mounting strategies but is linked via symlink to /samba-share/remote Dockerfile30
Sources: README.md:46-50
Option Details
allow_other
-o allow_other
The allow_other option is a FUSE-level permission that allows users other than the mount owner to access the mounted filesystem. This option is absolutely critical for the Samba integration.
graph LR
subgraph "Without allow_other"
SSHFSMount1["SSHFS Mount"]
SSHUser1["sshuser (uid 1000)"]
SambaProc1["smbd process"]
SSHFSMount1 -->|Owned by| SSHUser1
SambaProc1 -.->|Access Denied| SSHFSMount1
end
subgraph "With allow_other"
SSHFSMount2["SSHFS Mount"]
SSHUser2["sshuser (uid 1000)"]
SambaProc2["smbd process"]
SSHFSMount2 -->|Owned by| SSHUser2
SambaProc2 -->|Access Granted| SSHFSMount2
end
Technical Mechanism
Diagram: Impact of allow_other on cross-process access
By default, FUSE mounts are only accessible to the user who created the mount. Since the sshfs command is executed as sshuser, but the smbd daemon runs as a different process (potentially with different effective permissions), the Samba service would be unable to read files from the mount without allow_other.
Prerequisites
The allow_other option requires that /etc/fuse.conf contains the user_allow_other directive. This is configured during the Docker image build:
Sources: README.md52 Dockerfile:23-24
uid=1000
-o uid=1000
graph TB
subgraph "Remote Server"
RemoteFiles["Remote Files\n(various owners)"]
end
subgraph "Container (SSHFS Mount)"
MountedFiles["Mounted Files\n(all uid=1000)"]
end
subgraph "Samba Layer"
SambaConfig["smb.conf\nforce user = sshuser"]
MacClient["macOS Client\n(connects as Guest)"]
end
RemoteFiles -->|SSH Connection| MountedFiles
MountedFiles -.->|uid override| SambaConfig
SambaConfig -->|All operations as sshuser| MacClient
The uid=1000 option forces all files in the mounted filesystem to appear as if they are owned by user ID 1000, which corresponds to the sshuser account created in the container.
User ID Mapping
Diagram: UID mapping through the system layers
The sshuser account is created with UID 1000 during the Docker build:
By forcing all files to appear as owned by UID 1000, the system ensures consistent ownership regardless of the actual ownership on the remote server. This is then coordinated with the Samba configuration's force user = sshuser directive, creating a unified permission model.
Sources: README.md53 Dockerfile:8-9
graph TB
subgraph "Read-Only Configuration"
Mount1["sshfs -o allow_other\n(gid omitted)"]
Files1["Files with remote GID"]
MacClient1["macOS Client"]
Mount1 --> Files1
Files1 -.->|Write Denied| MacClient1
end
subgraph "Write-Enabled Configuration"
Mount2["sshfs -o allow_other,gid=1000"]
Files2["Files with gid=1000"]
MacClient2["macOS Client"]
Mount2 --> Files2
Files2 -->|Write Allowed| MacClient2
end
gid=1000
-o gid=1000
The gid=1000 option forces all files to appear as if they belong to group ID 1000. This option is essential for write access from the macOS client.
Write Access Dependency
Diagram: Impact of gid option on write permissions
Without the gid=1000 option, files retain their original group ownership from the remote server. This can cause permission mismatches when the Samba service (operating as sshuser with GID 1000) attempts to modify files. The result is a read-only mount from the macOS perspective, even though the underlying remote filesystem is writable.
By setting gid=1000, all files appear to belong to the sshuser group, matching the effective group of the Samba service, thus enabling write operations.
Sources: README.md53
Mount Point Architecture
Directory Structure
Diagram: Mount point and directory relationships
The container creates two directories during the build:
- Dockerfile:11-12:
/remotedirectory (currently unused in the recommended workflow) - Dockerfile:14-15:
/samba-sharedirectory (primary mount point)
A symbolic link is created from /samba-share/remote to /remote Dockerfile30 but when mounting directly to /samba-share, this link is effectively shadowed by the mount.
The /samba-share directory has permissions set to 777 Dockerfile:20-21 to ensure maximum compatibility, though this is less critical when using the uid and gid options.
Sources: Dockerfile:11-12 Dockerfile:14-15 Dockerfile:20-21 Dockerfile30
Option Interaction Matrix
The three options must work together to enable full functionality:
| Configuration | allow_other | uid=1000 | gid=1000 | Result |
|---|---|---|---|---|
| ❌ None | No | No | No | Samba cannot access mount |
| ⚠️ Partial | Yes | No | No | Accessible but likely read-only; ownership mismatches |
| ⚠️ Partial | Yes | Yes | No | Accessible but read-only from macOS |
| ⚠️ Partial | Yes | No | Yes | Accessible; UID mismatches may cause issues |
| ✅ Complete | Yes | Yes | Yes | Full read/write access from macOS |
Sources: README.md:52-53
Command Execution Context
The SSHFS mount command is executed inside the running container via docker exec:
Diagram: Mount command execution sequence
The command runs in an interactive shell (bash) within the container, allowing the user to provide SSH authentication credentials when prompted by the remote server.
Sources: README.md:40-50
Additional SSHFS Options
While allow_other, uid=1000, and gid=1000 are required for the Samba integration, SSHFS supports many additional options that can be appended to the -o flag:
Common Optional Settings
| Option | Purpose | Example |
|---|---|---|
StrictHostKeyChecking=no | Skip SSH host key verification | -o StrictHostKeyChecking=no |
IdentityFile=/path/to/key | Use specific SSH key | -o IdentityFile=/root/.ssh/id_rsa |
port=2222 | Use non-standard SSH port | -o port=2222 |
reconnect | Automatically reconnect on connection loss | -o reconnect |
compression=yes | Enable SSH compression | -o compression=yes |
Combined Example
These additional options do not affect the Samba integration but may improve reliability or performance depending on the remote connection characteristics.
Sources: README.md49
Verification
To verify that the mount options are correctly applied, inspect the mount inside the container:
Expected output should show:
user@host:path on /samba-share type fuse.sshfs (rw,nosuid,nodev,relatime,user_id=1000,group_id=1000,allow_other)
Key indicators:
user_id=1000confirms theuidoptiongroup_id=1000confirms thegidoptionallow_otherconfirms cross-user access is enabled
Sources: Implicit from system behavior
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Troubleshooting
Relevant source files
This page provides diagnostic procedures and solutions for common issues encountered when using sshfs-mac-docker. It covers platform-specific problems, connection failures, mount/unmount errors, and permission issues. For detailed configuration reference, see Configuration Reference. For basic usage instructions, see Getting Started.
Common Issues Overview
The following decision tree maps symptoms to their most likely causes and solutions:
Sources : README.md9 README.md:57-85 README.md:48-53
graph TD
Start["Issue Encountered"]
Start --> BuildRun{"Container\nbuild or run\nissue?"}
Start --> Connect{"Cannot\nconnect from\nmacOS?"}
Start --> Mount{"SSHFS mount\nissue?"}
Start --> Unmount{"Cannot\nunmount?"}
BuildRun --> BuildFail["Build fails"]
BuildRun --> RunFail["Run fails"]
BuildFail --> CheckDocker["Check Docker/OrbStack\ninstallation"]
RunFail --> CheckPrivileged["Missing --privileged flag?"]
RunFail --> PortConflict["Ports 139/445 in use?"]
Connect --> LocalhostFail["Using localhost?"]
Connect --> IPWrong["Wrong container IP?"]
Connect --> GuestFail["Guest auth fails?"]
LocalhostFail --> UseContainerIP["Use container IP instead\ndocker inspect --format"]
IPWrong --> InspectContainer["Run docker inspect\nto get correct IP"]
GuestFail --> CheckSmb["Check smb.conf\nguest ok = yes"]
Mount --> SSHFail["SSH auth fails?"]
Mount --> PermFail["Read-only mount?"]
Mount --> NoAccess["Samba can't access?"]
SSHFail --> SSHCreds["Check SSH credentials\nand key-based auth"]
PermFail --> AddUID["Add uid=1000,gid=1000\nto sshfs options"]
NoAccess --> AddAllowOther["Add allow_other\nto sshfs options"]
Unmount --> BusyError["Device or resource\nbusy error?"]
BusyError --> UnmountFinder["Unmount from macOS Finder first\nthen fusermount -u"]
style Start fill:#f9f9f9
style BusyError fill:#ffe6e6
style LocalhostFail fill:#ffe6e6
style PermFail fill:#ffe6e6
Platform-Specific Issues
OrbStack vs Docker Desktop
The system has different behavior depending on the Docker runtime:
| Platform | Status | Networking | Notes |
|---|---|---|---|
| OrbStack | ✅ Recommended | Works out-of-box | No network modifications needed |
| Docker Desktop | ⚠️ Requires modification | Needs routing changes | Default networking incompatible |
| Docker Engine (Linux) | ✅ Compatible | Standard Docker network | Native container networking |
Sources : README.md9
Docker Desktop Network Configuration
When using Docker Desktop on macOS, the default network configuration prevents SMB connections from macOS host to containers. This is because Docker Desktop uses a VM-based networking model that doesn't properly bridge SMB/CIFS ports.
Problem : Connecting to smb://172.17.0.2 (container IP) fails with connection timeout.
Solution Options :
-
Switch to OrbStack (recommended):
-
Modify Docker Desktop networking (advanced):
- Requires changes to Docker Desktop's VM network routing
- Not covered in this documentation due to complexity
- Consider OrbStack instead
Diagnostic Command :
Sources : README.md9
Connection Problems
Localhost Limitation
The most common connection issue stems from attempting to use localhost or 127.0.0.1 to connect to the Samba share from macOS Finder.
Symptom : Connection to smb://localhost or smb://127.0.0.1 fails even though ports are forwarded.
Root Cause : While ports 139 and 445 are forwarded to localhost in the docker run command README.md31 macOS's SMB client doesn't properly recognize these forwarded ports. The connection must use the container's internal Docker network IP.
Solution :
sequenceDiagram
participant User
participant Terminal
participant Docker
participant Finder
User->>Terminal: docker inspect --format '{{ .NetworkSettings.IPAddress }}' docker-sshfs
Terminal->>Docker: Inspect container network
Docker-->>Terminal: 172.17.0.2
Terminal-->>User: Display IP
User->>Finder: Connect to Server
User->>Finder: smb://172.17.0.2
rect rgb(240, 240, 240)
Note over User,Finder: ❌ DO NOT use smb://localhost\n❌ DO NOT use smb://127.0.0.1\n✅ DO use container IP
end
Finder->>Docker: SMB connection to container IP
Docker-->>Finder: Connection successful
Correct Workflow :
-
Get container IP:
-
Use the returned IP (e.g.,
172.17.0.2) in Finder:smb://172.17.0.2 -
Connect as Guest when prompted
Sources : README.md:57-68
Container IP Discovery
If the container IP is not showing or returns empty:
| Issue | Cause | Solution |
|---|---|---|
| Empty output from inspect | Container not running | docker ps to verify, then docker start docker-sshfs |
| Container exists but no network | Network mode issue | Ensure container not started with --network=host |
| IP changes after restart | Docker DHCP reassignment | Use docker inspect each time after container restart |
Dynamic IP Detection Script :
Sources : README.md:57-61
Guest Authentication Issues
The Samba share is configured for guest access in smb.conf
Expected Behavior : When connecting via Finder, select "Connect as Guest" option.
Common Problems :
-
"Guest" option not appearing :
- Verify
smb.confcontainsguest ok = yessmb.conf - Container may need rebuild if configuration changed
- Verify
-
Guest connection rejected :
-
Check that
smbdprocess is running inside container: -
Verify ports are exposed:
-
-
Prompted for username/password :
- Still use Guest option (ignore prompt)
- Or use: username=
sshuser, password=sshpassDockerfile9
Sources : README.md67 Dockerfile9
Mount and Unmount Issues
Device or Resource Busy Error
This is the most common unmounting issue.
Symptom :
Root Cause : macOS Finder still has an active SMB connection to the Samba share, which is serving files from /samba-share. The symbolic link at /samba-share/remote points to /remote (the SSHFS mount point) Dockerfile30 While the SMB connection is active, the FUSE filesystem cannot be unmounted.
Solution Sequence :
-
Unmount from macOS Finder :
- Open Finder
- Locate the mounted share in sidebar or "Network" section
- Click eject button or right-click → "Eject"
-
Unmount SSHFS inside container :
Alternative (Force Method) :
Sources : README.md:72-85 Dockerfile30
Write Permission Issues
Symptom : Files appear in Finder but are read-only, or write operations fail silently.
Root Cause : SSHFS mount missing uid=1000,gid=1000 options README.md53
Diagnostic :
Correct Mount Command :
Why These Options Matter :
| Option | Purpose | Without It |
|---|---|---|
uid=1000 | Sets file owner to sshuser (UID 1000) Dockerfile9 | Files owned by root, Samba can't write |
gid=1000 | Sets file group to sshuser (GID 1000) | Group permissions fail |
allow_other | Allows other users to access mount README.md52 | Samba process can't see mounted files |
Verification After Remounting :
Sources : README.md:48-53 Dockerfile9
Samba Cannot Access SSHFS Mount
Symptom : Samba share appears empty in Finder even after SSHFS mount succeeds.
Root Cause : Missing allow_other option in SSHFS mount, and/or user_allow_other not enabled in /etc/fuse.conf.
The Permission Chain :
Configuration Check :
-
Verify
/etc/fuse.confcontainsuser_allow_otherDockerfile24: -
Verify mount command includes
allow_otherREADME.md49:
Fix :
Sources : README.md:48-52 Dockerfile24
SSHFS Authentication Problems
SSH Connection Failures
Symptom : sshfs command hangs or fails with authentication error.
Common Causes :
| Error Message | Cause | Solution |
|---|---|---|
Permission denied (publickey) | SSH key not available | Use password auth or mount SSH key into container |
Connection refused | Remote SSH port blocked | Verify firewall rules, try different port |
Host key verification failed | Unknown host key | Accept host key or disable strict checking |
SSH Key-Based Authentication :
Password Authentication :
Debug SSH Connection :
Sources : README.md:46-49
Container Runtime Issues
Privileged Mode Requirement
Symptom : Container starts but SSHFS mount fails with "Operation not permitted" or "fuse: device not found".
Root Cause : Missing --privileged flag when starting container README.md31
Why Privileged Is Required : FUSE (Filesystem in Userspace) requires access to /dev/fuse device and capability to perform mount operations. Standard containers lack these permissions.
Correct Run Command :
Verification :
Sources : README.md31
Port Conflicts
Symptom : Container fails to start with error about port binding:
Error starting userland proxy: listen tcp4 127.0.0.1:445: bind: address already in use
Cause : Ports 139 or 445 already in use on macOS host.
Check What's Using Ports :
Solutions :
-
Stop conflicting service :
-
Use different host ports (requires client-side port specification):
Sources : README.md31 Dockerfile27
Diagnostic Commands Reference
Container Status Checks
Filesystem and Mount Checks
Network Diagnostics
Configuration Verification
Sources : README.md:57-85 Dockerfile:1-34
Recovery Procedures
Complete Reset
If issues persist and the system is in an unknown state:
Log Collection for Bug Reports
Sources : README.md:1-90 Dockerfile:1-34
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Platform-Specific Issues
Relevant source files
Purpose and Scope
This page documents platform-specific differences between Docker runtime environments on macOS, focusing on networking incompatibilities that affect SMB connectivity to the docker-sshfs container. The primary distinction is between OrbStack and Docker Desktop, which handle network routing differently and require different configuration approaches.
For connection troubleshooting steps, see Connection Problems. For general network architecture, see Network Architecture.
OrbStack vs Docker Desktop
The sshfs-mac-docker system exhibits significantly different behavior depending on the Docker runtime environment used on macOS. This is explicitly documented in README.md9
Recommended Configuration: OrbStack
OrbStack provides native networking that allows SMB connections to work without additional configuration. When using OrbStack:
- Container IP addresses are directly accessible from macOS
- Port forwarding to
127.0.0.1functions as expected - No network routing modifications required
- SMB protocol operates normally through Docker's network bridge
Sources : README.md9
Problematic Configuration: Docker Desktop
Docker Desktop requires network routing modifications to function with this system. The default Docker Desktop networking on macOS creates isolation that prevents SMB connections from functioning properly with the standard port forwarding configuration.
docker run --privileged --name docker-sshfs -p 127.0.0.1:139:139 -p 127.0.0.1:445:445 docker-sshfs
This command in README.md31 forwards ports 139 and 445 to 127.0.0.1, but Docker Desktop's network stack does not allow macOS SMB clients to connect through this forwarding.
Sources : README.md9 README.md31
Network Routing Comparison
OrbStack Network Model
Diagram 1: OrbStack Network Path
OrbStack provides a transparent network bridge that allows direct container IP access from macOS. The SMB client can connect to the container's IP address without traversing the localhost port forwarding.
Sources : README.md9 README.md:57-61
Docker Desktop Network Model
Diagram 2: Docker Desktop Network Path with Isolation
Docker Desktop introduces a VM-based isolation layer that interferes with SMB protocol handling. The localhost port forwarding does not function for SMB connections, and direct container IP access requires network routing modifications.
Sources : README.md9 README.md57
Localhost Connection Limitation
A critical platform quirk affects both OrbStack and Docker Desktop: the Finder SMB client cannot connect to smb://localhost or smb://127.0.0.1, even when ports 139 and 445 are correctly forwarded.
The Problem
The README.md31 command maps container ports to 127.0.0.1:
However, README.md57 explicitly notes:
Find Docker IP of container (localhost doesn't seem to work for this)
Root Cause
This limitation stems from macOS's SMB client implementation, which treats localhost SMB connections differently than network-based SMB connections. The SMB protocol stack on macOS expects to connect to network interfaces rather than loopback interfaces for standard file sharing operations.
Required Workaround
Users must obtain the container's internal Docker IP address and connect directly to it:
-
Discover container IP using README.md60:
-
Connect to
smb://<container-ip>in Finder README.md65
Sources : README.md31 README.md:57-65
Container IP Discovery
Command-Line Method
The standard approach uses docker inspect with Go template formatting:
This queries the .NetworkSettings.IPAddress field from the container's configuration and returns the dynamically assigned IP address (typically in the 172.17.0.x range).
File Reference : README.md60
IP Address Characteristics
| Property | Value |
|---|---|
| Network | Docker bridge network (default) |
| Range | 172.17.0.0/16 (typical) |
| Assignment | Dynamic (changes on container restart) |
| Accessibility | OrbStack: Direct; Docker Desktop: Requires routing |
| Persistence | Not persistent across container recreation |
Sources : README.md60
Platform Decision Tree
Diagram 3: Platform-Specific Connection Decision Tree
This flowchart illustrates the critical decision points when setting up sshfs-mac-docker on different macOS Docker runtimes and highlights the localhost connection pitfall.
Sources : README.md9 README.md31 README.md:57-61
Platform Compatibility Matrix
| Feature | OrbStack | Docker Desktop | Notes |
|---|---|---|---|
| Port Forwarding (139/445) | ✓ Works | ⚠ Limited | Both bind to 127.0.0.1 per README.md31 |
| Container IP Access | ✓ Direct | ⚠ Needs Routes | OrbStack provides native bridge |
| localhost SMB Connection | ✗ Not Supported | ✗ Not Supported | Platform-wide limitation README.md57 |
| Network Modifications | Not Required | Required | Docker Desktop needs routing config README.md9 |
| Connection Method | smb://container-ip | smb://container-ip (with routes) | Must use README.md60 to find IP |
| Privileged Container | Required | Required | --privileged flag in README.md31 |
Sources : README.md9 README.md31 README.md:57-61
Identifying Your Platform
Determining Docker Runtime
OrbStack Output :
Operating System: OrbStack
Docker Desktop Output :
Operating System: Docker Desktop
Testing Connectivity
After starting the container with README.md31 test connectivity by obtaining the IP with README.md60 and attempting connection via Finder README.md:63-67
If connection fails with OrbStack, verify:
- Container is running:
docker ps | grep docker-sshfs - Ports are bound:
docker port docker-sshfs smbdprocess is running:docker exec docker-sshfs ps aux | grep smbd
If connection fails with Docker Desktop, network routing must be configured (exact steps depend on Docker Desktop version and macOS configuration).
Sources : README.md31 README.md60 README.md:63-67
Network Routing Modifications for Docker Desktop
While Docker Desktop is not the recommended platform README.md9 users who must use it need to modify network routing to allow SMB connections to reach the container's IP address.
Understanding the Routing Problem
Docker Desktop runs containers inside a lightweight VM, creating an additional network layer between macOS and the container. The default routing table does not include paths for direct container IP access from macOS networking services like the Finder SMB client.
Required Configuration Approach
Network routing modifications typically involve:
- Identifying the Docker bridge network subnet (usually
172.17.0.0/16) - Adding static routes from macOS to the Docker VM's network interface
- Ensuring route persistence across Docker Desktop restarts
The exact commands depend on Docker Desktop's current networking configuration and may change between versions. This complexity is why OrbStack is the recommended platform README.md9
Alternative: Docker Desktop Network Driver
Some users report success by configuring Docker Desktop to use alternative network drivers, though this is not officially documented by the sshfs-mac-docker project.
Sources : README.md9
Verification Steps
After Container Startup
Execute these checks after running README.md31:
- Verify container IP assignment :
Expected: Non-empty IP in 172.17.0.x range
- Verify port bindings :
Expected output:
139/tcp -> 127.0.0.1:139
445/tcp -> 127.0.0.1:445
-
Test network connectivity :
- OrbStack: Should succeed
- Docker Desktop: May fail without routing modifications
Sources : README.md31 README.md60
Summary of Platform Differences
OrbStack Advantages
- Zero network configuration required
- Direct container IP accessibility
- Simpler troubleshooting path
- Officially recommended by project README.md9
Docker Desktop Limitations
- Requires network routing modifications
- More complex networking stack
- Additional troubleshooting steps needed
- Not officially supported without modifications README.md9
Universal Limitation
The localhost SMB connection restriction affects both platforms equally. All users must use the container IP obtained via README.md60 and connect to smb://container-ip in Finder README.md65 not smb://localhost.
Sources : README.md9 README.md57 README.md60 README.md65
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Device or Resource Busy Errors
Relevant source files
Purpose and Scope
This page documents the "Device or resource busy" error that occurs when attempting to unmount SSHFS filesystems in the docker-sshfs container. This error manifests when the fusermount -u command fails due to active connections from macOS Finder to the Samba share. This page covers the root cause, resolution steps, and prevention strategies.
For general unmounting procedures and container cleanup, see Unmounting and Cleanup. For issues establishing the initial connection, see Connection Problems.
Sources: README.md:72-86
Error Description
When attempting to unmount an SSHFS filesystem using the fusermount command, you may encounter the following error:
This error indicates that the mount point at /samba-share cannot be unmounted because another process or service is actively using the filesystem.
Sources: README.md:79-83
Root Cause Analysis
The "Device or resource busy" error occurs due to the dependency chain between the macOS SMB client, the smbd daemon, and the FUSE mount point. The error reflects the following system state:
| Component | State | Impact |
|---|---|---|
| macOS Finder | Connected to SMB share | Holds open file handles and directory listings |
smbd daemon | Serving /samba-share | Actively reading from the mount point |
| FUSE mount | Mounted at /samba-share | Cannot unmount while smbd has open file descriptors |
fusermount | Attempting unmount | Blocked by kernel due to active usage |
Dependency Chain
The system maintains the following dependency relationships:
Diagram: Dependency Chain Preventing Unmount
The error occurs because the Linux kernel's FUSE implementation prevents unmounting a filesystem that has active file descriptors or directory handles open. The smbd process maintains these handles as long as the macOS Finder client is connected to the SMB share.
Sources: README.md:79-86
System State During Error
When the unmount fails, the system is in the following state:
Diagram: System State Machine Showing Error Condition
The error occurs when attempting to transition from the macOSConnected state directly to the unmounted state without first disconnecting the macOS Finder client.
Sources: README.md:72-86
Resolution Procedure
Step 1: Disconnect macOS Finder from SMB Share
Before unmounting the SSHFS filesystem, you must first disconnect the macOS Finder client from the Samba share.
Method 1: Eject from Finder
- Open macOS Finder
- Locate the mounted network share in the sidebar under "Locations" or "Network"
- Click the eject icon next to the share name
Method 2: Unmount from Terminal
Step 2: Verify Samba Connections Closed
After disconnecting from Finder, verify that smbd no longer has active connections:
Step 3: Unmount SSHFS Filesystem
Once the macOS client is disconnected, the unmount command should succeed:
If successful, no output is displayed and the command returns exit code 0.
Sources: README.md:72-86
sequenceDiagram
participant User
participant Finder as macOS Finder
participant smbd as smbd Daemon
participant FUSE as FUSE Layer
participant MountPoint as /samba-share
rect rgb(255, 240, 240)
Note over User,MountPoint: Failed Unmount Sequence
User->>FUSE: fusermount -u /samba-share
FUSE->>MountPoint: Check for active handles
MountPoint-->>FUSE: smbd has open file descriptors
FUSE-->>User: Error: Device or resource busy
Note over User: Mount still active
end
rect rgb(240, 255, 240)
Note over User,MountPoint: Successful Unmount Sequence
User->>Finder: Eject SMB share
Finder->>smbd: Close SMB connection
smbd->>FUSE: Release file descriptors
FUSE->>MountPoint: All handles closed
User->>FUSE: fusermount -u /samba-share
FUSE->>MountPoint: Check for active handles
MountPoint-->>FUSE: No open handles
FUSE->>MountPoint: Unmount filesystem
MountPoint-->>User: Success (exit code 0)
end
Resolution Sequence Diagram
The following diagram illustrates the correct sequence for unmounting versus the failed sequence:
Diagram: Failed vs Successful Unmount Sequence
The key difference is that the successful sequence ensures all Samba connections are closed before attempting the unmount operation.
Sources: README.md:72-86
Troubleshooting Flowchart
Diagram: Troubleshooting Decision Tree
This flowchart provides a systematic approach to resolving the unmount error.
Sources: README.md:72-86
Prevention Strategies
Strategy 1: Establish Unmount Order Protocol
Always follow this order when disconnecting:
- First: Unmount the SMB share from macOS Finder
- Second: Verify Samba connections are closed (
smbstatus --brief) - Third: Unmount the SSHFS filesystem (
fusermount -u /samba-share)
Strategy 2: Monitor Active Connections
Before attempting unmount, check for active Samba sessions:
Strategy 3: Use Forced Unmount with Caution
If normal unmount fails and you've verified Finder is disconnected, you can attempt a lazy unmount:
The -z flag (lazy unmount) detaches the filesystem immediately but delays cleanup until all references are released. However, this can lead to inconsistent states and should only be used when normal unmount repeatedly fails.
Note: Lazy unmount may result in cached data not being flushed to the remote server. Use only as a last resort.
Sources: README.md:72-86
Alternative Solution: Container Stop
If unmounting proves difficult or if you need to disconnect immediately, stopping the container will force cleanup of all resources:
This approach:
- Terminates the
smbddaemon (running as PID 1 in the container) - Forces disconnection of all SMB clients
- Triggers automatic FUSE filesystem cleanup
- Closes the SSH connection to the remote server
Consequences of Container Stop
| Aspect | Impact |
|---|---|
| macOS Finder connection | Immediately broken; may show "connection failed" error |
| Unsaved data in transit | Potentially lost if not yet written to remote server |
| SSHFS mount state | Automatically cleaned up by container shutdown |
| Container restart | Requires re-running sshfs command to remount |
After stopping the container, you can restart it and remount the remote filesystem fresh:
Sources: README.md:72-86
Technical Details
FUSE Mount Reference Counting
The FUSE kernel module maintains a reference count for each mount point. The count increments when:
- A process opens a file or directory on the mount
- A process holds a file descriptor to the mount
- A process has a current working directory on the mount
The fusermount -u command can only succeed when the reference count reaches zero. The smbd daemon holds references for each active SMB client session.
Relevant File Paths
| Path | Purpose | Referenced In |
|---|---|---|
/samba-share | SSHFS mount point and Samba root | README.md49 README.md76 |
/usr/bin/fusermount | FUSE unmount utility | README.md76 |
/usr/sbin/smbd | Samba daemon binary | Container runtime |
Related SSHFS Mount Options
The mount options specified during the initial sshfs command affect the unmount behavior:
allow_other: Enablessmbd(running assshuser) to access the mount- Without this option, only the mounting user could access files, preventing Samba integration
The allow_other option is critical for the system to function but also means multiple processes can hold references to the mount, increasing the likelihood of busy errors during unmount.
Sources: README.md:49-53 README.md:76-86
Summary
The "Device or resource busy" error when unmounting /samba-share is a normal consequence of the system's architecture where the smbd daemon maintains open file handles to serve macOS Finder clients. The error is resolved by disconnecting the SMB client first, then unmounting the FUSE filesystem. When in doubt, stopping the container provides a forceful but reliable cleanup method.
Sources: README.md:72-86
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Connection Problems
Relevant source files
Purpose and Scope
This page addresses common connection issues when attempting to access the Samba share from macOS Finder. It covers troubleshooting the specific network and authentication configurations required for successful SMB connections to the Docker container.
For platform-specific networking issues with Docker Desktop, see Platform-Specific Issues. For problems unmounting active shares, see Device or Resource Busy Errors.
Connection Problem Symptoms
Users typically encounter connection problems in the following scenarios:
| Symptom | Likely Cause |
|---|---|
"Connection failed" when using smb://localhost | Localhost limitation - must use container IP |
"Connection failed" when using smb://127.0.0.1 | Same localhost limitation |
| Cannot find container IP address | Container not running or incorrect inspection command |
| Authentication dialog appears and rejects credentials | Attempting to use registered user instead of Guest |
| Connection refused on SMB ports | Port forwarding not configured or ports already in use |
| Network timeout when connecting | Docker Desktop networking issue (requires OrbStack) |
Sources : README.md:57-69
The Localhost Limitation
Problem Description
Despite port forwarding configured with -p 127.0.0.1:139:139 -p 127.0.0.1:445:445 in README.md31 macOS Finder's SMB client cannot successfully connect using smb://localhost or smb://127.0.0.1. This is a known limitation of how Docker's port forwarding interacts with macOS's SMB client implementation.
graph TB
subgraph "Attempted Connection (Fails)"
FinderA["macOS Finder"]
LocalhostA["smb://localhost or\nsmb://127.0.0.1"]
PortForwardA["Port Forward\n-p 127.0.0.1:139:139\n-p 127.0.0.1:445:445"]
FailureA["❌ Connection Refused"]
FinderA --> LocalhostA
LocalhostA --> PortForwardA
PortForwardA --> FailureA
end
subgraph "Working Connection (Required)"
FinderB["macOS Finder"]
ContainerIPB["smb://172.17.0.2\n(container IP)"]
DockerNetB["Docker Bridge Network\ndocker0"]
SmbdB["smbd process\nPorts 139/445"]
SambaShareB["/samba-share"]
FinderB --> ContainerIPB
ContainerIPB --> DockerNetB
DockerNetB --> SmbdB
SmbdB --> SambaShareB
end
subgraph "IP Discovery"
InspectCmd["docker inspect\n--format '{{ .NetworkSettings.IPAddress }}'\ndocker-sshfs"]
OutputIP["172.17.0.2"]
InspectCmd --> OutputIP
end
Network Path Analysis
Diagram : Network path comparison showing why localhost fails and container IP succeeds
The port forwarding defined in Dockerfile27 (EXPOSE 139 445) and mapped in the docker run command README.md31 forwards traffic to 127.0.0.1 on the host, but the SMB protocol requires direct access to the container's IP address within the Docker bridge network.
Sources : README.md31 README.md57 Dockerfile27
IP Address Discovery
Primary Method
The recommended approach uses docker inspect with a format template:
This command queries the container's network configuration and extracts the IPAddress field from the NetworkSettings object. The output is typically in the form 172.17.0.x for default Docker bridge networks.
Sources : README.md60
Alternative Discovery Methods
If the primary method fails or returns an empty string, try these alternatives:
| Method | Command | When to Use |
|---|---|---|
| Full inspect output | docker inspect docker-sshfs | Review complete network configuration |
| Network listing | docker network inspect bridge | Check bridge network configuration |
| Container stats | docker ps with wide output | Verify container is running |
| OrbStack UI | Check OrbStack dashboard | When using OrbStack instead of Docker Desktop |
Container Not Running Issue
If docker inspect returns no output or error:
Diagram : Diagnostic workflow for IP address discovery failures with specific commands
Sources : README.md:59-61 README.md31
Authentication Configuration
Guest Access Requirement
The Samba configuration in smb.conf:3-4 and smb.conf:13-14 enforces guest-only access:
[global]
security = user
map to guest = bad user
[SSHFS Share]
guest ok = yes
guest only = yes
This configuration means:
map to guest = bad user: Any authentication attempt with invalid credentials is mapped to guest accessguest ok = yes: Anonymous guest connections are permittedguest only = yes: All connections are forced to guest mode, regardless of credentials provided
Connection Procedure
When connecting from macOS Finder README.md:63-67:
- Navigate to Finder → Go → Connect to Server
- Enter
smb://172.17.0.2(using discovered container IP) - When authentication dialog appears, select Connect as Guest
- Do not attempt to enter username/password - the configuration does not support registered users
graph TB
subgraph "Samba Authentication Flow"
FinderReq["Finder SMB Request"]
SmbGlobal["smb.conf [global]\nsecurity = user\nmap to guest = bad user"]
AuthCheck{"Valid\ncredentials?"}
MapGuest["Map to guest"]
ShareConfig["[SSHFS Share]\nguest ok = yes\nguest only = yes\nforce user = sshuser"]
AccessGranted["Access granted as sshuser"]
FinderReq --> SmbGlobal
SmbGlobal --> AuthCheck
AuthCheck -->|No| MapGuest
AuthCheck -->|N/A guest mode| MapGuest
MapGuest --> ShareConfig
ShareConfig --> AccessGranted
end
subgraph "Common Mistakes"
UserPass["Attempting\nusername/password"]
RegisteredUser["Selecting\nregistered user"]
Rejection["❌ Authentication\nRejected"]
UserPass --> Rejection
RegisteredUser --> Rejection
end
Authentication Failure Scenarios
Diagram : Samba authentication flow showing smb.conf directives and their effects
The force user = sshuser directive in smb.conf19 ensures that all file operations, regardless of connection method, execute with sshuser identity (UID 1000 from Dockerfile9).
Sources : smb.conf:3-4 smb.conf:13-14 smb.conf19 README.md67 Dockerfile9
Port Binding Issues
Verifying Port Availability
The container requires exclusive access to ports 139 and 445 on the host. If another service is using these ports, the container will fail to start or Samba will fail to bind.
Check port usage before starting the container:
Common port conflicts:
- Port 445 : Windows File Sharing or other SMB servers on macOS
- Port 139 : NetBIOS services or legacy SMB implementations
Port Forwarding Configuration
The port mapping in README.md31 explicitly binds to 127.0.0.1:
This configuration:
- Forwards host port 139 → container port 139
- Forwards host port 445 → container port 445
- Restricts access to localhost only (security measure)
- Does not work with the SMB client connecting to
127.0.0.1(see localhost limitation above)
To verify port forwarding is active:
Expected output:
139/tcp -> 127.0.0.1:139
445/tcp -> 127.0.0.1:445
Sources : README.md31 README.md34 Dockerfile27
Docker Desktop Network Routing
Known Limitation
README.md9 explicitly warns:
Note: It is highly recommended to use OrbStack. Docker Desktop will not work for this without modifying the network routing.
Why Docker Desktop Fails
Docker Desktop on macOS uses a virtual machine (HyperKit or QEMU) with NAT networking. The SMB protocol's direct IP addressing requirements conflict with Docker Desktop's network isolation model. The container's IP address exists within the VM's network namespace, which is not directly accessible from the macOS host without additional routing configuration.
graph TB
subgraph "OrbStack (Recommended)"
MacOSA["macOS Host"]
OrbStackNet["OrbStack Network\nDirect Integration"]
ContainerA["docker-sshfs Container\nIP: 172.17.0.2"]
SmbdA["smbd\nPorts 139/445"]
MacOSA -->|Direct SMB access| OrbStackNet
OrbStackNet --> ContainerA
ContainerA --> SmbdA
end
subgraph "Docker Desktop (Requires Modification)"
MacOSB["macOS Host"]
HyperKit["HyperKit/QEMU VM"]
NATLayer["NAT Networking"]
ContainerB["docker-sshfs Container\nIP: 172.17.0.2"]
SmbdB["smbd\nPorts 139/445"]
BlockB["❌ Network isolation\nprevents SMB"]
MacOSB --> HyperKit
HyperKit --> NATLayer
NATLayer --> ContainerB
ContainerB --> SmbdB
NATLayer -.->|Blocked| BlockB
end
Network Architecture Comparison
Diagram : Network architecture differences between OrbStack and Docker Desktop affecting SMB connectivity
Mitigation for Docker Desktop
If OrbStack cannot be used, modify Docker Desktop's network routing to bridge the VM network to the host. This is beyond the scope of this project and requires advanced Docker Desktop configuration. Consider using OrbStack as specified in the prerequisites README.md9
Sources : README.md9
Diagnostic Connection Workflow
Complete Troubleshooting Sequence
Diagram : Complete diagnostic workflow with specific commands for each step
Sources : README.md:28-69
Connection Checklist
Use this checklist to systematically verify connection prerequisites:
Pre-Connection Verification
| Step | Command/Action | Expected Result |
|---|---|---|
| 1. Container running | `docker ps | grep docker-sshfs` |
| 2. Samba process active | docker exec docker-sshfs pgrep smbd | Returns process ID |
| 3. SSHFS mounted | `docker exec docker-sshfs mount | grep sshfs` |
| 4. Get container IP | docker inspect --format '{{ .NetworkSettings.IPAddress }}' docker-sshfs | Returns IP like 172.17.0.x |
| 5. Verify ports | docker port docker-sshfs | Shows 139/tcp and 445/tcp mappings |
Connection Attempt
| Step | Action | Configuration |
|---|---|---|
| 1. Open Finder | Go → Connect to Server | N/A |
| 2. Enter address | smb://172.17.0.2 | Use discovered container IP, not localhost |
| 3. Authenticate | Connect as Guest | Do not use registered user |
| 4. Verify mount | Check Network in Finder sidebar | Share appears as sambaserver |
| 5. Access files | Navigate to remote folder | Contents of SSHFS mount visible |
Sources : README.md:55-69 smb.conf6
Advanced Diagnostics
Testing SMB Connectivity
From macOS terminal, test SMB availability:
Expected output from nc:
Connection to 172.17.0.2 port 445 [tcp/microsoft-ds] succeeded!
Inspecting Samba Logs
Check Samba server logs for connection attempts:
Look for:
- Connection attempts from macOS IP
- Authentication successes/failures
- Share access denials
Verifying Configuration Files
Confirm Samba configuration is correctly loaded:
This command runs the Samba parameter verification tool against smb.conf:1-20 and displays the active configuration, highlighting any syntax errors or warnings.
Sources : smb.conf:1-20 Dockerfile18
Summary of Common Solutions
| Problem | Solution | Reference |
|---|---|---|
Cannot connect to smb://localhost | Use container IP from docker inspect | README.md:57-61 |
| Cannot find container IP | Verify container is running with docker ps | README.md31 |
| Authentication fails | Connect as Guest , not registered user | README.md67 smb.conf:13-14 |
| Connection timeout with Docker Desktop | Switch to OrbStack | README.md9 |
| Port binding fails | Check for conflicts on 139/445 with lsof | README.md31 |
| Container runs but no SMB | Verify smbd process with docker exec docker-sshfs pgrep smbd | Dockerfile33 |
| Files not visible | Ensure SSHFS mounted with allow_other flag | README.md49 |
Sources : README.md9 README.md31 README.md49 README.md:57-67 smb.conf:13-14 Dockerfile33
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Command Reference
Relevant source files
This page provides a comprehensive reference for all commands used to build, run, manage, and interact with the sshfs-mac-docker system. Commands are organized by functional category for quick lookup during system operations.
For detailed explanations of how these commands fit into the overall workflow, see Getting Started. For troubleshooting command-related issues, see Troubleshooting.
Sources: README.md:1-90
Command Workflow Overview
Sources: README.md:19-86
Docker Image Management
Commands for building and managing the docker-sshfs image.
docker build
Builds the Docker image from the Dockerfile in the current directory.
| Component | Value |
|---|---|
| Syntax | docker build -t docker-sshfs . |
| Working Directory | Repository root (contains Dockerfile) |
| Tag Name | docker-sshfs |
| Context | . (current directory) |
| Build Time | ~1-2 minutes (downloads Ubuntu base + packages) |
Example:
What This Builds:
- Ubuntu base image with sshfs and samba packages
- User account
sshuser:sshpass(UID 1000, GID 1000) - Directory structure:
/remote,/samba-sharewith symbolic link - Configuration files:
smb.conf, modifiedfuse.conf
Sources: README.md:19-23
docker rmi
Removes the built image from the local Docker registry.
| Component | Value |
|---|---|
| Syntax | docker rmi docker-sshfs |
| When to Use | After removing all containers using this image |
| Effect | Deletes image, freeing disk space |
Example:
Sources: Referenced in state diagram (Diagram 6 in high-level overview)
Container Lifecycle Management
Commands for starting, stopping, and managing the container instance.
graph LR
Command["docker run"]
subgraph "Required_Flags"
Privileged["--privileged\nFUSE requires privileged mode"]
Name["--name docker-sshfs\nContainer identifier"]
Port139["-p 127.0.0.1:139:139\nNetBIOS port mapping"]
Port445["-p 127.0.0.1:445:445\nSMB port mapping"]
end
subgraph "Result"
Container["Container running\nsmbd in foreground"]
end
Command --> Privileged
Command --> Name
Command --> Port139
Command --> Port445
Privileged --> Container
Name --> Container
Port139 --> Container
Port445 --> Container
docker run
Starts a new container from the docker-sshfs image with required privileges and port mappings.
| Component | Value | Purpose |
|---|---|---|
| Full Command | docker run --privileged --name docker-sshfs -p 127.0.0.1:139:139 -p 127.0.0.1:445:445 docker-sshfs | Start container |
--privileged | Required | FUSE operations need elevated privileges |
--name | docker-sshfs | Container identifier for subsequent commands |
-p 127.0.0.1:139:139 | Port mapping | NetBIOS session service (localhost only) |
-p 127.0.0.1:445:445 | Port mapping | SMB over TCP (localhost only) |
| Process Mode | Foreground | Runs smbd -F as main process; container stops when smbd exits |
Example:
Important Notes:
- Container runs in foreground by default; terminal shows Samba logs
- Use
-dflag for detached mode:docker run -d --privileged ... - Port binding to
127.0.0.1prevents external network access - Container must be named
docker-sshfsor update container name in subsequent commands
Sources: README.md:26-34
docker stop
Stops a running container gracefully.
| Component | Value |
|---|---|
| Syntax | docker stop docker-sshfs |
| Effect | Sends SIGTERM to smbd, unmounts may fail if Finder still connected |
| Timeout | 10 seconds (default), then SIGKILL |
Example:
Sources: Implied by container lifecycle in README.md85
docker start
Restarts a stopped container (preserves SSHFS mounts if not explicitly unmounted).
| Component | Value |
|---|---|
| Syntax | docker start docker-sshfs |
| When to Use | After docker stop, to resume without rebuilding |
| Note | SSHFS mounts are not preserved across container restarts |
Example:
Sources: Container lifecycle management pattern
docker rm
Removes a stopped container permanently.
| Component | Value |
|---|---|
| Syntax | docker rm docker-sshfs |
| Prerequisite | Container must be stopped first |
| Effect | Deletes container and all internal state |
Example:
Sources: Standard Docker lifecycle pattern
Container Inspection
Commands for querying container state and metadata.
docker inspect
Retrieves detailed container metadata, most commonly used to get the container's IP address.
| Component | Value |
|---|---|
| Get IP Address | docker inspect --format '{{ .NetworkSettings.IPAddress }}' docker-sshfs |
| Full JSON Output | docker inspect docker-sshfs |
| Common Use Case | Find container IP for SMB connection from macOS |
Example - Get Container IP:
Output Example:
172.17.0.2
This IP address is used in Finder to connect: smb://172.17.0.2
Sources: README.md:57-61
docker logs
Views container output (Samba logs).
| Component | Value |
|---|---|
| Syntax | docker logs docker-sshfs |
| Follow Mode | docker logs -f docker-sshfs |
| Last N Lines | docker logs --tail 50 docker-sshfs |
Example:
Sources: Standard Docker logging pattern
docker ps
Lists running containers.
| Component | Value |
|---|---|
| Running Containers | docker ps |
| All Containers | docker ps -a |
| Filter by Name | docker ps -f name=docker-sshfs |
Example:
Sources: Standard Docker inspection pattern
SSHFS Operations
Commands executed inside the container to mount and unmount remote filesystems.
Entering the Container
Before executing SSHFS commands, access the container's interactive shell.
| Component | Value |
|---|---|
| Syntax | docker exec -it docker-sshfs bash |
| User | root (default exec user) |
| Working Directory | / (container root) |
Example:
You will see a shell prompt like:
root@a1b2c3d4e5f6:/#
Sources: README.md:38-44
sshfs Mount Command
Mounts a remote filesystem via SSH to /remote inside the container.
| Component | Value | Purpose |
|---|---|---|
| Full Syntax | sshfs -o allow_other,uid=1000,gid=1000 user@host:path /remote | Mount remote filesystem |
-o allow_other | Critical | Without this, Samba cannot access the mount (different user) |
-o uid=1000 | Critical for writes | Sets file ownership to sshuser (UID 1000) |
-o gid=1000 | Critical for writes | Sets group ownership to sshuser (GID 1000) |
user@host:path | Variable | SSH connection string; replace with actual remote endpoint |
/remote | Fixed | Mount point directory (pre-created in container) |
Examples:
Why These Options Matter:
| Option | Without It | With It |
|---|---|---|
allow_other | Samba (running as sshuser) cannot access files | Samba can read/write mounted files |
uid=1000 | Files owned by root or remote UID | Files owned by sshuser, enabling writes |
gid=1000 | Group ownership mismatch | Consistent group ownership |
Sources: README.md:46-53
Additional SSHFS Options
Optional parameters for advanced use cases.
| Option | Purpose | Example |
|---|---|---|
-o port=N | Custom SSH port | -o port=2222 |
-o IdentityFile=/path/to/key | Use specific SSH key | -o IdentityFile=/root/.ssh/id_rsa |
-o reconnect | Auto-reconnect on connection loss | -o reconnect |
-o ServerAliveInterval=15 | Keep connection alive | -o ServerAliveInterval=15 |
-o Compression=yes | Enable SSH compression | -o Compression=yes |
Combined Example:
Sources: Standard SSHFS options (not explicitly in README but commonly used)
fusermount Unmount Command
Unmounts the SSHFS filesystem from /remote.
| Component | Value |
|---|---|
| Syntax | fusermount -u /remote |
| When to Use | Before stopping container or remounting different endpoint |
| Common Error | Device or resource busy (see below) |
Example:
Handling "Device or resource busy" Error:
Cause: macOS Finder still has an active SMB connection to the share.
Solution:
- In macOS Finder, eject the network share (right-click → Eject)
- Then retry:
fusermount -u /remote - Alternative:
docker stop docker-sshfs(forces unmount)
Sources: README.md:72-85
macOS Client Operations
Commands and procedures executed on the macOS host to connect to the Samba share.
Discovering Container IP
Since localhost does not work for SMB connections, the container's internal Docker IP must be used.
Command:
Expected Output:
172.17.0.2
Sources: README.md:57-61
Connecting from Finder
Procedure:
-
In macOS Finder, press
Cmd+Kor navigate toGo → Connect to Server -
Enter the connection string using the container IP:
smb://172.17.0.2
Replace 172.17.0.2 with the actual container IP from docker inspect
-
Click Connect
-
Authentication prompt:
- Select Guest (do not use username/password)
- Click Connect
-
The share appears in Finder under Network → IP address
-
Inside the share, navigate to the
remotedirectory to access mounted files
Sources: README.md:55-69
Connection String Format
| Component | Format | Example |
|---|---|---|
| Protocol | smb:// | Required |
| IP Address | Container IP (not localhost) | 172.17.0.2 |
| Port | Implicit (139/445) | Not specified in URL |
| Full String | smb://<container-ip> | smb://172.17.0.2 |
Why localhost doesn't work: Despite port forwarding to 127.0.0.1, the macOS SMB client requires connection to the container's internal IP address due to how Docker's network stack handles SMB protocol translation.
Sources: README.md57
Disconnecting from macOS
Procedure:
- In Finder, locate the mounted share under Network
- Right-click (or Control-click) on the share
- Select Eject or click the eject button (⏏)
- Share disappears from Finder
Alternative: Drag the share icon to Trash
Effect: Closes SMB connection, allowing safe unmounting with fusermount -u in the container
Sources: README.md85
Command Sequences for Common Tasks
Complete Setup and Mount
Sources: README.md:19-69
Complete Teardown
Sources: README.md:72-85
Remounting Different Remote Endpoint
Sources: Implied workflow from README.md:46-85
State-Based Command Reference
Sources: README.md:19-85
Quick Reference Table
| Task | Command | Location |
|---|---|---|
| Build image | docker build -t docker-sshfs . | Repository root |
| Start container | docker run --privileged --name docker-sshfs -p 127.0.0.1:139:139 -p 127.0.0.1:445:445 docker-sshfs | macOS host |
| Get container IP | docker inspect --format '{{ .NetworkSettings.IPAddress }}' docker-sshfs | macOS host |
| Enter container | docker exec -it docker-sshfs bash | macOS host |
| Mount remote | sshfs -o allow_other,uid=1000,gid=1000 user@host:path /remote | Inside container |
| Unmount remote | fusermount -u /remote | Inside container |
| Connect in Finder | smb://container-ip | macOS Finder |
| Stop container | docker stop docker-sshfs | macOS host |
| View logs | docker logs docker-sshfs | macOS host |
| Remove container | docker rm docker-sshfs | macOS host |
| Remove image | docker rmi docker-sshfs | macOS host |
Sources: README.md:19-90
This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Technical Specifications
Relevant source files
This page provides reference information for all technical specifications used in sshfs-mac-docker, including port numbers, file paths, protocol versions, user IDs, permissions, and configuration parameters. This is intended as a quick reference for developers and system administrators who need precise technical details.
For detailed explanations of how these specifications are applied in practice, see Configuration Reference. For architectural context explaining why these specifications were chosen, see Architecture.
Network Specifications
Port Configuration
The system uses two standard SMB/CIFS ports that are exposed from the container to the macOS host:
| Port | Protocol | Purpose | Binding | Exposure |
|---|---|---|---|---|
| 139 | TCP | NetBIOS Session Service | 127.0.0.1:139 | Localhost only |
| 445 | TCP | SMB over TCP (Direct hosting) | 127.0.0.1:445 | Localhost only |
| 22 | TCP | SSH (outbound from container) | N/A | Remote connection |
The port mappings are configured via Docker run command as specified in README.md31:
-p 127.0.0.1:139:139 -p 127.0.0.1:445:445
Both ports are exposed in the Dockerfile at Dockerfile27:
EXPOSE 139 445
Network Architecture Diagram
Sources: README.md31 README.md34 README.md:57-61 Dockerfile27
Container IP Discovery
The container IP address is dynamically assigned by Docker and must be discovered at runtime using:
This command is documented in README.md60
Network Limitation: The SMB client on macOS cannot connect using localhost or 127.0.0.1 despite the port forwarding. Connections must use the container's internal Docker IP address (e.g., 172.17.0.2), as noted in README.md57
Sources: README.md:57-61
Filesystem Specifications
Directory Structure
The container uses a specific directory hierarchy to integrate SSHFS mounts with Samba shares:
| Path | Type | Permissions | Owner | Purpose |
|---|---|---|---|---|
/remote | Directory | Default (created by mkdir) | root | SSHFS mount point |
/samba-share | Directory | 777 (rwxrwxrwx) | root | Samba share root |
/samba-share/remote | Symbolic Link | N/A | root | Link to /remote |
Sources: Dockerfile12 Dockerfile15 Dockerfile21 Dockerfile30 smb.conf11 README.md49
Permission Specifications
| Component | Permission | Octal | Specification |
|---|---|---|---|
/samba-share directory | rwxrwxrwx | 0777 | Dockerfile21 |
| SSHFS mounted files | Controlled by mount options | N/A | See SSHFS Mount Options |
| Samba share create mask | rwxrwxrwx | 0777 | smb.conf17 |
| Samba share directory mask | rwxrwxrwx | 0777 | smb.conf18 |
The broad permissions on /samba-share are necessary because the Samba service must write to this directory on behalf of guest users, which are force-mapped to sshuser via smb.conf19
Sources: Dockerfile21 smb.conf:17-19
User and Group Specifications
User Account
A single non-root user account is created during image build:
| Parameter | Value | Source |
|---|---|---|
| Username | sshuser | Dockerfile9 |
| Password | sshpass | Dockerfile9 |
| UID | 1000 (default) | Implicitly assigned by useradd -m |
| GID | 1000 (default) | Implicitly assigned by useradd -m |
| Home Directory | /home/sshuser | Created by useradd -m flag |
The user is created via: Dockerfile9
useradd -m sshuser && echo "sshuser:sshpass" | chpasswd
User ID Mapping
Sources: Dockerfile9 smb.conf4 smb.conf19 README.md49
Protocol Specifications
SMB/CIFS Protocol
| Configuration Key | Value | Source | Purpose |
|---|---|---|---|
workgroup | WORKGROUP | smb.conf2 | NetBIOS workgroup name |
security | user | smb.conf3 | Security mode |
map to guest | bad user | smb.conf4 | Map unknown users to guest |
client min protocol | SMB2 | smb.conf7 | Minimum client protocol version |
server min protocol | SMB2 | smb.conf8 | Minimum server protocol version |
server string | Samba Server %v | smb.conf5 | Server identification string |
netbios name | sambaserver | smb.conf6 | NetBIOS server name |
The protocol enforcement prevents fallback to SMB1, which has known security vulnerabilities.
Sources: smb.conf:1-9
SSH Protocol
| Parameter | Value | Notes |
|---|---|---|
| Port | 22 (default) | Standard SSH port |
| Protocol | SSH-2 | Used by sshfs command |
| Authentication | User-specified | Passed via user@host syntax |
| Connection Type | FUSE-based | Filesystem operations over SSH |
The SSH connection is established by the sshfs command as documented in README.md49
Sources: README.md49
SSHFS Mount Specifications
Required Mount Options
The following options must be specified for proper system operation:
| Option | Value | Purpose | Source |
|---|---|---|---|
allow_other | Flag | Allows non-mounting user (Samba) to access mount | README.md52 |
uid | 1000 | Sets file ownership to sshuser | README.md53 |
gid | 1000 | Sets group ownership to sshuser | README.md53 |
Complete mount command syntax from README.md49:
Critical Dependencies:
allow_otherrequiresuser_allow_otherenabled in/etc/fuse.conf(Dockerfile24)uid=1000,gid=1000must match the UID/GID ofsshuserfor write access- Without these options, the mount will be read-only or inaccessible to Samba
Sources: README.md:49-53 Dockerfile24
FUSE Configuration
FUSE (Filesystem in Userspace) requires specific configuration to enable cross-user access:
| Configuration File | Setting | Value | Purpose |
|---|---|---|---|
/etc/fuse.conf | user_allow_other | Enabled | Permits non-root users to use allow_other flag |
This is configured via Dockerfile24:
Without this configuration, the allow_other mount option would be rejected by FUSE.
Sources: Dockerfile24
Container Runtime Specifications
Required Privileges
The container must run with elevated privileges:
| Flag | Value | Purpose | Source |
|---|---|---|---|
--privileged | Required | Enables FUSE filesystem operations | README.md31 |
The privileged mode is necessary because FUSE requires access to /dev/fuse and the ability to perform mount operations, which are restricted in standard containers.
Sources: README.md31
Container Command
The container runs Samba as its primary process:
smbd --foreground --no-process-group --debug-stdout
| Argument | Purpose |
|---|---|
--foreground | Runs smbd in foreground (required for Docker) |
--no-process-group | Prevents process group creation |
--debug-stdout | Sends debug output to stdout |
This is specified in Dockerfile33
Sources: Dockerfile33
Samba Share Specifications
Share Configuration
The share named "SSHFS Share" has the following configuration from smb.conf:10-20:
| Parameter | Value | Purpose |
|---|---|---|
path | /samba-share | Root directory of share |
writable | yes | Allows write operations |
guest ok | yes | Permits guest access |
guest only | yes | Forces guest authentication |
read only | no | Explicitly enables writing |
browseable | yes | Share visible in network browser |
create mask | 0777 | Default permissions for new files |
directory mask | 0777 | Default permissions for new directories |
force user | sshuser | All operations performed as this user |
Sources: smb.conf:10-20
Package Specifications
Installed Packages
The following packages are installed via apt-get in Dockerfile:5-6:
| Package | Purpose | Version |
|---|---|---|
sshfs | SSHFS client for mounting remote filesystems over SSH | Latest from Ubuntu repository |
samba | SMB/CIFS server for file sharing | Latest from Ubuntu repository |
Base Image
| Specification | Value |
|---|---|
| Base Image | ubuntu:latest |
| Source | Dockerfile2 |
Sources: Dockerfile2 Dockerfile:5-6
Command Reference Summary
Docker Commands
| Command | Purpose | Source |
|---|---|---|
docker build -t docker-sshfs . | Build container image | README.md22 |
docker run --privileged --name docker-sshfs -p 127.0.0.1:139:139 -p 127.0.0.1:445:445 docker-sshfs | Start container | README.md31 |
docker exec -it docker-sshfs bash | Access container shell | README.md41 |
docker inspect --format '{{ .NetworkSettings.IPAddress }}' docker-sshfs | Get container IP | README.md60 |
SSHFS Commands
| Command | Purpose | Source |
|---|---|---|
sshfs -o allow_other,uid=1000,gid=1000 user@host:path /remote | Mount remote filesystem | README.md49 |
fusermount -u /samba-share | Unmount filesystem | README.md76 |
Sources: README.md22 README.md31 README.md41 README.md49 README.md60 README.md76
Platform Requirements
Recommended Platform
| Requirement | Specification | Notes |
|---|---|---|
| Platform | OrbStack | Highly recommended for network compatibility |
| Alternative | Docker Desktop | Requires network routing modifications |
| macOS Version | Any version supporting Docker | No macFUSE required |
The recommendation for OrbStack over Docker Desktop is documented in README.md9 due to networking compatibility issues with Docker Desktop's SMB client integration.
Sources: README.md9
Error Messages and Codes
Known Error Conditions
| Error Message | Cause | Resolution |
|---|---|---|
fusermount: failed to unmount /samba-share: Device or resource busy | Samba share still mounted on macOS | Unmount from Finder first, or stop container |
This error is documented in README.md:82-85
Sources: README.md:82-85