Net::S...pServer(3pm)
okay bestie, pull up a chair π«Ά this is the manual page for Net::SFTP::SftpServer, and it lives in the Perl user-contributed docs (that's the 3pm section, iykyk). it's long, so we're doing this in two parts β this is part one π
π NAME π
Net::SFTP::SftpServer β a Perl implementation of the SFTP subsystem with user access controls β¨
πΈ SYNOPSIS πΈ
use Net::SFTP::SftpServer;
my $sftp = Net::SFTP::SftpServer->new();
$sftp->run();
π DESCRIPTION π
so basically: it's a Perl port of sftp-server from OpenSSH π¦ but it gives you access control on a per-user, per-command basis and better logging via syslog. iconic behaviour, honestly.
now the limitations compared to the OpenSSH implementation β read these, bestie, they matter:
- Only files and directories are dealt with β other types are not returned on
readdirπ - a virtual chroot is performed.
/is treated as the user's home directory from the client's perspective, and all file access to/will be in/<home_path>/<username>.home_pathis defined on object initialisation, not accessed from/etc/passwd. The script DOES NOT run underchrootβ this prevents it needing SUID to start. The virtual chroot is enforced by the objects and prevents operations outside the home area π - all symlinked files or directories are hidden and not accessible on request π
symlinkreturns permission denied. Please contact me if you need this functionality implementing πreadlinkreturns file does not exist. Please contact me if you need this functionality implementing π- setting of stats (
set_statorset_fstat) is disabled β the client will receive permission denied. Please contact me if you need this functionality implementing π - permissions for a file or dir are defaulted β the default is set on object initialisation β
β¨ USAGE β¨
Basic usage:
use Net::SFTP::SftpServer;
Import options:
:LOG - Import logging functions for use in callbacks
:ACTION - Import constants for Allow/Deny of actions
π Configuring syslog π
Syslog output mode must be configured in the use statement of the module, like this:
use Net::SFTP::SftpServer ( { log => 'local5' }, qw ( :LOG :ACTIONS ) );
Net::SFTP::SftpServer will default to using "daemon" β go check your system's syslog documentation for more deets π
π¦ Options for object initialisation π¦
debugβ log debug-level information. Default is0. (heads up bestie, this will create very large log files β use with caution π)homeβ filesystem location of user home directories. Default is/homefile_permsβ octal file permissions to force on creation of files. Default is0666, or the permissions specified by the file open command from the clientdir_permsβ octal dir permissions to force on creation of directories. Default is0777, or the permissions specified by themkdircommand from the clienton_file_sent,on_file_receivedβ references to callback functions to be called on a complete file sent or received. The function will be passed the full path and filename on the filesystem as a single argument π«Άuse_tmp_uploadβ use temporary upload filenames while a file is being uploaded. This lets a monitoring script know which files are in transit without having to watch file size π. It's done transparently to the user, and the file will be renamed to the original file name on close. The temporary extension is.SftpXFR.$$. Default is0max_file_sizeβ maximum file size (in bytes) which can be uploaded. Default is0(no limit)valid_filename_charβ array of valid characters for filenamesallow,denyβ actions allowed or denied β see "PERMISSIONS" for details. Default is to allow ALL πfake_okβ array of actions (see action constants in "PERMISSIONS") which will be given the responseSSH2_FX_OKinstead ofSSH2_FX_PERMISSION_DENIEDwhen denied by the abovedenyoptions. Default is[]log_action_supressβ array of actions to log quietly (logDetailβ syslog debug level); logs messages whenever this action is performed. Default is quiet forSSH2_FXP_READ,SSH2_FXP_WRITE,SSH2_FX_OPENDIR,SSH2_FXP_READDIR,SSH2_FXP_CLOSE,SSH2_FXP_STATSSH2_FXP_FSTATandSSH2_FXP_LSTAT. Override withlog_action, see belowlog_actionβ array of actions to log loudly (logGeneralβ syslog info level); logs messages whenever this action is performed π’log_all_statusβ log all status messages at info level. By defaultSSH2_FX_OKandSSH2_FX_EOFwill be logged at debug level
π PERMISSIONS π
ALLβ All actionsNET_SFTP_SYMLINKSβ Symlinks in paths to files (recommended deny to enforce chroot)NET_SFTP_RENAME_DIRβ Rename directories (recommended deny if also denyingSSH2_FXP_MKDIR)SSH2_FXP_OPENSSH2_FXP_CLOSESSH2_FXP_READSSH2_FXP_WRITESSH2_FXP_LSTATSSH2_FXP_STAT_VERSION_0SSH2_FXP_FSTATSSH2_FXP_SETSTATβ Automatically denied, not implemented in moduleSSH2_FXP_FSETSTATβ Automatically denied, not implemented in moduleSSH2_FXP_OPENDIRSSH2_FXP_READDIRSSH2_FXP_REMOVESSH2_FXP_MKDIRSSH2_FXP_RMDIRSSH2_FXP_STATSSH2_FXP_RENAMESSH2_FXP_READLINKβ Automatically denied, not implemented in moduleSSH2_FXP_SYMLINKβ Automatically denied, not implemented in module
π«Ά CALLBACKS π«Ά
Callback functions can be used to perform actions when files are sent or received β for example, moving a fully downloaded file to a processed directory, or moving a received file into an input directory π¦. The callback is provided with a Net::SFTP::SftpServer::File object. This object allows access to the file within the virtual chroot environment. It will also return the full filename, or move the file to an explicit location on the full filesystem. Either of these actions will break the chroot, and the methods on the object will no longer be available π
The following methods are provided:
readβ read the data from the file, likeIO::File->read. Will open the file for reading if it's not already open, and read back the dataopenβ will open the file, likeIO::File->open, but the filename is not suppliedgetFilenameβ will return the filename as within the virtual chroot πgetFullFilenameBREAKCHROOTβ will return the full filename on the real filesystem and break the virtual chrootrenameBREAKCHROOTβ takes a single argument of the new filename, will rename the file to that location and break the virtual chroot
π LOGGING π
If :LOG is used when including Net::SFTP::SftpServer, the following logging functions will be available:
logError - syslog with a log level of error
logWarning - syslog with a log level of warning
logGeneral - syslog with a log level of info
logDetail - syslog with a log level of debug, unless object was created with debug=>1 then syslog with a level of info
π₯ HARDENED EXAMPLE SCRIPT π₯
the following example script shows how this module can be used to give far greater control over what's allowed on your SFTP server. main character security energy, fr fr π
this setup is aimed at admins who want to use SFTP uploads but do not wish to grant users a system account. You will also need to set both the SFTP subsystem and the user's shell to the sftp script, e.g. /usr/local/bin/sftp-server.pl
this configuration:
- Enforces that users can only access the sftp script, not an ssh shell π
- Chroots them into their home directory in
/var/upload/sftp - Sets all file permissions to
0660and does not permit users to change them - Does not allow symlinks, making directories or renaming directories, but allows all other normal actions
- Has a max upload filesize of
200Mb - Has a script memory limit of
100Mbfor safety - Will log actions by user
sftptestin debug mode - Will only allow alphanumeric plus
_,.and-in filenames - Will call
ActionOnSentandActionOnReceivedrespectively when files have been sent or received
#!/usr/local/bin/perl
use strict;
use warnings;
use Net::SFTP::SftpServer ( { log => 'local5' }, qw ( :LOG :ACTIONS ) );
use BSD::Resource; # for setrlimit
use constant DEBUG_USER => {
SFTPTEST => 1,
};
# Security - make sure we have started this as sftp not ssh
unless ( scalar @ARGV == 2 and
$ARGV[0] eq '-c' and
($ARGV[1] eq '/usr/local/bin/sftp-server.pl') ){
logError "SFTP connection attempted for application $ARGV[0] - exiting";
print "\n\rYou do not have permission to login interactively to this host.\n\r\n\rPlease contact the system administrator if you believe this to be a configuration error.\n\r";
exit 1;
}
my $MEMLIMIT = 100 * 1024 * 1024; # 100 Mb
# hard limits on process memory usage;
setrlimit( RLIMIT_RSS, $MEMLIMIT, $MEMLIMIT );
setrlimit( RLIMIT_VMEM, $MEMLIMIT, $MEMLIMIT );
my $debug = (defined DEBUG_USER->{uc(getpwuid($>))} and DEBUG_USER->{uc(getpwuid($>))}) ? 1 : 0;
my $sftp = Net::SFTP::SftpServer->new(
debug => $debug,
home => '/var/upload/sftp',
file_perms => 0660,
on_file_sent => \&ActionOnSent,
on_file_received => \&ActionOnReceived,
use_tmp_upload => 1,
max_file_size => 200 * 1024 * 1024,
valid_filename_char => [ 'a' .. 'z', 'A' .. 'Z', '0' .. '9', '_', '.', '-' ],
deny => ALL,
allow => [ (
SSH2_FXP_OPEN,
SSH2_FXP_CLOSE,
SSH2_FXP_READ,
SSH2_FXP_WRITE,
SSH2_FXP_LSTAT,
SSH2_FXP_STAT_VERSION_0,
SSH2_FXP_FSTAT,
SSH2_FXP_OPENDIR,
SSH2_FXP_READDIR,
SSH2_FXP_REMOVE,
SSH2_FXP_STAT,
SSH2_FXP_RENAME,
)],
fake_ok => [ (
SSH2_FXP_SETSTAT,
SSH2_FXP_FSETSTAT,
)],
);
$sftp->run();
sub ActionOnSent {
my $fileObject = shift;
## Do Stuff
}
sub ActionOnReceived {
my $fileObject = shift;
## Do Stuff
}
π§ DEPENDENCIES π§
Stat::lsMode
Fcntl
POSIX
Sys::Syslog
Errno
π SEE ALSO π
- Sftp protocol β <http://www.openssh.org/txt/draft-ietf-secsh-filexfer-02.txt>
β¨ AUTHOR β¨
Okay so who do we have to thank for this absolute masterpiece? π«Ά
- Simon Day, Pirum Systems Ltd
cpan <at> simonday.infoβ yes bestie, that's literally the contact, hit them up π
π COPYRIGHT AND LICENSE π
ngl, this part is the legal bit, and you should actually read it. No skimming. π
This whole thing is based on sftp-server.c Copyright (c) 2000-2004 Markus Friedl. All rights reserved. β¨
Then it got ported to Perl and extended by Simon Day, Copyright (c) 2009 Pirum Systems Ltd. All rights reserved. π
And here's the permission situation, fr fr: Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. That's it, that's the deal. Slay. π
β οΈ THE BIG LOUD WARRANTY DISCLAIMER β οΈ
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. Not me exaggerating β this is literally all-caps in the source, so you know it's serious. π
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Translation for the girlies: it's free, it's generous, but if it eats your data that's on you, bestie. π
and that's the whole page, you made it to the end!! ππ
This one came from perl v5.36.0 and got dated 2022-12-04, all filed under Net::S...pServer(3pm) in the User Contributed Perl Documentation. Go forth and sftp responsibly. π¦πΈ
π¦ where this page comes from
- debian bookworm: libnet-sftp-sftpserver-perl 1.1.0-7 Β· MIT~OldStyle-with-Disclaim-2
- debian trixie: libnet-sftp-sftpserver-perl 1.1.0-7 Β· MIT~OldStyle-with-Disclaim-2
- ubuntu noble: libnet-sftp-sftpserver-perl 1.1.0-7 Β· MIT~OldStyle-with-Disclaim-2
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms