sftpgo: port auth program to python
This commit is contained in:
parent
3c43fba878
commit
4c1a7fc910
|
@ -10,93 +10,12 @@
|
|||
# TODO: change umask so sftpgo-created files default to 644.
|
||||
# - it does indeed appear that the 600 is not something sftpgo is explicitly doing.
|
||||
|
||||
|
||||
{ config, lib, pkgs, sane-lib, ... }:
|
||||
let
|
||||
# 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)
|
||||
#
|
||||
# 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 {
|
||||
sftpgo_external_auth_hook = pkgs.static-nix-shell.mkPython3Bin {
|
||||
pname = "sftpgo_external_auth_hook";
|
||||
srcRoot = ./.;
|
||||
pkgs = [ "coreutils" ];
|
||||
};
|
||||
authProgram = pkgs.writeShellScript "sftpgo-auth-hook" ''
|
||||
${unwrappedAuthProgram}/bin/sftpgo_external_auth_hook ${authFailJson} ${authSuccessJson}
|
||||
'';
|
||||
in
|
||||
{
|
||||
# Client initiates a FTP "control connection" on port 21.
|
||||
|
@ -160,7 +79,7 @@ in
|
|||
};
|
||||
data_provider = {
|
||||
driver = "memory";
|
||||
external_auth_hook = "${authProgram}";
|
||||
external_auth_hook = "${sftpgo_external_auth_hook}/bin/sftpgo_external_auth_hook";
|
||||
# track_quota:
|
||||
# - 0: disable quota tracking
|
||||
# - 1: quota is updated on every upload/delete, even if user has no quota restriction
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p coreutils
|
||||
# vim: set filetype=bash :
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])"
|
||||
# vim: set filetype=python :
|
||||
#
|
||||
# available environment variables:
|
||||
# - SFTPGO_AUTHD_USERNAME
|
||||
|
@ -12,12 +12,114 @@
|
|||
# - SFTPGO_AUTHD_KEYBOARD_INTERACTIVE
|
||||
# - 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
|
||||
cat "$2"
|
||||
else
|
||||
cat "$1"
|
||||
fi
|
||||
authFail = dict(username="")
|
||||
|
||||
def mkAuthOk(username: str) -> dict:
|
||||
return dict(
|
||||
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()
|
||||
|
|
Loading…
Reference in New Issue
Block a user