diff --git a/cmd/portable.go b/cmd/portable.go index b03ff350..9055eb93 100644 --- a/cmd/portable.go +++ b/cmd/portable.go @@ -5,7 +5,9 @@ import ( "fmt" "io/ioutil" "os" + "path" "path/filepath" + "strings" "github.com/drakkan/sftpgo/dataprovider" "github.com/drakkan/sftpgo/service" @@ -25,6 +27,8 @@ var ( portablePublicKeys []string portablePermissions []string portableSSHCommands []string + portableAllowedExtensions []string + portableDeniedExtensions []string portableFsProvider int portableS3Bucket string portableS3Region string @@ -113,6 +117,9 @@ Please take a look at the usage below to customize the serving parameters`, KeyPrefix: portableGCSKeyPrefix, }, }, + Filters: dataprovider.UserFilters{ + FileExtensions: parseFileExtensionsFilters(), + }, }, } if err := service.StartPortableMode(portableSFTPDPort, portableSSHCommands, portableAdvertiseService, @@ -135,6 +142,10 @@ func init() { portableCmd.Flags().StringSliceVarP(&portablePublicKeys, "public-key", "k", []string{}, "") portableCmd.Flags().StringSliceVarP(&portablePermissions, "permissions", "g", []string{"list", "download"}, "User's permissions. \"*\" means any permission") + portableCmd.Flags().StringArrayVar(&portableAllowedExtensions, "allowed-extensions", []string{}, + "Allowed file extensions case insensitive. The format is /dir::ext1,ext2. For example: \"/somedir::.jpg,.png\"") + portableCmd.Flags().StringArrayVar(&portableDeniedExtensions, "denied-extensions", []string{}, + "Denied file extensions case insensitive. The format is /dir::ext1,ext2. For example: \"/somedir::.jpg,.png\"") portableCmd.Flags().BoolVarP(&portableAdvertiseService, "advertise-service", "S", true, "Advertise SFTP service using multicast DNS") portableCmd.Flags().BoolVarP(&portableAdvertiseCredentials, "advertise-credentials", "C", false, @@ -158,3 +169,58 @@ func init() { "credentials file, 1 automatic") rootCmd.AddCommand(portableCmd) } + +func parseFileExtensionsFilters() []dataprovider.ExtensionsFilter { + var extensions []dataprovider.ExtensionsFilter + for _, val := range portableAllowedExtensions { + p, exts := getExtensionsFilterValues(strings.TrimSpace(val)) + if len(p) > 0 { + extensions = append(extensions, dataprovider.ExtensionsFilter{ + Path: path.Clean(p), + AllowedExtensions: exts, + DeniedExtensions: []string{}, + }) + } + } + for _, val := range portableDeniedExtensions { + p, exts := getExtensionsFilterValues(strings.TrimSpace(val)) + if len(p) > 0 { + found := false + for index, e := range extensions { + if path.Clean(e.Path) == path.Clean(p) { + extensions[index].DeniedExtensions = append(extensions[index].DeniedExtensions, exts...) + found = true + break + } + } + if !found { + extensions = append(extensions, dataprovider.ExtensionsFilter{ + Path: path.Clean(p), + AllowedExtensions: []string{}, + DeniedExtensions: exts, + }) + } + } + } + return extensions +} + +func getExtensionsFilterValues(value string) (string, []string) { + if strings.Contains(value, "::") { + dirExts := strings.Split(value, "::") + if len(dirExts) > 1 { + dir := strings.TrimSpace(dirExts[0]) + exts := []string{} + for _, e := range strings.Split(dirExts[1], ",") { + cleanedExt := strings.TrimSpace(e) + if len(cleanedExt) > 0 { + exts = append(exts, cleanedExt) + } + } + if len(dir) > 0 && len(exts) > 0 { + return dir, exts + } + } + } + return "", nil +} diff --git a/docs/account.md b/docs/account.md index 12c7d6c8..e80afa87 100644 --- a/docs/account.md +++ b/docs/account.md @@ -57,5 +57,5 @@ These properties are stored inside the data provider. If you want to use your existing accounts, you have these options: - If your accounts are aleady stored inside a supported database, you can create a database view. Since a view is read only, you have to disable user management and quota tracking so SFTPGo will never try to write to the view -- you can import your users inside SFTPGo. Take a look at [sftpgo_api_cli.py](../scripts/README.md "SFTPGo api cli script"), it can convert and import users from Linux system users and Pure-FTPd/ProFTPD virtual users +- you can import your users inside SFTPGo. Take a look at [sftpgo_api_cli.py](../scripts#convert-users-from-other-stores "SFTPGo API CLI script"), it can convert and import users from Linux system users and Pure-FTPd/ProFTPD virtual users - you can use an external authentication program diff --git a/docs/portable-mode.md b/docs/portable-mode.md index e14399c6..d013f868 100644 --- a/docs/portable-mode.md +++ b/docs/portable-mode.md @@ -14,30 +14,33 @@ Usage: sftpgo portable [flags] Flags: - -C, --advertise-credentials If the SFTP service is advertised via multicast DNS, this flag allows to put username/password inside the advertised TXT record - -S, --advertise-service Advertise SFTP service using multicast DNS (default true) - -d, --directory string Path to the directory to serve. This can be an absolute path or a path relative to the current directory (default ".") - -f, --fs-provider int 0 means local filesystem, 1 Amazon S3 compatible, 2 Google Cloud Storage - --gcs-automatic-credentials int 0 means explicit credentials using a JSON credentials file, 1 automatic (default 1) + -C, --advertise-credentials If the SFTP service is advertised via multicast DNS, this flag allows to put username/password inside the advertised TXT record + -S, --advertise-service Advertise SFTP service using multicast DNS (default true) + --allowed-extensions stringArray Allowed file extensions case insensitive. The format is /dir::ext1,ext2. For example: "/somedir::.jpg,.png" + --denied-extensions stringArray Denied file extensions case insensitive. The format is /dir::ext1,ext2. For example: "/somedir::.jpg,.png" + -d, --directory string Path to the directory to serve. This can be an absolute path or a path relative to the current directory (default ".") + -f, --fs-provider int 0 means local filesystem, 1 Amazon S3 compatible, 2 Google Cloud Storage + --gcs-automatic-credentials int 0 means explicit credentials using a JSON credentials file, 1 automatic (default 1) --gcs-bucket string - --gcs-credentials-file string Google Cloud Storage JSON credentials file - --gcs-key-prefix string Allows to restrict access to the virtual folder identified by this prefix and its contents + --gcs-credentials-file string Google Cloud Storage JSON credentials file + --gcs-key-prefix string Allows to restrict access to the virtual folder identified by this prefix and its contents --gcs-storage-class string - -h, --help help for portable - -l, --log-file-path string Leave empty to disable logging - -p, --password string Leave empty to use an auto generated value - -g, --permissions strings User's permissions. "*" means any permission (default [list,download]) + -h, --help help for portable + -l, --log-file-path string Leave empty to disable logging + -p, --password string Leave empty to use an auto generated value + -g, --permissions strings User's permissions. "*" means any permission (default [list,download]) -k, --public-key strings --s3-access-key string --s3-access-secret string --s3-bucket string --s3-endpoint string - --s3-key-prefix string Allows to restrict access to the virtual folder identified by this prefix and its contents + --s3-key-prefix string Allows to restrict access to the virtual folder identified by this prefix and its contents --s3-region string --s3-storage-class string - -s, --sftpd-port int 0 means a random non privileged port - -c, --ssh-commands strings SSH commands to enable. "*" means any supported SSH command including scp (default [md5sum,sha1sum,cd,pwd]) - -u, --username string Leave empty to use an auto generated value + -s, --sftpd-port int 0 means a random non privileged port + -c, --ssh-commands strings SSH commands to enable. "*" means any supported SSH command including scp (default [md5sum,sha1sum,cd,pwd]) + -u, --username string Leave empty to use an auto generated value + ``` In portable mode, SFTPGo can advertise the SFTP service and, optionally, the credentials via multicast DNS, so there is a standard way to discover the service and to automatically connect to it. diff --git a/service/service.go b/service/service.go index 8e2676bc..6f8788a5 100644 --- a/service/service.go +++ b/service/service.go @@ -221,8 +221,9 @@ func (s *Service) StartPortableMode(sftpdPort int, enabledSSHCommands []string, }() logger.InfoToConsole("Portable mode ready, SFTP port: %v, user: %#v, password: %#v, public keys: %v, directory: %#v, "+ - "permissions: %v, enabled ssh commands: %v", sftpdConf.BindPort, s.PortableUser.Username, s.PortableUser.Password, - s.PortableUser.PublicKeys, s.getPortableDirToServe(), s.PortableUser.Permissions, sftpdConf.EnabledSSHCommands) + "permissions: %+v, enabled ssh commands: %v file extensions filters: %+v", sftpdConf.BindPort, s.PortableUser.Username, + s.PortableUser.Password, s.PortableUser.PublicKeys, s.getPortableDirToServe(), s.PortableUser.Permissions, + sftpdConf.EnabledSSHCommands, s.PortableUser.Filters.FileExtensions) return nil }