sftpgo: port auth program to python
This commit is contained in:
@@ -10,93 +10,12 @@
|
|||||||
# TODO: change umask so sftpgo-created files default to 644.
|
# TODO: change umask so sftpgo-created files default to 644.
|
||||||
# - it does indeed appear that the 600 is not something sftpgo is explicitly doing.
|
# - it does indeed appear that the 600 is not something sftpgo is explicitly doing.
|
||||||
|
|
||||||
|
|
||||||
{ config, lib, pkgs, sane-lib, ... }:
|
{ config, lib, pkgs, sane-lib, ... }:
|
||||||
let
|
let
|
||||||
# user permissions:
|
sftpgo_external_auth_hook = pkgs.static-nix-shell.mkPython3Bin {
|
||||||
# - see <repo:drakkan/sftpgo:internal/dataprovider/user.go>
|
|
||||||
# - "*" = grant all permissions
|
|
||||||
# - read-only perms:
|
|
||||||
# - "list" = list files and directories
|
|
||||||
# - "download"
|
|
||||||
# - rw perms:
|
|
||||||
# - "upload"
|
|
||||||
# - "overwrite" = allow uploads to replace existing files
|
|
||||||
# - "delete" = delete files and directories
|
|
||||||
# - "delete_files"
|
|
||||||
# - "delete_dirs"
|
|
||||||
# - "rename" = rename files and directories
|
|
||||||
# - "rename_files"
|
|
||||||
# - "rename_dirs"
|
|
||||||
# - "create_dirs"
|
|
||||||
# - "create_symlinks"
|
|
||||||
# - "chmod"
|
|
||||||
# - "chown"
|
|
||||||
# - "chtimes" = change atime/mtime (access and modification times)
|
|
||||||
#
|
|
||||||
# home_dir:
|
|
||||||
# - it seems (empirically) that a user can't cd above their home directory.
|
|
||||||
# though i don't have a reference for that in the docs.
|
|
||||||
authResponseSuccess = {
|
|
||||||
status = 1;
|
|
||||||
username = "anonymous";
|
|
||||||
expiration_date = 0;
|
|
||||||
home_dir = "/var/export";
|
|
||||||
# uid/gid 0 means to inherit sftpgo uid.
|
|
||||||
# - i.e. users can't read files which Linux user `sftpgo` can't read
|
|
||||||
# - uploaded files belong to Linux user `sftpgo`
|
|
||||||
# other uid/gid values aren't possible for localfs backend, unless i let sftpgo use `sudo`.
|
|
||||||
uid = 0;
|
|
||||||
gid = 0;
|
|
||||||
# uid = 65534;
|
|
||||||
# gid = 65534;
|
|
||||||
max_sessions = 0;
|
|
||||||
# quota_*: 0 means to not use SFTP's quota system
|
|
||||||
quota_size = 0;
|
|
||||||
quota_files = 0;
|
|
||||||
permissions = {
|
|
||||||
"/" = [ "list" "download" ];
|
|
||||||
"/playground" = [
|
|
||||||
# read-only:
|
|
||||||
"list"
|
|
||||||
"download"
|
|
||||||
# write:
|
|
||||||
"upload"
|
|
||||||
"overwrite"
|
|
||||||
"delete"
|
|
||||||
"rename"
|
|
||||||
"create_dirs"
|
|
||||||
"create_symlinks"
|
|
||||||
# intentionally omitted:
|
|
||||||
# "chmod"
|
|
||||||
# "chown"
|
|
||||||
# "chtimes"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
upload_bandwidth = 0;
|
|
||||||
download_bandwidth = 0;
|
|
||||||
filters = {
|
|
||||||
allowed_ip = [];
|
|
||||||
denied_ip = [];
|
|
||||||
};
|
|
||||||
public_keys = [];
|
|
||||||
# other fields:
|
|
||||||
# ? groups
|
|
||||||
# ? virtual_folders
|
|
||||||
};
|
|
||||||
authResponseFail = {
|
|
||||||
username = "";
|
|
||||||
};
|
|
||||||
authSuccessJson = pkgs.writeText "sftp-auth-success.json" (builtins.toJSON authResponseSuccess);
|
|
||||||
authFailJson = pkgs.writeText "sftp-auth-fail.json" (builtins.toJSON authResponseFail);
|
|
||||||
unwrappedAuthProgram = pkgs.static-nix-shell.mkBash {
|
|
||||||
pname = "sftpgo_external_auth_hook";
|
pname = "sftpgo_external_auth_hook";
|
||||||
srcRoot = ./.;
|
srcRoot = ./.;
|
||||||
pkgs = [ "coreutils" ];
|
|
||||||
};
|
};
|
||||||
authProgram = pkgs.writeShellScript "sftpgo-auth-hook" ''
|
|
||||||
${unwrappedAuthProgram}/bin/sftpgo_external_auth_hook ${authFailJson} ${authSuccessJson}
|
|
||||||
'';
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# Client initiates a FTP "control connection" on port 21.
|
# Client initiates a FTP "control connection" on port 21.
|
||||||
@@ -160,7 +79,7 @@ in
|
|||||||
};
|
};
|
||||||
data_provider = {
|
data_provider = {
|
||||||
driver = "memory";
|
driver = "memory";
|
||||||
external_auth_hook = "${authProgram}";
|
external_auth_hook = "${sftpgo_external_auth_hook}/bin/sftpgo_external_auth_hook";
|
||||||
# track_quota:
|
# track_quota:
|
||||||
# - 0: disable quota tracking
|
# - 0: disable quota tracking
|
||||||
# - 1: quota is updated on every upload/delete, even if user has no quota restriction
|
# - 1: quota is updated on every upload/delete, even if user has no quota restriction
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env nix-shell
|
#!/usr/bin/env nix-shell
|
||||||
#!nix-shell -i bash -p coreutils
|
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])"
|
||||||
# vim: set filetype=bash :
|
# vim: set filetype=python :
|
||||||
#
|
#
|
||||||
# available environment variables:
|
# available environment variables:
|
||||||
# - SFTPGO_AUTHD_USERNAME
|
# - SFTPGO_AUTHD_USERNAME
|
||||||
@@ -12,12 +12,114 @@
|
|||||||
# - SFTPGO_AUTHD_KEYBOARD_INTERACTIVE
|
# - SFTPGO_AUTHD_KEYBOARD_INTERACTIVE
|
||||||
# - SFTPGO_AUTHD_TLS_CERT
|
# - SFTPGO_AUTHD_TLS_CERT
|
||||||
#
|
#
|
||||||
|
# user permissions:
|
||||||
|
# - see <repo:drakkan/sftpgo:internal/dataprovider/user.go>
|
||||||
|
# - "*" = grant all permissions
|
||||||
|
# - read-only perms:
|
||||||
|
# - "list" = list files and directories
|
||||||
|
# - "download"
|
||||||
|
# - rw perms:
|
||||||
|
# - "upload"
|
||||||
|
# - "overwrite" = allow uploads to replace existing files
|
||||||
|
# - "delete" = delete files and directories
|
||||||
|
# - "delete_files"
|
||||||
|
# - "delete_dirs"
|
||||||
|
# - "rename" = rename files and directories
|
||||||
|
# - "rename_files"
|
||||||
|
# - "rename_dirs"
|
||||||
|
# - "create_dirs"
|
||||||
|
# - "create_symlinks"
|
||||||
|
# - "chmod"
|
||||||
|
# - "chown"
|
||||||
|
# - "chtimes" = change atime/mtime (access and modification times)
|
||||||
#
|
#
|
||||||
# call with <script_name> /path/to/fail/response.json /path/to/success/response.json
|
# home_dir:
|
||||||
|
# - it seems (empirically) that a user can't cd above their home directory.
|
||||||
|
# though i don't have a reference for that in the docs.
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
if [ "$SFTPGO_AUTHD_USERNAME" = "anonymous" ]; then
|
authFail = dict(username="")
|
||||||
cat "$2"
|
|
||||||
else
|
def mkAuthOk(username: str) -> dict:
|
||||||
cat "$1"
|
return dict(
|
||||||
fi
|
status = 1,
|
||||||
|
username = username,
|
||||||
|
expiration_date = 0,
|
||||||
|
home_dir = "/var/export",
|
||||||
|
# uid/gid 0 means to inherit sftpgo uid.
|
||||||
|
# - i.e. users can't read files which Linux user `sftpgo` can't read
|
||||||
|
# - uploaded files belong to Linux user `sftpgo`
|
||||||
|
# other uid/gid values aren't possible for localfs backend, unless i let sftpgo use `sudo`.
|
||||||
|
uid = 0,
|
||||||
|
gid = 0,
|
||||||
|
# uid = 65534,
|
||||||
|
# gid = 65534,
|
||||||
|
max_sessions = 0,
|
||||||
|
# quota_*: 0 means to not use SFTP's quota system
|
||||||
|
quota_size = 0,
|
||||||
|
quota_files = 0,
|
||||||
|
permissions = {
|
||||||
|
"/": [ "list", "download" ],
|
||||||
|
"/playground": [
|
||||||
|
# read-only:
|
||||||
|
"list",
|
||||||
|
"download",
|
||||||
|
# write:
|
||||||
|
"upload",
|
||||||
|
"overwrite",
|
||||||
|
"delete",
|
||||||
|
"rename",
|
||||||
|
"create_dirs",
|
||||||
|
"create_symlinks",
|
||||||
|
# intentionally omitted:
|
||||||
|
# "chmod",
|
||||||
|
# "chown",
|
||||||
|
# "chtimes",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
upload_bandwidth = 0,
|
||||||
|
download_bandwidth = 0,
|
||||||
|
filters = dict(
|
||||||
|
allowed_ip = [],
|
||||||
|
denied_ip = [],
|
||||||
|
),
|
||||||
|
public_keys = [],
|
||||||
|
# other fields:
|
||||||
|
# ? groups
|
||||||
|
# ? virtual_folders
|
||||||
|
)
|
||||||
|
|
||||||
|
def isLan(ip: str) -> bool:
|
||||||
|
return ip.startswith("10.78.76.") \
|
||||||
|
or ip.startswith("10.78.77.") \
|
||||||
|
or ip.startswith("10.78.78.") \
|
||||||
|
or ip.startswith("10.78.79.")
|
||||||
|
|
||||||
|
def isWireguard(ip: str) -> bool:
|
||||||
|
return ip.startswith("10.0.10.")
|
||||||
|
|
||||||
|
def getAuthResponse(username: str, ip: str) -> dict:
|
||||||
|
"""
|
||||||
|
return a sftpgo auth response either denying the user or approving them
|
||||||
|
with a set of permissions.
|
||||||
|
"""
|
||||||
|
if isLan(ip):
|
||||||
|
if username == "anonymous":
|
||||||
|
# allow anonymous users on the LAN
|
||||||
|
return mkAuthOk("anonymous")
|
||||||
|
if isWireguard(ip):
|
||||||
|
# allow any user from wireguard
|
||||||
|
return mkAuthOk(username)
|
||||||
|
|
||||||
|
return authFail
|
||||||
|
|
||||||
|
def main():
|
||||||
|
username = os.environ.get("SFTPGO_AUTHD_USERNAME")
|
||||||
|
ip = os.environ.get("SFTPGO_AUTHD_IP")
|
||||||
|
resp = getAuthResponse(username, ip)
|
||||||
|
print(json.dumps(resp))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
Reference in New Issue
Block a user