The Problem
Filestash's passthrough auth returns "Invalid account" on login. The backend is configured to forward credentials to the local SSH server (OpenSSH on the same machine). SSH works fine when tested directly. Password auth is enabled.
SYST INFO [auth] status=failed user=myuser backend=sftp err=Invalid+account
HTTP 307 POST 4.7ms /api/session/auth/?label=sftp
Environment:
- Filestash running in Docker (bridge network
172.20.0.0/16) - SSH server on host:
192.168.1.x:22, listening on0.0.0.0:22 - UFW active on host
-
identity_provider.type = passthrough,attribute_mapping.related_backend = sftp
Diagnostic Step 1: Read the Response Time
Before touching config, the response time is the first clue.
| Time | What it means |
|---|---|
| < 5ms | Pre-connection failure — params missing/invalid, DNS failure |
| ~5–50ms | TCP connection refused (immediate RST) |
| 100–500ms | SSH handshake + auth failure (wrong password) |
| 3000ms+ | TCP timeout (UFW DROP, no route) |
4.7ms → we never attempted a TCP connection. The error is thrown before ssh.Dial().
In Filestash's Go SFTP backend (plg_backend_sftp.go), ErrNotValid ("Invalid account") is returned whenever:
-
hostnameis empty after decryption -
ssh.Dial()fails for any reason
At 4.7ms, it's case 1.
Diagnostic Step 2: Test Container Network Access
Even though the fast response time ruled out a network issue causing this failure, understanding the network matters for the full fix:
# From inside the Filestash container
docker exec filestash curl --connect-timeout 3 telnet://192.168.1.x:22
# → Timed out after 3002ms
docker exec filestash curl --connect-timeout 3 telnet://172.20.0.1:22
# → Timed out after 3002ms
UFW is blocking Docker's subnet from SSH. This won't cause "Invalid account" (too slow), but it means even after fixing the config, we'll need to address networking.
Diagnostic Step 3: Read the Actual Config
The SFTP params are AES-256-GCM encrypted in config.json. The admin API (GET /admin/api/config) returns decrypted values — but only if you're the browser.
Initial curl attempt:
curl -b admin_cookies.txt http://localhost:8334/admin/api/config
# → {"status": "error", "message": "Not Allowed"}
Check the Filestash JS source (lib/ajax.js):
opts.headers["X-Requested-With"] = "XmlHttpRequest";
All Filestash AJAX requests include this header. Without it, admin endpoints return 403.
curl -b admin_cookies.txt \
-H 'X-Requested-With: XmlHttpRequest' \
http://localhost:8334/admin/api/config
Now the decrypted config returns. The SFTP params:
{
"sftp": {
"type": "sftp",
"hostname": "myserver",
"username": "{{ .user }}",
"password": "{{ .password }}",
"path": "192.168.1.x",
"port": "22"
}
}
hostname = "myserver" (machine hostname, not resolvable inside Docker container)
path = "192.168.1.x" (IP address, entered in the wrong field)
The fields were swapped during browser configuration. DNS lookup for the machine hostname fails instantly inside the container → "Invalid account" at 4.7ms. ✅ Confirmed.
The Fix
Two issues to address:
Fix 1: Correct the SFTP params
POST the corrected config via admin API:
import json, subprocess
# Read current admin config
r = subprocess.run([
'curl', '-s', '-b', 'admin_cookies.txt',
'-H', 'X-Requested-With: XmlHttpRequest',
'http://localhost:8334/admin/api/config'
], capture_output=True, text=True)
cfg = json.loads(r.stdout)['result']
# strip formObj metadata wrappers (Filestash admin API format)
def strip_metadata(obj):
if isinstance(obj, dict):
if 'value' in obj and 'label' in obj:
return obj['value']
return {k: strip_metadata(v) for k, v in obj.items()}
return obj
clean = strip_metadata(cfg)
# Fix the swapped fields
sftp_params = json.loads(clean['middleware']['attribute_mapping']['params'])
sftp_params['sftp']['hostname'] = '127.0.0.1' # SSH server on the host
sftp_params['sftp']['path'] = '/home/myuser' # initial directory
clean['middleware']['attribute_mapping']['params'] = json.dumps(sftp_params)
# Restore connections (they're not in admin config, come from public config)
clean['connections'] = [{'type': 'sftp', 'label': 'sftp'}]
subprocess.run([
'curl', '-s', '-b', 'admin_cookies.txt',
'-H', 'X-Requested-With: XmlHttpRequest',
'-H', 'Content-Type: application/json',
'-X', 'POST', '-d', json.dumps(clean),
'http://localhost:8334/admin/api/config'
])
Note: Always restore
connectionswhen POSTing admin config — the admin config endpoint does not include connections (they come from the public/api/configendpoint). If you don't add them back, the login form disappears.
Fix 2: Fix container networking
With hostname = 127.0.0.1, the container needs to reach the host's SSH. Two options:
Option A — UFW rule (requires sudo):
sudo ufw allow from 172.25.0.0/16 to any port 22
Option B — Host networking (no sudo, cleaner):
# docker-compose.yml
services:
filestash:
image: filestash/filestash
container_name: filestash
network_mode: host # remove ports: mapping, not needed with host networking
volumes:
- filestash_data:/app/data/state
restart: unless-stopped
volumes:
filestash_data:
cd ~/filestash && docker compose down && docker compose up -d
With host networking, the Filestash process runs on the host's network stack. 127.0.0.1:22 is the host's SSH server. No UFW rules needed.
Verification
After the fix:
SYST INFO [auth] status=failed user=myuser backend=sftp err=Invalid+account
HTTP 307 POST 53ms /api/session/auth/?label=sftp
53ms — actual SSH connection attempted (test was with wrong password, so auth failed, but the connection was made). With correct credentials:
SYST INFO [auth] status=ok user=myuser backend=sftp
HTTP 302 POST 91ms /api/session/auth/?label=sftp
Summary
| Issue | Symptom | Cause | Fix |
|---|---|---|---|
| Hostname/path swapped | "Invalid account" in 4.7ms | Browser form filled incorrectly — hostname="myserver", path="192.168.1.x"
|
Swap: hostname="127.0.0.1", path="/home/myuser"
|
| UFW blocks Docker subnet | TCP timeout (3s) to host SSH | UFW blocks 172.25.0.0/16 → port 22
|
Switch to network_mode: host
|
Key debugging insight: Response time tells you where the failure is. Sub-5ms = before any network I/O. This rules out 90% of the usual suspects and points directly at config/param issues.
Checklist for Filestash SFTP Passthrough Auth
- [ ]
middleware.identity_provider.type = "passthrough" - [ ]
middleware.attribute_mapping.related_backend = "sftp" - [ ]
attribute_mapping.paramscontains correcthostname(not the machine's hostname, not the path) - [ ]
connectionsarray is present in config (check/api/configpublic endpoint) - [ ] Container can reach
hostname:portvia TCP (test withcurl telnet://host:portfrom inside container) - [ ] SSH server has
PasswordAuthentication yes(or not explicitly disabled) - [ ] When calling admin API from scripts: include
X-Requested-With: XmlHttpRequestheader
Filestash: https://www.filestash.app — self-hosted file browser with SFTP, S3, FTP, and more.
Top comments (0)