This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Protocol Translation
Relevant source files
Purpose and Scope
This document explains how sshfs-mac-docker performs protocol translation to bridge SSH and SMB protocols. The system translates between SSH-based remote filesystem access (via SSHFS) and SMB-based local network sharing (via Samba) using filesystem operations and symbolic links within the Docker container.
For information about the overall three-tier architecture, see Three-Tier Design. For networking details including port mappings, see Network Architecture. For filesystem layout details, see Filesystem Layout.
Protocol Translation Overview
The system performs protocol translation by layering three distinct technologies—SSH, FUSE/SSHFS, and SMB/Samba—and connecting them through filesystem primitives. The translation occurs entirely within the Docker container's userspace, avoiding any kernel modifications on the macOS host.
graph TB
subgraph "Remote Source"
RemoteSSH["Remote SSH Server\nuser@host:path"]
end
subgraph "Container Protocol Bridge"
SSHLayer["SSH Protocol Layer"]
FUSELayer["FUSE Filesystem Layer"]
FileSystem["Filesystem Integration"]
SMBLayer["SMB Protocol Layer"]
SSHLayer -->|FUSE operations| FUSELayer
FUSELayer -->|mounts to /remote| FileSystem
FileSystem -->|symlink to /samba-share/remote| SMBLayer
end
subgraph "macOS Consumer"
FinderSMB["macOS Finder\nSMB Client"]
end
RemoteSSH -->|SSH port 22| SSHLayer
SMBLayer -->|SMB ports 139/445| FinderSMB
Protocol Translation Layers
Diagram: Protocol Translation Stack
The translation chain transforms SSH-based remote access into SMB-based network share access through intermediate filesystem representations.
Sources: README.md, Dockerfile, smb.conf
SSH to FUSE Translation
The first translation occurs when SSHFS converts SSH protocol operations into FUSE filesystem operations, mounting the remote filesystem at /remote within the container.
SSHFS Mount Process
The SSHFS mount command translates remote SSH access into local filesystem operations:
This command is executed inside the container via docker exec -it docker-sshfs bash README.md41
SSHFS Mount Options Mapping
Diagram: SSHFS Mount Option Translation
Critical Mount Options
| Mount Option | Purpose | Without It |
|---|---|---|
allow_other | Allows users other than the mounting user to access the mount | Samba service cannot access files mounted by sshuser |
uid=1000 | Sets file ownership to UID 1000 (sshuser) | Write operations fail; filesystem appears read-only |
gid=1000 | Sets group ownership to GID 1000 (sshuser group) | Write operations fail; filesystem appears read-only |
The allow_other option requires the user_allow_other configuration in /etc/fuse.conf Dockerfile24:
user_allow_other
This configuration is added during container build to enable cross-user filesystem access between the SSHFS mount (owned by sshuser) and the Samba service (running as a different process).
Sources: README.md:49-53, Dockerfile:24
graph TB
subgraph "SSHFS Mount"
RemoteDir["/remote\nFUSE mount point\nCreated: Dockerfile:12"]
RemoteFiles["Remote files\nvia SSHFS"]
end
subgraph "Samba Share Root"
SambaDir["/samba-share\nSamba root directory\nCreated: Dockerfile:15\nPermissions: 777"]
SymLink["/samba-share/remote\nSymbolic link\nCreated: Dockerfile:30"]
end
RemoteFiles --> RemoteDir
RemoteDir -->|ln -s /remote /samba-share/remote| SymLink
SymLink -->|part of| SambaDir
Filesystem Integration Layer
The integration between the SSHFS mount at /remote and the Samba share at /samba-share occurs through a symbolic link, created during container build.
Symbolic Link Integration
Diagram: Symbolic Link Integration Point
The symbolic link is created during the Docker image build process Dockerfile30:
This creates /samba-share/remote as a symbolic link pointing to /remote. The symbolic link is permanent—created once during image build and persisting across container restarts.
Directory Creation Sequence
The directories and symbolic link are created in this order during build:
/remotedirectory created Dockerfile12/samba-sharedirectory created Dockerfile15/samba-sharepermissions set to777Dockerfile21- Symbolic link
/samba-share/remote → /remotecreated Dockerfile30
The 777 permissions on /samba-share Dockerfile21 ensure that:
- The Samba service can access the directory
- Files created through SMB inherit permissive access
- The symbolic link remains accessible to all processes
Sources: Dockerfile:12, Dockerfile:15, Dockerfile:21, Dockerfile:30
graph TB
subgraph "Filesystem Layer"
SambaShare["/samba-share\nServed directory"]
SymLink["/samba-share/remote\n→ /remote"]
RemoteMount["/remote\nSSHFS mount"]
end
subgraph "Samba Configuration"
ShareDef["[SSHFS Share]\nsmb.conf:10-19"]
PathConf["path = /samba-share\nsmb.conf:11"]
GuestConf["guest ok = yes\nsmb.conf:13"]
ForceUser["force user = sshuser\nsmb.conf:19"]
WritableConf["writable = yes\nsmb.conf:12"]
end
subgraph "SMB Protocol"
SMBPorts["Ports 139/445\nDockerfile:27"]
SMBClient["macOS SMB Client"]
end
ShareDef --> PathConf
ShareDef --> GuestConf
ShareDef --> ForceUser
ShareDef --> WritableConf
PathConf --> SambaShare
SambaShare --> SymLink
SymLink -.->|follows link| RemoteMount
ForceUser -.->|all operations as| sshuser["sshuser\nUID 1000"]
sshuser -.->|matches| RemoteMount
ShareDef --> SMBPorts
SMBPorts --> SMBClient
FUSE to SMB Translation
The final translation occurs when Samba serves the /samba-share directory (which contains the symbolic link to the SSHFS mount) over the SMB protocol.
Samba Configuration for Protocol Bridge
Diagram: Samba Configuration for Protocol Translation
The Samba share is defined in smb.conf smb.conf:10-19:
[SSHFS Share]
path = /samba-share
writable = yes
guest ok = yes
guest only = yes
read only = no
browseable = yes
create mask = 0777
directory mask = 0777
force user = sshuser
Critical Configuration Elements
| Configuration | Purpose | Impact on Protocol Translation |
|---|---|---|
path = /samba-share | Sets root directory for SMB share | Exposes directory containing symbolic link to SSHFS mount |
force user = sshuser | Forces all file operations to run as sshuser | Matches UID/GID from SSHFS mount options, enabling write access |
guest ok = yes | Allows guest authentication | Simplifies macOS client connection, avoiding credential management |
writable = yes | Enables write operations | Allows bidirectional protocol translation (read and write) |
The force user = sshuser smb.conf19 is critical because:
- SSHFS mounts files with
uid=1000,gid=1000(matchingsshuser) README.md49 - Samba operations run as
sshuser, matching the file ownership - This alignment enables write access through the protocol bridge
Sources: smb.conf:10-19, README.md:49
sequenceDiagram
participant Finder as "macOS Finder"
participant SMB as "Samba Service\n(smbd)"
participant FS as "Filesystem Layer\n(/samba-share)"
participant Link as "Symbolic Link\n(/samba-share/remote)"
participant Mount as "FUSE Mount\n(/remote)"
participant SSHFS as "SSHFS Process"
participant Remote as "Remote SSH Server"
Finder->>SMB: SMB read request
SMB->>FS: Access /samba-share/remote/file.txt\n(as sshuser)
FS->>Link: Follow symbolic link
Link->>Mount: Resolve to /remote/file.txt
Mount->>SSHFS: FUSE read operation
SSHFS->>Remote: SSH file read
Remote-->>SSHFS: File contents
SSHFS-->>Mount: FUSE response
Mount-->>Link: File data
Link-->>FS: File data
FS-->>SMB: File data (as sshuser)
SMB-->>Finder: SMB response
Bidirectional Data Flow
The protocol translation supports both read and write operations, with data flowing through the complete translation stack in both directions.
Read Operation Flow
Diagram: Read Operation Protocol Translation Flow
Write Operation Flow
Diagram: Write Operation Protocol Translation Flow
Write Access Requirements
Write operations succeed because of aligned user identity across the translation layers:
- SSHFS Mount: Files owned by
uid=1000,gid=1000README.md49 - Samba Configuration:
force user = sshusersmb.conf19 - User Account:
sshuserhas UID 1000, GID 1000 Dockerfile9
Without this alignment, write operations fail. The uid=1000,gid=1000 mount options are noted as required for write access README.md53:
uid=1000,gid=1000 is needed for write-access (otherwise, it will be read-only if omitted)
Sources: README.md:49-53, smb.conf:19, Dockerfile:9
Protocol Translation Component Mapping
The following table maps high-level protocol translation concepts to specific code entities and configuration files:
| Translation Stage | Input Protocol | Output Interface | Code Entity | Configuration |
|---|---|---|---|---|
| Remote to SSH | File I/O | SSH protocol | user@host:path (mount target) | SSH credentials |
| SSH to FUSE | SSH protocol | Filesystem operations | sshfs command README.md49 | -o allow_other,uid=1000,gid=1000 |
| FUSE to Filesystem | FUSE operations | VFS operations | /remote mount point Dockerfile12 | user_allow_other Dockerfile24 |
| Filesystem Integration | VFS operations | VFS operations | /samba-share/remote symlink Dockerfile30 | chmod 777 /samba-share Dockerfile21 |
| Filesystem to SMB | VFS operations | SMB protocol | smbd process Dockerfile33 | smb.conf smb.conf:1-19 |
| SMB to Network | SMB protocol | TCP/IP | Ports 139/445 Dockerfile27 | -p 127.0.0.1:139:139 -p 127.0.0.1:445:445 README.md31 |
Sources: README.md:31, README.md:49, Dockerfile:12, Dockerfile:21, Dockerfile:24, Dockerfile:27, Dockerfile:30, Dockerfile:33, smb.conf:1-19
graph TB
subgraph "FUSE Configuration"
FuseConf["user_allow_other\n/etc/fuse.conf\nDockerfile:24"]
end
subgraph "SSHFS Mount Options"
AllowOther["allow_other\nREADME.md:49"]
UIDMap["uid=1000\nREADME.md:49"]
GIDMap["gid=1000\nREADME.md:49"]
end
subgraph "User Account"
SSHUser["sshuser\nUID 1000, GID 1000\nDockerfile:9"]
end
subgraph "Samba Configuration"
ForceUser["force user = sshuser\nsmb.conf:19"]
GuestOK["guest ok = yes\nsmb.conf:13"]
Writable["writable = yes\nsmb.conf:12"]
end
subgraph "Filesystem"
SharePerms["chmod 777 /samba-share\nDockerfile:21"]
SymLink["ln -s /remote /samba-share/remote\nDockerfile:30"]
end
FuseConf -->|enables| AllowOther
AllowOther -->|allows cross-user access| ForceUser
UIDMap -->|sets ownership| SSHUser
GIDMap -->|sets ownership| SSHUser
SSHUser -->|identity matches| ForceUser
ForceUser -->|runs operations as| SharePerms
SharePerms -->|writable by| SSHUser
GuestOK -->|simplifies auth| ForceUser
Writable -->|enables writes| ForceUser
SymLink -->|integrates| SharePerms
Critical Configuration Dependencies
The protocol translation requires specific configuration elements that work together to enable cross-protocol access:
Configuration Dependency Graph
Diagram: Configuration Dependency Graph for Protocol Translation
All these configuration elements must be correctly set for the protocol translation to function:
user_allow_otherin fuse.conf enables theallow_othermount optionallow_othermount option allows Samba to access SSHFS-mounted filesuid=1000,gid=1000mount options set file ownership to matchsshuserforce user = sshuserin Samba config aligns with SSHFS file ownershipchmod 777 /samba-shareensures permissive access to the share directory- Symbolic link connects the Samba share to the SSHFS mount point
If any single element is missing or misconfigured, the protocol translation fails—typically resulting in permission denied errors or read-only access.
Sources: Dockerfile:9, Dockerfile:21, Dockerfile:24, Dockerfile:30, README.md:49, smb.conf:12-13, smb.conf:19