Documentation
¶
Overview ¶
Package sftpsrv serves SFTP: filex, seen by every client that already knows how to talk to a file server over SSH.
Why this exists ¶
The rule for this whole family of work is symmetry: filex can CONNECT to an SFTP server (internal/storage/drivers/sftp), so it must be connectable AS one. Somebody with a scanner, a backup job or a WinSCP habit should be able to point it at filex and have it work — with the same identity, the same permissions, the same trash and the same audit trail as the web app.
The fact that shapes the design ¶
`sftp.NewRequestServer` fans READ and WRITE packets out to eight concurrent workers that SHARE one handle, with no per-handle lock in the library. Multiply that by OpenSSH's own defaults (64 outstanding requests, 32 KiB each) and ReadAt/WriteAt are called concurrently, at unordered offsets, for a perfectly sequential `put`. Since OpenSSH 9.0 `scp` speaks SFTP too, so this is every transfer rather than an edge case. The handles in file.go are written for that from the first line; the failure mode of not doing so is "works on a 1 MB test file, corrupts a 2 GB upload".
What it does not own ¶
Identity (internal/protocolauth), post-write bookkeeping (internal/protocolsync), the trash (internal/trash) and the read door (internal/filebody) are shared with every other protocol. This package owns the SSH transport, the SFTP verbs, and the mapping between them.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Fingerprint ¶
Fingerprint is the SHA256 fingerprint OpenSSH prints, without the `SHA256:` prefix — the form stored in the ssh_keys table.
func ParseAuthorizedKey ¶
ParseAuthorizedKey accepts one line in authorized_keys form and returns its fingerprint and normalised wire form.
⚠ It refuses a line with anything before the key type (an authorized_keys OPTIONS field: `command=`, `permitopen=`, `no-pty`). Those options mean something to OpenSSH and nothing here; storing a key with options filex silently ignores would hand somebody a restriction they believe is enforced.
Types ¶
type Config ¶
type Config struct {
// Enabled — FILEX_SFTP kill switch. OFF by default, unlike /dav and S3:
// this one opens a TCP port of its own, and a port nobody asked for is not
// something to switch on for them.
Enabled bool
// Addr is the listen address. 2022 by convention (sftpgo and
// `rclone serve sftp` both use it; 2222 means "SSH in a container").
Addr string
// HostKeyDir is where the host keys live.
//
// ⚠ It must be a PERSISTENT directory. Regenerating host keys on a
// container rebuild gives every user REMOTE HOST IDENTIFICATION HAS
// CHANGED, which is indistinguishable from an attack and which most people
// resolve by deleting their known_hosts entry — training them to ignore the
// one warning that matters.
HostKeyDir string
// Banner is shown before authentication. Empty sends none.
Banner string
Store db.Store
// Auth is the shared credential door. Required.
Auth *protocolauth.Resolver
ACL *acl.Resolver
// E2EPolicy is who may encrypt (internal/e2epolicy): a write that CREATES
// an encrypted folder's key file or a `.fxe` asks it
// (protoperm.EncryptionAllowed). The router's own, handed over like ACL.
// nil: New builds one over Store (protoperm.EncryptionPolicy), so a
// lost wiring line never switches the rule off.
E2EPolicy *e2epolicy.Service
// Resolver returns the live driver for a storage id.
Resolver func(int64) (storage.Driver, error)
// Body is the one read door — it knows whether the bytes are on the driver
// or still in the staging area of an upload that has not landed.
Body *filebody.Resolver
// Quota is the per-user ceiling, checked before bytes land.
Quota *quota.Service
// Index and Thumbs feed the shared post-write bookkeeping.
Index *search.Index
Thumbs *thumb.Pipeline
// SpoolDir is where uploads are spooled before they are committed. Empty
// uses the OS temp dir.
SpoolDir string
// MaxSpool caps one upload's spool file. 0 uses defaultMaxSpool.
//
// ⚠ An uncapped spool on a 40 GB object fills the disk, and the disk it
// fills is the one the storages are on.
MaxSpool int64
// MultiTenant mirrors config.MultiTenant.
MultiTenant bool
}
Config wires the server to the shared services.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is a running SFTP endpoint.
func (*Server) ListenAndServe ¶
ListenAndServe starts accepting connections and blocks until Close.