Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

GitHub

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

FilePurposeKey Settings
DockerfileContainer image definitionBase: ubuntu:latest
Packages: sshfs, samba
User: sshuser
smb.confSamba server configurationShare path: /samba-share
Guest access enabled
Force user: sshuser
/etc/fuse.confFUSE permissionsuser_allow_other enabled

Sources: Dockerfile:1-34 smb.conf:1-20

Runtime Components

Component Details:

  • smbd process: Samba daemon started via Dockerfile33 as container's main process
  • sshuser account: Non-root user created in Dockerfile9 with hardcoded password
  • /remote directory: SSHFS mount point created in Dockerfile12
  • /samba-share directory: 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:

TechnologyRoleConfiguration
SSHFSRemote filesystem clientMount options in README.md49
Requires allow_other, uid=1000, gid=1000
SambaSMB/CIFS serverConfigured in smb.conf:1-20
Ports 139 and 445
FUSEFilesystem in userspaceEnabled in Dockerfile24
With user_allow_other permission

Critical Integration:

  • FUSE'sallow_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

Sources: README.md22 README.md31 Dockerfile33

Phase 2: Remote Filesystem Mounting

  • Executes SSHFS as sshuser inside container README.md:41-49
  • Mounts remote filesystem at /remote
  • Accessible via symbolic link at /samba-share/remote Dockerfile30

Sources: README.md:41-49 Dockerfile30

Phase 3: macOS Client Connection

Sources: README.md:57-69


Why Samba Instead of Docker Volume Mounts?

Docker's native volume mounting (docker run -v) cannot be used because:

  1. Volume mounts are designed for local filesystems : They map host directories to container paths
  2. SSHFS operates inside the container : The mounted remote filesystem exists only in the container's namespace
  3. 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:

PlatformStatusNotes
OrbStack✅ RecommendedWorks out-of-box with proper networking README.md9
Docker Desktop⚠️ Requires modificationsNetwork routing must be modified README.md9
macOSRequiredTarget 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.1 prevent external network access README.md34
  • Guest authentication : SMB configured for guest-only access smb.conf:13-14
  • Forced user identity : All operations execute as sshuser regardless of SMB client smb.conf19
  • Hardcoded credentials : sshuser:sshpass defined in Dockerfile9

Sources: README.md31 README.md34 smb.conf:13-14 smb.conf19 Dockerfile9


Limitations and Alternatives

Known Limitations:

  • Requires running container with --privileged flag
  • 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


GitHub

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:

ComponentFile/CommandPurposeReference
Build DefinitionDockerfileDefines image with sshfs, samba, sshuser, directoriesDockerfile:1-34
Samba Configurationsmb.confConfigures guest access, forced user, share pathsmb.conf
FUSE Configuration/etc/fuse.confEnables user_allow_other optionDockerfile24
Remote Mount Point/remoteDirectory where SSHFS mounts remote filesystemDockerfile12
Samba Share Directory/samba-shareRoot directory served by Samba with 777 permissionsDockerfile15 Dockerfile21
Symbolic Link/samba-share/remoteLinks Samba share to SSHFS mount pointDockerfile30
Service UsersshuserNon-root user (uid=1000) for SSHFS operationsDockerfile9
Build Commanddocker build -t docker-sshfs .Builds image from DockerfileREADME.md22
Run Commanddocker run --privileged -p 127.0.0.1:139:139 -p 127.0.0.1:445:445Starts container with port forwardingREADME.md31
Mount Commandsshfs -o allow_other,uid=1000,gid=1000 user@host:path /samba-shareMounts remote filesystem via SSHFSREADME.md49
Unmount Commandfusermount -u /samba-shareUnmounts SSHFS filesystemREADME.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.

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

OperationCommandLocation
Build imagedocker build -t docker-sshfs .Host terminal
Start containerdocker run --privileged --name docker-sshfs -p 127.0.0.1:139:139 -p 127.0.0.1:445:445 docker-sshfsHost terminal
Access containerdocker exec -it docker-sshfs bashHost terminal
Mount remotesshfs -o allow_other,uid=1000,gid=1000 user@host:path /samba-shareInside container
Get container IPdocker inspect --format '{{ .NetworkSettings.IPAddress }}' docker-sshfsHost terminal
Connect from Findersmb://[container-ip]Finder → Go → Connect to Server
Unmount filesystemfusermount -u /samba-shareInside container
Stop containerdocker stop docker-sshfsHost terminal

Sources: README.md:22-86


Next Steps

After completing the basic setup outlined on this page:

  1. Review Prerequisites and Platform Requirements for detailed platform considerations, especially regarding OrbStack versus Docker Desktop
  2. Follow the detailed steps in Building the Container through Connecting from macOS
  3. Consult Unmounting and Cleanup when you need to disconnect or reconfigure
  4. If you encounter issues, see Troubleshooting for common problems and solutions
  5. For a deeper understanding of the system architecture, see Architecture

Sources: README.md


GitHub

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:

ComponentPurposeVersion Notes
macOSHost operating systemAny modern version supporting Docker
Docker RuntimeContainer execution environmentOrbStack (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 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:

ComponentWhy It's ProblematicHow sshfs-mac-docker Avoids It
macFUSEThird-party kernel extensionRuns FUSE inside Docker container
Kernel ExtensionsRequires lowering system securityNo host kernel modifications needed
System Integrity Protection (SIP)May require disabling or modificationsHost 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/fuse device
  • 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):

PortProtocolPurpose
139NetBIOS Session ServiceSMB legacy compatibility
445SMB over TCPModern 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:

  1. 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 inspect command shown in README.md60
  2. 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

RequirementStatus Check CommandExpected Result
Docker Runtime Installeddocker --versionVersion information displayed
OrbStack Running (if used)docker infoShows OrbStack as provider
Ports 139/445 Availablelsof -i :139 and lsof -i :445No output (ports not in use)
Privileged Mode Supporteddocker run --rm --privileged alpine ls /dev/fuse/dev/fuse exists
Network Access to Remotessh user@hostSuccessful SSH connection

Minimum System Profile

Sources : README.md:1-90


Next Steps

Once all prerequisites are satisfied:

  1. Verify Docker runtime is operational
  2. Confirm ports 139 and 445 are available
  3. Proceed to Building the Container

For troubleshooting platform-specific issues during setup, see Platform-Specific Issues.

Sources : README.md:1-90


GitHub

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:

PackagePurposeKey Functionality
sshfsSSH filesystem clientMounts remote directories via SSH protocol
sambaSMB/CIFS serverExposes 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 -m flag)

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

PathCreated ByPurposePermissions
/remoteDockerfile12SSHFS mount target directoryDefault (755)
/samba-shareDockerfile15Samba share root directory777 (world-writable)
/samba-share/remoteDockerfile30Symbolic link to /remoteFollows 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 sshuser account
  • The Samba daemon (smbd) runs as a different user
  • Without user_allow_other, smbd cannot 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


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 --foreground mode (does not daemonize)
  • Use --no-process-group to avoid creating a process group
  • Enable --debug-stdout to 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


GitHub

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

FlagPurposeWhy Required
--privilegedGrants container access to host devices, specifically /dev/fuseSSHFS 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-sshfsAssigns a fixed name to the containerEnables consistent container identification for subsequent docker exec, docker inspect, and docker stop commands
-p 127.0.0.1:139:139Maps container port 139 to host port 139, bound to localhost onlyExposes NetBIOS Session Service for SMB connections. The 127.0.0.1 binding prevents external network access
-p 127.0.0.1:445:445Maps container port 445 to host port 445, bound to localhost onlyExposes SMB over TCP for modern SMB protocol versions (SMB2+). The 127.0.0.1 binding provides security isolation
docker-sshfsImage name to instantiateReferences 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:

FlagPurpose
--foregroundKeeps 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-groupPrevents smbd from creating a new process group, ensuring proper signal handling in the container environment
--debug-stdoutSends 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:

ComponentStateDetails
/dev/fuseMountedAvailable due to --privileged flag
/remote directoryCreated, emptyMount point for SSHFS, created during image build
/samba-share directoryCreated, permissions 777Samba root directory with symbolic link to /remote
/samba-share/remoteSymbolic linkLinks to /remote, created during image build
smbd processRunning, foregroundListening on ports 139 and 445
Samba shareAvailableNamed "SSHFS Share", serving /samba-share
SSHFS mountNot mountedRequires 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.


GitHub

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/documents
  • deploy@example.com:/var/www/html
  • admin@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.

OptionPurposeWithout This Option
allow_otherPermits users other than the mount owner to access the filesystemThe smbd service (running as sshuser) cannot read the mounted filesystem, preventing macOS access
uid=1000Sets the user ID for all files in the mounted filesystemFiles may be owned by the wrong user, causing permission errors
gid=1000Sets the group ID for all files in the mounted filesystemFiles 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.

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:

  1. Check mount point contents:

The directory should display the contents of the remote filesystem.

  1. Inspect active mounts:

Should display a line showing the SSHFS mount at /samba-share.

  1. 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 sshfs interactively 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:

  1. The sshfs process runs as the user executing the command inside the container
  2. The smbd service runs as a separate process (as sshuser due to force user configuration)
  3. Without allow_other, FUSE would deny smbd access to the mounted filesystem
  4. With allow_other enabled, smbd can 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)


GitHub

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:

RequirementVerification CommandExpected State
Container runningdocker psdocker-sshfs appears in list
SSHFS mounteddocker exec docker-sshfs ls /remoteRemote files visible
Samba service activedocker exec docker-sshfs pgrep smbdReturns 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

  1. Open Finder on macOS

  2. Access Server Connection Dialog

    • Press ⌘K (Cmd+K), or
    • Navigate to menu bar: Go → Connect to Server
  3. Enter Server Address

    • Input: smb://<container-ip> where <container-ip> is the IP obtained from docker inspect
    • Example: smb://172.17.0.2
    • Click Connect
  4. Select Authentication Method

    • When prompted, select Guest radio button
    • Do not enter username/password
    • Click Connect
  5. Share Selection

    • If multiple shares exist, select SSHFS Share
    • Click OK

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 ParameterValueEffect
securityuserUse user-based authentication model
map to guestbad userMap invalid users to guest account
guest okyesAllow guest access to share
guest onlyyesRestrict share to guest access only
force usersshuserAll 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.1 prevents 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.

  1. Sidebar Access

    • Finder sidebar → Network section
    • Look for container IP (e.g., 172.17.0.2)
    • Click to expand
  2. Share Contents

    • Top level shows SSHFS Share directory
    • Navigate to remote subdirectory
    • Contents are the mounted remote filesystem
  3. Direct Volume Path

    • Accessible at /Volumes/SSHFS Share/remote/
    • Usable in Terminal and CLI tools

Share Mapping

macOS ViewContainer PathRemote 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:

OperationSupportNotes
Read files✓ FullNo restrictions
Write files✓ FullRequires uid=1000,gid=1000 mount option
Create directories✓ FullPermissions: 0777 (configurable)
Delete files/directories✓ FullSubject to remote server permissions
Rename/move✓ FullWithin mounted filesystem
File metadata✓ PartialTimestamps 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>
ComponentDescriptionExample
<container-ip>Docker bridge network IP172.17.0.2
<share-name>Share name from smb.conf10SSHFS 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

IssueLikely CauseSolution
"Connection failed"Container not runningVerify with docker ps
"Connection timed out"Wrong IP addressRe-run docker inspect
"Authentication failed"Not using GuestSelect "Guest" option, not "Registered User"
"Share not found"SSHFS not mountedCheck Mounting Remote Filesystems
Files not writableMissing mount optionsRemount with uid=1000,gid=1000
Share disappearsContainer stopped/restartedReconnect with new IP address

For detailed troubleshooting, see Connection Problems and Platform-Specific Issues.

Sources : README.md:55-70


GitHub

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

MethodSteps
Finder SidebarRight-click the mounted share name → Select "Eject"
Finder MenuFile → Eject [share-name]
Desktop IconRight-click the mounted volume icon → Select "Eject"
Keyboard ShortcutSelect 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

ComponentPurpose
fusermountFUSE filesystem management utility
-uUnmount option
/samba-shareMount 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:

  1. Active SMB connections from macOS Finder
  2. Open file handles from Samba daemon (smbd)
  3. 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

CommandContextDescription
fusermount -u /samba-shareInside containerUnmount SSHFS filesystem
docker exec -it docker-sshfs fusermount -u /samba-shareHost shellUnmount from host without entering container
lsof /samba-shareInside containerList processes using the mount
`mountgrep samba-share`Inside container

Container Lifecycle

CommandDescription
docker stop docker-sshfsGracefully stop container (SIGTERM)
docker start docker-sshfsRestart stopped container
docker restart docker-sshfsStop and start container
docker rm docker-sshfsRemove stopped container
docker rm -f docker-sshfsForce remove (stops if running)

Image Management

CommandDescription
`docker imagesgrep docker-sshfs`
docker rmi docker-sshfsRemove image (no containers must exist)
docker rmi -f docker-sshfsForce 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

  1. Save all open files in macOS applications accessing the remote filesystem
  2. Close applications that may have file handles open on the remote share
  3. Eject from Finder using any of the methods described above
  4. Wait 2-3 seconds for SMB connections to fully terminate
  5. Execute fusermount inside the container
  6. Verify unmount using mount | grep samba-share (should return nothing)
  7. Stop container if no longer needed

Avoiding Common Mistakes

MistakeConsequenceSolution
Unmounting SSHFS before ejecting from Finder"Device or resource busy" errorAlways disconnect macOS first
Stopping container with active Finder connectionFinder shows "Connection failed" errorsEject from Finder before stopping
Force-stopping container frequentlyPotential data loss on remote serverUse graceful shutdown procedures
Not verifying unmount statusConfusion about system stateCheck 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 smbd daemon

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


GitHub

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

LayerComponentsResponsibility
Layer 1: macOS HostFinder, Docker CLI, User TerminalClient interface and container orchestration
Layer 2: Docker Containersmbd, sshfs, filesystem integrationProtocol translation and filesystem bridging
Layer 3: Remote InfrastructureSSH server with target filesystemData 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:

Directory Structure:

User Context:

  • sshuser account 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:

README.md49

OptionPurposeConsequence if Omitted
allow_otherPermits access by users other than mount ownerSamba cannot read mounted files
uid=1000Sets file owner to match sshuser UIDWrite operations fail (read-only)
gid=1000Sets file group to match sshuser GIDWrite 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:

  1. SSHFS mount must include uid=1000,gid=1000 README.md53
  2. Samba must use force user = sshuser smb.conf19
  3. Directory permissions must be 777 Dockerfile21

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:

README.md31

FlagPurposeSecurity Implication
--privilegedAllows FUSE operationsContainer has elevated privileges
-p 127.0.0.1:139:139Forward NetBIOS port to localhost onlyNot accessible from external network
-p 127.0.0.1:445:445Forward SMB port to localhost onlyNot accessible from external network

Exposed Ports in Dockerfile:

Dockerfile27

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:

README.md60

Connection String:

smb://172.17.0.2  # Example IP, varies per container

README.md65

Network Topology Table

Network ElementAddress/PortAccess ScopeConfiguration Source
Samba NetBIOSContainer:139Container internalDockerfile27
Samba SMBContainer:445Container internalDockerfile27
Host NetBIOS127.0.0.1:139Localhost onlyREADME.md31
Host SMB127.0.0.1:445Localhost onlyREADME.md31
Container IP172.17.0.0/16Docker bridge networkDocker runtime
Remote SSHRemote:22Internet/VPNUser 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:

smb.conf:3-14

SettingValueEffect
securityuserRequires user-level authentication
map to guestbad userInvalid usernames map to guest account
guest okyesAllow guest connections
guest onlyyesForce 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:

smb.conf19

Purpose: All file operations, regardless of the connecting user's identity, execute as sshuser (UID 1000). This ensures:

  1. Consistent ownership of created files
  2. Write permissions match SSHFS mount options
  3. No privilege escalation beyond container boundaries

User Creation:

Dockerfile9

Privileged Container Requirement

Flag:

README.md31

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:

smb.conf:7-8

Rationale: SMB1 has known vulnerabilities. Enforcing SMB2 minimum ensures secure communication between Finder and the container.

Filesystem Permission Model

Samba Share Permissions:

Dockerfile21

PermissionUserGroupOthers
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:

README.md49

Forces all files in the remote mount to appear owned by UID 1000 (sshuser), enabling write operations.

Security Boundaries Summary

BoundaryProtectionThreat Model
Docker isolationContainer → HostLimits impact of container compromise
Localhost bindingExternal → ContainerPrevents network exposure
Forced user identityClient → FilesystemPrevents privilege escalation via SMB
SMB2 enforcementClient → ServerPrevents SMB1 vulnerabilities
SSH encryptionContainer → RemoteProtects 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


GitHub

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:

TierEnvironmentPrimary RoleKey Technologies
Tier 1macOS HostUser interface and container managementFinder, Docker CLI
Tier 2Docker ContainerProtocol bridging and filesystem operationsSSHFS, Samba, FUSE
Tier 3Remote ServerData storage and SSH authenticationSSH 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

  1. Container Lifecycle Management : Building and running the Docker container via docker build and docker run commands
  2. User Interaction : Providing the terminal interface for executing commands within the container via docker exec
  3. 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 PhaseLinesPurposeKey Artifacts
Base Image2Provides Ubuntu Linux environmentubuntu:latest
Package Installation4-6Installs SSHFS and Sambasshfs, samba packages
User Creation8-9Creates non-root user for SSHFSsshuser account
Directory Structure11-15Establishes mount points/remote, /samba-share
Configuration17-24Sets up Samba and FUSE configssmb.conf, fuse.conf
Integration29-30Links SSHFS mount to Samba shareSymbolic link
Service Launch32-33Starts Samba daemonsmbd 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:

  1. Remote SSH Server : The target system that the user specifies as user@host:path in the sshfs command README.md49
  2. Remote Filesystem : The actual files and directories being accessed
  3. 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:

  1. Installing a kernel extension (kext)
  2. Allowing system extensions in Security & Privacy settings
  3. Potential compatibility issues with macOS updates
  4. 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

AspectTraditional SSHFSsshfs-mac-docker
FUSE LocationmacOS kernel spaceDocker container (Linux userspace)
Requires macFUSEYesNo
Kernel ExtensionsRequired on macOSOnly within container (isolated)
macOS System ModificationsYesNone
SSH ClientRuns on macOSRuns in container
File Access ProtocolDirect FUSESMB (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:

ComponentTierFile/PathCreated ByPurpose
Docker CLI1N/AmacOSContainer management
Finder1N/AmacOSSMB client
ubuntu:latest2N/ADockerfile:2Base image
sshfs package2N/ADockerfile:6SSHFS client
samba package2N/ADockerfile:6SMB server
sshuser account2N/ADockerfile:9Non-root user
/remote directory2/remoteDockerfile:12SSHFS mount point
/samba-share directory2/samba-shareDockerfile:15Samba share root
smb.conf2/etc/samba/smb.confDockerfile:18Samba configuration
fuse.conf2/etc/fuse.confDockerfile:24FUSE permissions
Symbolic link2/samba-share/remoteDockerfile:30Integration point
smbd process2N/ADockerfile:33SMB daemon
Remote SSH Server3user@hostExternalSSH daemon
Remote Filesystem3user@host:pathExternalData 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:

  1. Isolating FUSE operations within a Docker container (Tier 2) where Linux provides native FUSE support
  2. Exposing the remote filesystem through SMB, which macOS (Tier 1) natively supports
  3. 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


GitHub

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:

README.md49

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 OptionPurposeWithout It
allow_otherAllows users other than the mounting user to access the mountSamba service cannot access files mounted by sshuser
uid=1000Sets file ownership to UID 1000 (sshuser)Write operations fail; filesystem appears read-only
gid=1000Sets 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.

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:

  1. /remote directory created Dockerfile12
  2. /samba-share directory created Dockerfile15
  3. /samba-share permissions set to 777 Dockerfile21
  4. Symbolic link /samba-share/remote → /remote created 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

ConfigurationPurposeImpact on Protocol Translation
path = /samba-shareSets root directory for SMB shareExposes directory containing symbolic link to SSHFS mount
force user = sshuserForces all file operations to run as sshuserMatches UID/GID from SSHFS mount options, enabling write access
guest ok = yesAllows guest authenticationSimplifies macOS client connection, avoiding credential management
writable = yesEnables write operationsAllows bidirectional protocol translation (read and write)

The force user = sshuser smb.conf19 is critical because:

  • SSHFS mounts files with uid=1000,gid=1000 (matching sshuser) 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:

  1. SSHFS Mount: Files owned by uid=1000,gid=1000 README.md49
  2. Samba Configuration: force user = sshuser smb.conf19
  3. User Account: sshuser has 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 StageInput ProtocolOutput InterfaceCode EntityConfiguration
Remote to SSHFile I/OSSH protocoluser@host:path (mount target)SSH credentials
SSH to FUSESSH protocolFilesystem operationssshfs command README.md49-o allow_other,uid=1000,gid=1000
FUSE to FilesystemFUSE operationsVFS operations/remote mount point Dockerfile12user_allow_other Dockerfile24
Filesystem IntegrationVFS operationsVFS operations/samba-share/remote symlink Dockerfile30chmod 777 /samba-share Dockerfile21
Filesystem to SMBVFS operationsSMB protocolsmbd process Dockerfile33smb.conf smb.conf:1-19
SMB to NetworkSMB protocolTCP/IPPorts 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:

  1. user_allow_other in fuse.conf enables the allow_other mount option
  2. allow_other mount option allows Samba to access SSHFS-mounted files
  3. uid=1000,gid=1000 mount options set file ownership to match sshuser
  4. force user = sshuser in Samba config aligns with SSHFS file ownership
  5. chmod 777 /samba-share ensures permissive access to the share directory
  6. 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


GitHub

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

PortProtocolPurpose
139NetBIOS Session ServiceLegacy SMB over NetBIOS
445SMB over TCP/IPModern 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:

  1. Port forwarding operates at Layer 4 (transport layer), forwarding TCP connections from 127.0.0.1 to the container
  2. SMB protocol negotiation includes hostname/IP checks at the application layer
  3. macOS's SMB client validates the target address during connection establishment
  4. 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 run command

Outbound (SSH)

  • Source: Container's sshfs client 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 sshfs mount 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

  1. IP Discovery: User runs docker inspect to find container IP
  2. SMB Connection: Finder connects to smb://172.17.0.x:445
  3. Request Routing: smbd receives request for files in /samba-share
  4. Symbolic Link Traversal: Path resolves to /remote via symlink
  5. FUSE Intercept: SSHFS intercepts filesystem operations on /remote
  6. SSH Transport: Request forwarded to remote server over SSH
  7. Remote Execution: Remote server performs actual filesystem operation
  8. 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 :

ComponentNetwork ExposureJustification
Samba ports (139/445)Localhost only (127.0.0.1 binding)Prevents external network access to file shares
Container IPDocker bridge network onlyNot exposed to external networks without additional Docker configuration
SSH clientOutbound onlyNo 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


GitHub

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
  1. macOS Host : Fully trusted - any local user can connect as guest
  2. Docker Container : Isolated but privileged execution environment
  3. 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

CapabilityRequired ForAlternative
FUSE device accessMounting SSHFS filesystems via /dev/fuseNone - FUSE requires kernel privileges
System call filtering bypassFUSE mount/umount operationsNone - standard containers block these syscalls
Device node accessCharacter device /dev/fuseAdding 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

PropertyValuePurpose
UsernamesshuserConsistent identity for all operations
PasswordsshpassNot used - authentication via SSH to remote
UID1000Default first user UID on most Linux systems
GID1000Default first user GID on most Linux systems
Home Directory/home/sshuserCreated 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 user ensures all operations as sshuser

Remote Mount Point : Dockerfile12

  • Initial Owner : root
  • Runtime Owner : sshuser after SSHFS mount with uid=1000,gid=1000

Permission Enforcement Points

  1. macOS → Samba : No enforcement (guest access)
  2. Samba → Filesystem : force user = sshuser applied by Samba
  3. SSHFS → Remote : uid=1000,gid=1000 ensures files created as UID/GID 1000
  4. 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

OptionPurposeSecurity Impact
allow_otherPermit access by users other than mount ownerAllows smbd (root) to access sshuser-mounted filesystem
uid=1000Set file owner to UID 1000Files appear owned by sshuser
gid=1000Set file group to GID 1000Files 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 VersionStatusSecurity IssuesAllowed
SMB1DeprecatedNo encryption, vulnerable to exploits (EternalBlue)❌ Blocked
SMB2LegacyBasic security, limited encryption✓ Minimum
SMB3ModernFull 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:

  1. Remote server SSH configuration
  2. OpenSSH client defaults (installed via apt-get install sshfs)
  3. 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

LimitationDescriptionImpactMitigation
No SMB authenticationAny local user can accessLocal users can read/write all filesIntended for single-user systems
Privileged containerContainer has host kernel accessContainer compromise = host compromiseUse only on trusted local machine
777 permissionsWorld-writable directoryAny container process can modify filesContainer is single-purpose
Guest-only accessNo user identity trackingCannot audit which macOS user performed actionAudit at remote SSH server level

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 ControlFileLine(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 accountDockerfile99sshuser:sshpass
Share permissionsDockerfile2121chmod 777 /samba-share
FUSE cross-userDockerfile2424user_allow_other
Security modesmb.conf33security = user
Guest mappingsmb.conf44map to guest = bad user
Guest accesssmb.conf:13-1413-14guest ok = yes, guest only = yes
Force usersmb.conf1919force user = sshuser
SMB protocolsmb.conf:7-87-8min protocol = SMB2
SSHFS options--allow_other,uid=1000,gid=1000

Sources : README.md31 README.md49 Dockerfile9 Dockerfile21 Dockerfile24 smb.conf:1-20


GitHub

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 StageDockerfile LinesActionArtifacts Created
Base ImageDockerfile2Pull ubuntu:latestLinux OS environment
Package InstallDockerfile:5-6apt-get install sshfs sambaSSHFS client, Samba server binaries
User CreationDockerfile9Create sshuser accountNon-root user (UID 1000)
Directory SetupDockerfile:12-15Create /remote and /samba-shareMount points
Config CopyDockerfile18Copy smb.conf to /etc/samba/Samba configuration
PermissionsDockerfile21chmod 777 /samba-shareWorld-writable share
FUSE ConfigDockerfile24Enable user_allow_otherCross-user mount access
Port ExposureDockerfile27EXPOSE 139 445SMB port metadata
Symbolic LinkDockerfile30ln -s /remote /samba-share/remoteIntegration point
Entry PointDockerfile33Set CMD for smbdProcess 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:

CapabilityRequired ForAlternative
Device AccessFUSE /dev/fuse deviceNone - FUSE requires device access
Mount OperationsCreating mount points in container namespaceNone - fundamental to SSHFS
Process ManagementFUSE background processesNone - 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.1 only (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 = sshuser in smb.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:

  1. smbd runs as root but accesses files as sshuser (via force user)
  2. sshfs mounts files owned by UID 1000 (sshuser)
  3. 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.

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 /remote directory 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 (not 0.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"]
FlagPurposeConsequence
--foregroundRun in foreground (don't daemonize)Container stays alive
--no-process-groupDon't create new process groupProper signal handling
--debug-stdoutLog to stdout instead of filesDocker 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:

  1. Base ubuntu:latest layer (~30-80 MB depending on version)
  2. apt-get update metadata layer (~40 MB)
  3. sshfs and samba package layers (~80 MB combined)
  4. User creation layer (minimal)
  5. Directory creation layers (minimal)
  6. Configuration file layers (< 1 MB)
  7. 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 smbd as 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


GitHub

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-get package 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:

PackagePurposeProvides
sshfsRemote filesystem clientSSHFS binary, FUSE dependencies, SSH client
sambaSMB serversmbd 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 -m flag)

Why Non-Root User

The sshuser account is critical for two reasons:

  1. SSHFS Mount Ownership: SSHFS mounts are owned by the user who initiates the mount. Running SSHFS as sshuser ensures consistent file ownership (UID 1000, GID 1000).

  2. Samba Force User: The smb.conf forces all SMB operations to run as sshuser, 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:

DirectoryPurposeMount Role
/remoteSSHFS mount pointTarget for sshfs user@host:path /remote
/samba-shareSamba root directoryServed 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:

  1. sshuser runs: sshfs user@host:path /remote
  2. Mount succeeds, files owned by sshuser
  3. smbd (running as root) tries to access /samba-share/remote
  4. 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:

PortProtocolPurpose
139NetBIOS Session ServiceLegacy SMB over NetBIOS
445SMB over TCPDirect 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


Dockerfile30 creates the critical integration point between SSHFS and Samba:

  • Source: /remote (SSHFS mount point)
  • Link: /samba-share/remote (symlink visible to Samba)

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:

FlagPurpose
--foregroundRun smbd in foreground instead of daemonizing
--no-process-groupPrevent process group creation
--debug-stdoutSend 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:

  1. Container remains running while smbd is active
  2. Docker can monitor the process health
  3. Logs are visible via docker logs
  4. 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 sshfs and samba in a single RUN to minimize layers
  • Separate Directory Creation: Dockerfile12 and Dockerfile15 create directories in separate commands (could be optimized)
  • Single Configuration Writes: Dockerfile24 appends to fuse.conf in one operation

The largest layers are:

  1. Package installation (100-200 MB including dependencies)
  2. Base Ubuntu image (~70 MB)
  3. 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:

  1. Ubuntu base image selection
  2. Package installation (SSHFS + Samba)
  3. User account creation (sshuser)
  4. Directory structure (/remote, /samba-share)
  5. Samba configuration deployment
  6. Permission configuration (777 on /samba-share)
  7. FUSE configuration (user_allow_other)
  8. Port exposure declaration (139, 445)
  9. Symbolic link creation (integration point)
  10. 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


GitHub

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.

PropertyValue
Path/remote
Created ByDockerfile12
PurposeSSHFS mount target
Default StateEmpty directory
PermissionsInherited from parent
Typical Mount Commandsshfs -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.

PropertyValue
Path/samba-share
Created ByDockerfile15
PurposeSamba share root directory
Permissions777 (world-writable)
Permission CommandDockerfile21
Samba ConfigurationReferenced 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

/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
PropertyValue
Symlink Path/samba-share/remote
Target Path/remote
Created Byln -s /remote /samba-share/remote at Dockerfile30
PurposeMake SSHFS-mounted files accessible via Samba
VisibilityAppears as a folder named remote in Finder

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 smbd process (running as root or nobody) to access files in /remote which are mounted by the sshuser account
  • FUSE Requirement : The symbolic link only functions correctly if the SSHFS mount uses the allow_other option, enabled by user_allow_other in /etc/fuse.conf
  • Transparent Operation : macOS clients see /samba-share/remote as 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

PathOwnerPermissionsPurpose
/remoteroot:root755 (default)SSHFS mount target
/samba-shareroot:root777 (world-writable)Samba share root
/samba-share/remoteroot:root777 (symlink)Integration point
/etc/samba/smb.confroot:root644 (default)Samba configuration
/etc/fuse.confroot:root644 (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

The filesystem layout is tightly coupled with configuration files that control access permissions:

  • /etc/fuse.conf : Contains user_allow_other setting (Dockerfile24), which enables the symbolic link to function across different user contexts
  • /etc/samba/smb.conf : Defines path = /samba-share as the share root and configures guest access and forced user identity

For complete configuration reference, see Samba Configuration) and FUSE Configuration.

Sources: Dockerfile24 Dockerfile18


GitHub

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

PropertyValueLocation
UsernamesshuserDockerfile9
PasswordsshpassDockerfile9
User TypeNon-root with home directoryDockerfile9
UID1000 (default for first non-root user)Assigned by useradd
GID1000 (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:

  1. SSHFS Mount Owner : When users manually execute sshfs commands inside the container, they typically do so as sshuser via docker exec
  2. File Operation Identity : All files accessed through Samba are forced to use sshuser as the effective user (via force user = sshuser in smb.conf19)
  3. 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 :

OctalBinaryOwnerGroupOther
777111 111 111rwxrwxrwx

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:

  1. Multiple Process Access : Both the smbd daemon and SSHFS mount processes need write access to directories under /samba-share
  2. Symbolic Link Traversal : The symbolic link at /samba-share/remote (created at Dockerfile30) points to /remote, and both services must traverse this link
  3. Unknown Client UIDs : macOS clients connecting as "guest" might have their requests mapped to various internal UIDs by Samba
  4. 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 :

  1. Without user_allow_other: FUSE kernel module checks if requesting process UID matches mount owner UID
  2. With user_allow_other + allow_other mount 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:

OperationRequired PermissionsConfiguration Source
Samba reads /samba-shareWorld-readable (r--)Dockerfile21
Samba writes to /samba-shareWorld-writable (-w-)Dockerfile21
Samba traverses symlinkWorld-executable (--x)Dockerfile21
Samba accesses /remoteuser_allow_other + allow_otherDockerfile24
SSHFS mounts remote filesUser sshuser credentialsManual sshfs command
Files created via SambaOwned by UID 1000 (forced)smb.conf19
File permission on creation0777 (create_mask)smb.conf17
Directory permission on creation0777 (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:

  1. The smbd daemon (running as root or dedicated samba user)
  2. The SSHFS mount (owned by sshuser)

The Solution Components

ComponentPurposeWhy It's Necessary
sshuser accountConsistent identityProvides a known UID (1000) for both Samba and SSHFS
777 on /samba-sharePermissive directory accessAllows any process to traverse and write
user_allow_other in fuse.confEnable cross-user FUSE accessAllows smbd to read sshuser's mounts
allow_other mount optionActivate cross-user accessMust be specified when mounting
force user = sshuserIdentity enforcementEnsures Samba operations use UID 1000
uid=1000,gid=1000 mount optionsFile ownership mappingMakes 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

  1. World-Writable Directories : The 777 permissions on /samba-share allow any process in the container to modify files
  2. Guest-Only Access : No authentication required to connect via SMB (smb.conf:13-14)
  3. Hardcoded Password : The sshuser password is sshpass, visible in the Dockerfile
  4. Privileged Container : The container typically runs with --privileged flag to enable FUSE

Mitigating Factors

  1. Container Isolation : Permissions are isolated within the container namespace
  2. Localhost Port Binding : SMB ports are bound to 127.0.0.1 only (see 3.3)
  3. Single-Purpose Container : No other services or users exist in the container
  4. 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 IdentifierFileLine(s)System EntityPurpose
sshuserDockerfile9Linux user accountSSHFS mount owner
sshpassDockerfile9Password stringAuthentication credential
useradd -mDockerfile9CommandUser creation with home dir
777Dockerfile21Octal permissionWorld-readable/writable/executable
user_allow_otherDockerfile24FUSE config optionEnable cross-user mount access
/etc/fuse.confDockerfile24Config file pathFUSE system configuration
force usersmb.conf19Samba directiveOverride client user identity
guest oksmb.conf13Samba directiveAllow guest connections
guest onlysmb.conf14Samba directiveEnforce guest authentication
map to guestsmb.conf4Samba directiveGuest authentication trigger
create masksmb.conf17Samba directiveNew file permissions
directory masksmb.conf18Samba directiveNew 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 sshuser account (UID 1000) as the consistent identity layer
  • World-writable permissions (777) on /samba-share for cross-process access
  • FUSE's user_allow_other configuration to enable Samba access to SSHFS mounts
  • Samba's force user directive to normalize all client operations to sshuser
  • 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


GitHub

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

FlagPurpose
--foregroundPrevents smbd from daemonizing, keeping it attached to the terminal so Docker can monitor it
--no-process-groupDisables process group creation, ensuring proper signal handling in the container environment
--debug-stdoutRedirects 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-sshfs image
  • Filesystem structure is verified:
  • Permissions are applied: /samba-share is set to 777 Dockerfile21

Phase 2: Service Startup

  • smbd process launches as PID 1 Dockerfile33
  • Configuration is loaded from /etc/samba/smb.conf Dockerfile18
  • 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

StateDescriptionKey Characteristics
ContainerCreatedContainer exists but smbd has not startedNo network services active
SmbdStartingsmbd process initializingReading configuration, binding ports
SmbdReady/NoRemoteMountSamba service operational, no SSHFS mount/samba-share accessible but /remote empty
SmbdReady/RemoteMountedBoth Samba and SSHFS operationalRemote files accessible via /samba-share/remote
SambaIdleNo active SMB client connectionsService listening, no file operations
SambaActivemacOS Finder connected and accessing filesFile I/O operations in progress
SmdbStoppingContainer shutting downPorts 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 User allows unauthenticated connections
  • Forced User Identity : force user = sshuser ensures all operations run as UID 1000
  • Protocol Version : min protocol = SMB2 enforces modern SMB protocol
  • Write Permissions : writable = yes with create mask = 0777 allows 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:

  1. User opens an interactive shell via docker exec README41
  2. SSHFS command is executed manually README49
  3. Mount options are critical:
    • allow_other: Allows smbd (running as different user) to access the mount README52
    • uid=1000,gid=1000: Maps remote files to sshuser for write access README53
  4. SSHFS daemonizes and runs in background
  5. The symbolic link /samba-share/remote now points to populated directory Dockerfile30
  6. smbd continues 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 SIGTERM to PID 1 (smbd)
  • By default, Docker waits 10 seconds for graceful shutdown
  • If smbd doesn't exit, Docker sends SIGKILL

Phase 2: Samba Cleanup

  • smbd closes 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 SIGTERM to all container processes, including background sshfs daemons
  • sshfs unmounts 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 /remote README76
  • Or simply docker stop will 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

ProcessPIDParentPurpose
smbd1None (init)Main Samba service, container's PID 1
smbd workersDynamic1Handle individual SMB client connections
sshfsVariable1FUSE 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:

PhaseDurationKey ProcessState
BuildOne-timeDocker buildImage created with smbd as CMD
Startup~1-2 secondssmbd initializationPorts bound, configuration loaded
Ready (No Mount)Indefinitesmbd serving/samba-share accessible but empty
Runtime (Mounted)Indefinitesmbd + sshfsFull functionality with remote files
Shutdown~1-10 secondsSignal handlingGraceful 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


GitHub

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:

  • Samba server settings: 5.1)
  • FUSE access control: 5.2
  • SSHFS mounting parameters: 5.3

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 TypeApplied AtPurposeModifiable at Runtime
Samba ConfigurationBuild-timeDefines SMB share behavior, authentication, and file permissionsNo (requires image rebuild)
FUSE ConfigurationBuild-timeEnables cross-user filesystem accessNo (requires image rebuild)
SSHFS Mount OptionsRuntimeControls remote mount behavior, permissions, and ownershipYes (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

ParameterFile LocationLine NumberValueRequiredPurpose
user_allow_other/etc/fuse.confAppendedN/AYesAllows non-mounting users (Samba) to access FUSE filesystems
allow_othersshfs commandRuntime optionN/AYesEnables cross-user access to SSHFS mount
uid=1000sshfs commandRuntime option1000YesSets file ownership to sshuser for write access
gid=1000sshfs commandRuntime option1000YesSets group ownership to sshuser for write access
force user/etc/samba/smb.conf19sshuserYesForces all Samba operations to use sshuser identity
map to guest/etc/samba/smb.conf4bad userYesMaps unknown/invalid users to guest account
guest ok/etc/samba/smb.conf13yesYesAllows guest access to share
guest only/etc/samba/smb.conf14yesYesForces guest mode even if credentials provided
writable/etc/samba/smb.conf12yesYesEnables write access through Samba
create mask/etc/samba/smb.conf170777NoPermissions for newly created files
directory mask/etc/samba/smb.conf180777NoPermissions for newly created directories
client min protocol/etc/samba/smb.conf7SMB2NoMinimum SMB protocol version
server min protocol/etc/samba/smb.conf8SMB2NoMinimum 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

ConfigurationModifiable After BuildHow to ModifyRequires Container Restart
Samba settings (smb.conf)NoRebuild image with modified smb.confN/A (new container)
FUSE settings (fuse.conf)NoRebuild image with modified DockerfileN/A (new container)
SSHFS mount optionsYesUse different options in sshfs commandNo (remount required)
User credentials (sshuser:sshpass)NoRebuild image with modified DockerfileN/A (new container)
Directory permissions (/samba-share)NoRebuild image with modified DockerfileN/A (new container)
Port mappingsNoStop and restart with docker runYes (container restart)
Privileged modeNoStop and restart with docker runYes (container restart)

Changing SSHFS Mount Options

SSHFS mount options are the only runtime-configurable settings. To change them:

  1. Unmount the existing mount: fusermount -u /samba-share
  2. Mount with new options: sshfs -o [new-options] user@host:path /samba-share

Common option modifications:

  • Change ownership : Modify uid= and gid= 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:


GitHub

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:

SectionLinesPurpose
[global]smb.conf:1-8Server-wide settings including security model, protocol versions, and network identity
[SSHFS Share]smb.conf:10-19Share-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

DirectiveValueLinePurpose
workgroupWORKGROUPsmb.conf2Sets Windows workgroup name; matches default macOS SMB client expectations
netbios namesambaserversmb.conf6Defines the server's NetBIOS identity for network browsing and name resolution
server stringSamba Server %vsmb.conf5Human-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
DirectiveValueLineBehavior
securityusersmb.conf3Requires user-level authentication (not share-level); enables per-user access control but overridden by map to guest
map to guestbad usersmb.conf4Converts 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
DirectiveValueLineEffect
client min protocolSMB2smb.conf7Prevents Samba from acting as client using SMB1 (not directly relevant for this server-only use case)
server min protocolSMB2smb.conf8Rejects 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

DirectiveValueLinePurpose
Share nameSSHFS Sharesmb.conf10Display name shown in macOS Finder network browser; spaces are preserved in SMB share names
path/samba-sharesmb.conf11Root directory served by this share; contains the symbolic link to /remote SSHFS mount point
browseableyessmb.conf16Makes 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:

DirectiveValueLineEffect
writableyessmb.conf12Enables write operations (create, modify, delete); opposite of read only
read onlynosmb.conf15Explicitly disables read-only mode; redundant with writable = yes but included for clarity
create mask0777smb.conf17Sets permission mask for newly created files to full access (owner/group/other all have rwx)
directory mask0777smb.conf18Sets permission mask for newly created directories to full access

The 0777 masks are unusually permissive but necessary for this use case because:

  1. The force user directive causes all operations to run as sshuser
  2. SSHFS mount options set uid=1000,gid=1000 matching sshuser
  3. 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
DirectiveValueLineBehavior
guest okyessmb.conf13Allows guest connections to this share; permits access without valid credentials
guest onlyyessmb.conf14Forces 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
DirectiveValueLinePurpose
force usersshusersmb.conf19Overrides 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:

  1. Guest connections typically map to the nobody user
  2. The /remote SSHFS mount is owned by sshuser (UID 1000) due to mount options
  3. FUSE by default only allows the mounting user to access mounted filesystems
  4. 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 ChoiceSecurity ImpactRationale
map to guest = bad user❌ No authentication requiredEliminates password friction for local development; container ports bound to 127.0.0.1 limits exposure
guest only = yes❌ Valid credentials ignoredEnsures consistent behavior; prevents confusion when credentials are provided but unused
create mask = 0777❌ World-writable filesRequired for UID/GID consistency across SMB and direct container access
directory mask = 0777❌ World-writable directoriesPrevents permission issues when accessing via different methods
force user = sshuser⚠️ Single user identityEnables write access through SSHFS mount; loses audit trail of actual user
server min protocol = SMB2✅ Blocks SMB1 vulnerabilitiesModern protocol enforcement reduces attack surface
Ports bound to 127.0.0.1✅ Localhost-only accessPrevents external network exposure despite guest access (requires Docker run configuration)

Mitigating Factors

The security model is acceptable for local development because:

  1. Network isolation : Docker run command binds ports to 127.0.0.1, preventing LAN access
  2. Temporary containers : Development containers are typically short-lived and frequently rebuilt
  3. Single-user context : Developer's workstation is single-user; no multi-tenant concerns
  4. 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 user based 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 SettingMust MatchLocationVerification Command
force user = sshuserUser account with UID 1000Dockerfileid sshuser should show uid=1000
force user = sshuserSSHFS mount uid=1000Mount command`mount
path = /samba-shareExisting directoryDockerfilels -ld /samba-share should exist with 777
path = /samba-shareContains symlink to /remoteDockerfilels -l /samba-share/remote should show symlink
create mask = 0777Directory permissions 777Dockerfilestat -c %a /samba-share should return 777
guest ok = yesmap to guest = bad user in [global]smb.conf4Both directives must be present
server min protocol = SMB2macOS SMB client versionmacOS 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


GitHub

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.

PropertyValue
File Path/etc/fuse.conf
FormatPlain text, one directive per line
Ownershiproot:root
Default StateContains commented-out directives
Modification MethodAppended 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:

Dockerfile:23-24

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

AspectDetails
When AppliedDuring docker build execution
PersistenceBaked into the Docker image
Modification ScopeContainer filesystem only, not host
Requires RebuildYes, 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:

  1. SSHFS Client : Mounts the remote filesystem (runs as sshuser)
  2. Samba Server : Serves the mounted filesystem to macOS (runs as root or 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

LayerComponentConfigurationDependency
Build Time/etc/fuse.confuser_allow_otherNone (base setting)
Runtimesshfs command-o allow_otherRequires user_allow_other in fuse.conf
EffectSamba accessCross-service file accessRequires 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 ElementLocationValuePurpose
FUSE policy directive/etc/fuse.confuser_allow_otherPermit non-root use of allow_other
Set duringDocker buildDockerfile24Persistent in image
Enables mount optionsshfs command-o allow_otherCross-user file access
BenefitsRuntimeSamba ↔ SSHFS accessProtocol bridge functionality
Security trade-offSystem-wideRelaxed isolationRequired 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


GitHub

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:

OptionValuePurposeConsequence if Omitted
allow_other(flag)Permits non-owner users (including Samba) to access mounted filesSamba cannot read the mounted filesystem; mount is unusable
uid1000Sets the user ID for all files in the mountFiles may have incorrect ownership; write operations may fail
gid1000Sets the group ID for all files in the mountWrite 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:path is the remote SSH endpoint (user, host, and remote directory path)
  • /samba-share is 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:

Dockerfile:23-24

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:

Dockerfile:8-9

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:

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:

Configurationallow_otheruid=1000gid=1000Result
❌ NoneNoNoNoSamba cannot access mount
⚠️ PartialYesNoNoAccessible but likely read-only; ownership mismatches
⚠️ PartialYesYesNoAccessible but read-only from macOS
⚠️ PartialYesNoYesAccessible; UID mismatches may cause issues
✅ CompleteYesYesYesFull 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

OptionPurposeExample
StrictHostKeyChecking=noSkip SSH host key verification-o StrictHostKeyChecking=no
IdentityFile=/path/to/keyUse specific SSH key-o IdentityFile=/root/.ssh/id_rsa
port=2222Use non-standard SSH port-o port=2222
reconnectAutomatically reconnect on connection loss-o reconnect
compression=yesEnable 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=1000 confirms the uid option
  • group_id=1000 confirms the gid option
  • allow_other confirms cross-user access is enabled

Sources: Implicit from system behavior


GitHub

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:

PlatformStatusNetworkingNotes
OrbStack✅ RecommendedWorks out-of-boxNo network modifications needed
Docker Desktop⚠️ Requires modificationNeeds routing changesDefault networking incompatible
Docker Engine (Linux)✅ CompatibleStandard Docker networkNative 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 :

  1. Switch to OrbStack (recommended):

  2. 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 :

  1. Get container IP:

  2. Use the returned IP (e.g., 172.17.0.2) in Finder:

    smb://172.17.0.2
    
  3. Connect as Guest when prompted

Sources : README.md:57-68

Container IP Discovery

If the container IP is not showing or returns empty:

IssueCauseSolution
Empty output from inspectContainer not runningdocker ps to verify, then docker start docker-sshfs
Container exists but no networkNetwork mode issueEnsure container not started with --network=host
IP changes after restartDocker DHCP reassignmentUse 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 :

  1. "Guest" option not appearing :

    • Verify smb.conf contains guest ok = yes smb.conf
    • Container may need rebuild if configuration changed
  2. Guest connection rejected :

    • Check that smbd process is running inside container:

    • Verify ports are exposed:

  3. Prompted for username/password :

    • Still use Guest option (ignore prompt)
    • Or use: username=sshuser, password=sshpass Dockerfile9

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 :

  1. Unmount from macOS Finder :

    • Open Finder
    • Locate the mounted share in sidebar or "Network" section
    • Click eject button or right-click → "Eject"
  2. 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 :

OptionPurposeWithout It
uid=1000Sets file owner to sshuser (UID 1000) Dockerfile9Files owned by root, Samba can't write
gid=1000Sets file group to sshuser (GID 1000)Group permissions fail
allow_otherAllows other users to access mount README.md52Samba 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 :

  1. Verify /etc/fuse.conf contains user_allow_other Dockerfile24:

  2. Verify mount command includes allow_other README.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 MessageCauseSolution
Permission denied (publickey)SSH key not availableUse password auth or mount SSH key into container
Connection refusedRemote SSH port blockedVerify firewall rules, try different port
Host key verification failedUnknown host keyAccept 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 :

  1. Stop conflicting service :

  2. 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


GitHub

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

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.1 functions 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:

  1. Discover container IP using README.md60:

  2. 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

PropertyValue
NetworkDocker bridge network (default)
Range172.17.0.0/16 (typical)
AssignmentDynamic (changes on container restart)
AccessibilityOrbStack: Direct; Docker Desktop: Requires routing
PersistenceNot 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

FeatureOrbStackDocker DesktopNotes
Port Forwarding (139/445)✓ Works⚠ LimitedBoth bind to 127.0.0.1 per README.md31
Container IP Access✓ Direct⚠ Needs RoutesOrbStack provides native bridge
localhost SMB Connection✗ Not Supported✗ Not SupportedPlatform-wide limitation README.md57
Network ModificationsNot RequiredRequiredDocker Desktop needs routing config README.md9
Connection Methodsmb://container-ipsmb://container-ip (with routes)Must use README.md60 to find IP
Privileged ContainerRequiredRequired--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:

  1. Container is running: docker ps | grep docker-sshfs
  2. Ports are bound: docker port docker-sshfs
  3. smbd process 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:

  1. Identifying the Docker bridge network subnet (usually 172.17.0.0/16)
  2. Adding static routes from macOS to the Docker VM's network interface
  3. 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:

  1. Verify container IP assignment :

Expected: Non-empty IP in 172.17.0.x range

  1. Verify port bindings :

Expected output:

     139/tcp -> 127.0.0.1:139
     445/tcp -> 127.0.0.1:445
  1. 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


GitHub

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:

ComponentStateImpact
macOS FinderConnected to SMB shareHolds open file handles and directory listings
smbd daemonServing /samba-shareActively reading from the mount point
FUSE mountMounted at /samba-shareCannot unmount while smbd has open file descriptors
fusermountAttempting unmountBlocked 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

  1. Open macOS Finder
  2. Locate the mounted network share in the sidebar under "Locations" or "Network"
  3. 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:

  1. First: Unmount the SMB share from macOS Finder
  2. Second: Verify Samba connections are closed (smbstatus --brief)
  3. 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 smbd daemon (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

AspectImpact
macOS Finder connectionImmediately broken; may show "connection failed" error
Unsaved data in transitPotentially lost if not yet written to remote server
SSHFS mount stateAutomatically cleaned up by container shutdown
Container restartRequires 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

PathPurposeReferenced In
/samba-shareSSHFS mount point and Samba rootREADME.md49 README.md76
/usr/bin/fusermountFUSE unmount utilityREADME.md76
/usr/sbin/smbdSamba daemon binaryContainer runtime

The mount options specified during the initial sshfs command affect the unmount behavior:

  • allow_other: Enables smbd (running as sshuser) 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


GitHub

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:

SymptomLikely Cause
"Connection failed" when using smb://localhostLocalhost limitation - must use container IP
"Connection failed" when using smb://127.0.0.1Same localhost limitation
Cannot find container IP addressContainer not running or incorrect inspection command
Authentication dialog appears and rejects credentialsAttempting to use registered user instead of Guest
Connection refused on SMB portsPort forwarding not configured or ports already in use
Network timeout when connectingDocker 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:

MethodCommandWhen to Use
Full inspect outputdocker inspect docker-sshfsReview complete network configuration
Network listingdocker network inspect bridgeCheck bridge network configuration
Container statsdocker ps with wide outputVerify container is running
OrbStack UICheck OrbStack dashboardWhen 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 access
  • guest ok = yes: Anonymous guest connections are permitted
  • guest only = yes: All connections are forced to guest mode, regardless of credentials provided

Connection Procedure

When connecting from macOS Finder README.md:63-67:

  1. Navigate to Finder → Go → Connect to Server
  2. Enter smb://172.17.0.2 (using discovered container IP)
  3. When authentication dialog appears, select Connect as Guest
  4. 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

StepCommand/ActionExpected Result
1. Container running`docker psgrep docker-sshfs`
2. Samba process activedocker exec docker-sshfs pgrep smbdReturns process ID
3. SSHFS mounted`docker exec docker-sshfs mountgrep sshfs`
4. Get container IPdocker inspect --format '{{ .NetworkSettings.IPAddress }}' docker-sshfsReturns IP like 172.17.0.x
5. Verify portsdocker port docker-sshfsShows 139/tcp and 445/tcp mappings

Connection Attempt

StepActionConfiguration
1. Open FinderGo → Connect to ServerN/A
2. Enter addresssmb://172.17.0.2Use discovered container IP, not localhost
3. AuthenticateConnect as GuestDo not use registered user
4. Verify mountCheck Network in Finder sidebarShare appears as sambaserver
5. Access filesNavigate to remote folderContents 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

ProblemSolutionReference
Cannot connect to smb://localhostUse container IP from docker inspectREADME.md:57-61
Cannot find container IPVerify container is running with docker psREADME.md31
Authentication failsConnect as Guest , not registered userREADME.md67 smb.conf:13-14
Connection timeout with Docker DesktopSwitch to OrbStackREADME.md9
Port binding failsCheck for conflicts on 139/445 with lsofREADME.md31
Container runs but no SMBVerify smbd process with docker exec docker-sshfs pgrep smbdDockerfile33
Files not visibleEnsure SSHFS mounted with allow_other flagREADME.md49

Sources : README.md9 README.md31 README.md49 README.md:57-67 smb.conf:13-14 Dockerfile33


GitHub

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.

ComponentValue
Syntaxdocker build -t docker-sshfs .
Working DirectoryRepository root (contains Dockerfile)
Tag Namedocker-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-share with symbolic link
  • Configuration files: smb.conf, modified fuse.conf

Sources: README.md:19-23

docker rmi

Removes the built image from the local Docker registry.

ComponentValue
Syntaxdocker rmi docker-sshfs
When to UseAfter removing all containers using this image
EffectDeletes 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.

ComponentValuePurpose
Full Commanddocker run --privileged --name docker-sshfs -p 127.0.0.1:139:139 -p 127.0.0.1:445:445 docker-sshfsStart container
--privilegedRequiredFUSE operations need elevated privileges
--namedocker-sshfsContainer identifier for subsequent commands
-p 127.0.0.1:139:139Port mappingNetBIOS session service (localhost only)
-p 127.0.0.1:445:445Port mappingSMB over TCP (localhost only)
Process ModeForegroundRuns smbd -F as main process; container stops when smbd exits

Example:

Important Notes:

  • Container runs in foreground by default; terminal shows Samba logs
  • Use -d flag for detached mode: docker run -d --privileged ...
  • Port binding to 127.0.0.1 prevents external network access
  • Container must be named docker-sshfs or update container name in subsequent commands

Sources: README.md:26-34

docker stop

Stops a running container gracefully.

ComponentValue
Syntaxdocker stop docker-sshfs
EffectSends SIGTERM to smbd, unmounts may fail if Finder still connected
Timeout10 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).

ComponentValue
Syntaxdocker start docker-sshfs
When to UseAfter docker stop, to resume without rebuilding
NoteSSHFS mounts are not preserved across container restarts

Example:

Sources: Container lifecycle management pattern

docker rm

Removes a stopped container permanently.

ComponentValue
Syntaxdocker rm docker-sshfs
PrerequisiteContainer must be stopped first
EffectDeletes 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.

ComponentValue
Get IP Addressdocker inspect --format '{{ .NetworkSettings.IPAddress }}' docker-sshfs
Full JSON Outputdocker inspect docker-sshfs
Common Use CaseFind 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).

ComponentValue
Syntaxdocker logs docker-sshfs
Follow Modedocker logs -f docker-sshfs
Last N Linesdocker logs --tail 50 docker-sshfs

Example:

Sources: Standard Docker logging pattern

docker ps

Lists running containers.

ComponentValue
Running Containersdocker ps
All Containersdocker ps -a
Filter by Namedocker 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.

ComponentValue
Syntaxdocker exec -it docker-sshfs bash
Userroot (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.

ComponentValuePurpose
Full Syntaxsshfs -o allow_other,uid=1000,gid=1000 user@host:path /remoteMount remote filesystem
-o allow_otherCriticalWithout this, Samba cannot access the mount (different user)
-o uid=1000Critical for writesSets file ownership to sshuser (UID 1000)
-o gid=1000Critical for writesSets group ownership to sshuser (GID 1000)
user@host:pathVariableSSH connection string; replace with actual remote endpoint
/remoteFixedMount point directory (pre-created in container)

Examples:

Why These Options Matter:

OptionWithout ItWith It
allow_otherSamba (running as sshuser) cannot access filesSamba can read/write mounted files
uid=1000Files owned by root or remote UIDFiles owned by sshuser, enabling writes
gid=1000Group ownership mismatchConsistent group ownership

Sources: README.md:46-53

Additional SSHFS Options

Optional parameters for advanced use cases.

OptionPurposeExample
-o port=NCustom SSH port-o port=2222
-o IdentityFile=/path/to/keyUse specific SSH key-o IdentityFile=/root/.ssh/id_rsa
-o reconnectAuto-reconnect on connection loss-o reconnect
-o ServerAliveInterval=15Keep connection alive-o ServerAliveInterval=15
-o Compression=yesEnable 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.

ComponentValue
Syntaxfusermount -u /remote
When to UseBefore stopping container or remounting different endpoint
Common ErrorDevice 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:

  1. In macOS Finder, eject the network share (right-click → Eject)
  2. Then retry: fusermount -u /remote
  3. 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:

  1. In macOS Finder, press Cmd+K or navigate to Go → Connect to Server

  2. 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

  1. Click Connect

  2. Authentication prompt:

    • Select Guest (do not use username/password)
    • Click Connect
  3. The share appears in Finder under NetworkIP address

  4. Inside the share, navigate to the remote directory to access mounted files

Sources: README.md:55-69

Connection String Format

ComponentFormatExample
Protocolsmb://Required
IP AddressContainer IP (not localhost)172.17.0.2
PortImplicit (139/445)Not specified in URL
Full Stringsmb://<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:

  1. In Finder, locate the mounted share under Network
  2. Right-click (or Control-click) on the share
  3. Select Eject or click the eject button (⏏)
  4. 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

TaskCommandLocation
Build imagedocker build -t docker-sshfs .Repository root
Start containerdocker run --privileged --name docker-sshfs -p 127.0.0.1:139:139 -p 127.0.0.1:445:445 docker-sshfsmacOS host
Get container IPdocker inspect --format '{{ .NetworkSettings.IPAddress }}' docker-sshfsmacOS host
Enter containerdocker exec -it docker-sshfs bashmacOS host
Mount remotesshfs -o allow_other,uid=1000,gid=1000 user@host:path /remoteInside container
Unmount remotefusermount -u /remoteInside container
Connect in Findersmb://container-ipmacOS Finder
Stop containerdocker stop docker-sshfsmacOS host
View logsdocker logs docker-sshfsmacOS host
Remove containerdocker rm docker-sshfsmacOS host
Remove imagedocker rmi docker-sshfsmacOS host

Sources: README.md:19-90


GitHub

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:

PortProtocolPurposeBindingExposure
139TCPNetBIOS Session Service127.0.0.1:139Localhost only
445TCPSMB over TCP (Direct hosting)127.0.0.1:445Localhost only
22TCPSSH (outbound from container)N/ARemote 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:

PathTypePermissionsOwnerPurpose
/remoteDirectoryDefault (created by mkdir)rootSSHFS mount point
/samba-shareDirectory777 (rwxrwxrwx)rootSamba share root
/samba-share/remoteSymbolic LinkN/ArootLink to /remote

Sources: Dockerfile12 Dockerfile15 Dockerfile21 Dockerfile30 smb.conf11 README.md49

Permission Specifications

ComponentPermissionOctalSpecification
/samba-share directoryrwxrwxrwx0777Dockerfile21
SSHFS mounted filesControlled by mount optionsN/ASee SSHFS Mount Options
Samba share create maskrwxrwxrwx0777smb.conf17
Samba share directory maskrwxrwxrwx0777smb.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:

ParameterValueSource
UsernamesshuserDockerfile9
PasswordsshpassDockerfile9
UID1000 (default)Implicitly assigned by useradd -m
GID1000 (default)Implicitly assigned by useradd -m
Home Directory/home/sshuserCreated 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 KeyValueSourcePurpose
workgroupWORKGROUPsmb.conf2NetBIOS workgroup name
securityusersmb.conf3Security mode
map to guestbad usersmb.conf4Map unknown users to guest
client min protocolSMB2smb.conf7Minimum client protocol version
server min protocolSMB2smb.conf8Minimum server protocol version
server stringSamba Server %vsmb.conf5Server identification string
netbios namesambaserversmb.conf6NetBIOS server name

The protocol enforcement prevents fallback to SMB1, which has known security vulnerabilities.

Sources: smb.conf:1-9

SSH Protocol

ParameterValueNotes
Port22 (default)Standard SSH port
ProtocolSSH-2Used by sshfs command
AuthenticationUser-specifiedPassed via user@host syntax
Connection TypeFUSE-basedFilesystem 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:

OptionValuePurposeSource
allow_otherFlagAllows non-mounting user (Samba) to access mountREADME.md52
uid1000Sets file ownership to sshuserREADME.md53
gid1000Sets group ownership to sshuserREADME.md53

Complete mount command syntax from README.md49:

Critical Dependencies:

  • allow_other requires user_allow_other enabled in /etc/fuse.conf (Dockerfile24)
  • uid=1000,gid=1000 must match the UID/GID of sshuser for 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 FileSettingValuePurpose
/etc/fuse.confuser_allow_otherEnabledPermits 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:

FlagValuePurposeSource
--privilegedRequiredEnables FUSE filesystem operationsREADME.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
ArgumentPurpose
--foregroundRuns smbd in foreground (required for Docker)
--no-process-groupPrevents process group creation
--debug-stdoutSends 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:

ParameterValuePurpose
path/samba-shareRoot directory of share
writableyesAllows write operations
guest okyesPermits guest access
guest onlyyesForces guest authentication
read onlynoExplicitly enables writing
browseableyesShare visible in network browser
create mask0777Default permissions for new files
directory mask0777Default permissions for new directories
force usersshuserAll 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:

PackagePurposeVersion
sshfsSSHFS client for mounting remote filesystems over SSHLatest from Ubuntu repository
sambaSMB/CIFS server for file sharingLatest from Ubuntu repository

Base Image

SpecificationValue
Base Imageubuntu:latest
SourceDockerfile2

Sources: Dockerfile2 Dockerfile:5-6

Command Reference Summary

Docker Commands

CommandPurposeSource
docker build -t docker-sshfs .Build container imageREADME.md22
docker run --privileged --name docker-sshfs -p 127.0.0.1:139:139 -p 127.0.0.1:445:445 docker-sshfsStart containerREADME.md31
docker exec -it docker-sshfs bashAccess container shellREADME.md41
docker inspect --format '{{ .NetworkSettings.IPAddress }}' docker-sshfsGet container IPREADME.md60

SSHFS Commands

CommandPurposeSource
sshfs -o allow_other,uid=1000,gid=1000 user@host:path /remoteMount remote filesystemREADME.md49
fusermount -u /samba-shareUnmount filesystemREADME.md76

Sources: README.md22 README.md31 README.md41 README.md49 README.md60 README.md76

Platform Requirements

RequirementSpecificationNotes
PlatformOrbStackHighly recommended for network compatibility
AlternativeDocker DesktopRequires network routing modifications
macOS VersionAny version supporting DockerNo 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 MessageCauseResolution
fusermount: failed to unmount /samba-share: Device or resource busySamba share still mounted on macOSUnmount from Finder first, or stop container

This error is documented in README.md:82-85

Sources: README.md:82-85