add support for build tag to allow to disable some features

The following build tags are available:

- "nogcs", disable Google Cloud Storage backend
- "nos3", disable S3 Compabible Object Storage backends
- "nobolt", disable Bolt data provider
- "nomysql", disable MySQL data provider
- "nopgsql", disable PostgreSQL data provider
- "nosqlite", disable SQLite data provider
- "noportable", disable portable mode
This commit is contained in:
Nicola Murino 2020-05-23 11:58:05 +02:00
parent 15298b0409
commit ad53429cf1
26 changed files with 406 additions and 189 deletions

View file

@ -1,3 +1,5 @@
// +build !noportable
package cmd package cmd
import ( import (
@ -14,6 +16,7 @@ import (
"github.com/drakkan/sftpgo/dataprovider" "github.com/drakkan/sftpgo/dataprovider"
"github.com/drakkan/sftpgo/service" "github.com/drakkan/sftpgo/service"
"github.com/drakkan/sftpgo/sftpd" "github.com/drakkan/sftpgo/sftpd"
"github.com/drakkan/sftpgo/utils"
"github.com/drakkan/sftpgo/vfs" "github.com/drakkan/sftpgo/vfs"
) )
@ -138,6 +141,8 @@ Please take a look at the usage below to customize the serving parameters`,
) )
func init() { func init() {
utils.AddFeature("+portable")
portableCmd.Flags().StringVarP(&directoryToServe, "directory", "d", ".", portableCmd.Flags().StringVarP(&directoryToServe, "directory", "d", ".",
"Path to the directory to serve. This can be an absolute path or a path relative to the current directory") "Path to the directory to serve. This can be an absolute path or a path relative to the current directory")
portableCmd.Flags().IntVarP(&portableSFTPDPort, "sftpd-port", "s", 0, "0 means a random non privileged port") portableCmd.Flags().IntVarP(&portableSFTPDPort, "sftpd-port", "s", 0, "0 means a random non privileged port")

9
cmd/portable_disabled.go Normal file
View file

@ -0,0 +1,9 @@
// +build noportable
package cmd
import "github.com/drakkan/sftpgo/utils"
func init() {
utils.AddFeature("-portable")
}

View file

@ -63,7 +63,7 @@ func init() {
version := utils.GetAppVersion() version := utils.GetAppVersion()
rootCmd.Flags().BoolP("version", "v", false, "") rootCmd.Flags().BoolP("version", "v", false, "")
rootCmd.Version = version.GetVersionAsString() rootCmd.Version = version.GetVersionAsString()
rootCmd.SetVersionTemplate(`{{printf "SFTPGo version: "}}{{printf "%s" .Version}} rootCmd.SetVersionTemplate(`{{printf "SFTPGo "}}{{printf "%s" .Version}}
`) `)
} }

View file

@ -1,3 +1,5 @@
// +build !nobolt
package dataprovider package dataprovider
import ( import (
@ -52,6 +54,10 @@ type compatUserV2 struct {
Status int `json:"status"` Status int `json:"status"`
} }
func init() {
utils.AddFeature("+bolt")
}
func initializeBoltProvider(basePath string) error { func initializeBoltProvider(basePath string) error {
var err error var err error
logSender = fmt.Sprintf("dataprovider_%v", BoltDataProviderName) logSender = fmt.Sprintf("dataprovider_%v", BoltDataProviderName)

View file

@ -0,0 +1,17 @@
// +build nobolt
package dataprovider
import (
"errors"
"github.com/drakkan/sftpgo/utils"
)
func init() {
utils.AddFeature("-bolt")
}
func initializeBoltProvider(basePath string) error {
return errors.New("bolt disabled at build time")
}

View file

@ -1,3 +1,5 @@
// +build !nomysql
package dataprovider package dataprovider
import ( import (
@ -6,7 +8,11 @@ import (
"strings" "strings"
"time" "time"
// we import go-sql-driver/mysql here to be able to disable MySQL support using a build tag
_ "github.com/go-sql-driver/mysql"
"github.com/drakkan/sftpgo/logger" "github.com/drakkan/sftpgo/logger"
"github.com/drakkan/sftpgo/utils"
) )
const ( const (
@ -28,6 +34,10 @@ type MySQLProvider struct {
dbHandle *sql.DB dbHandle *sql.DB
} }
func init() {
utils.AddFeature("+mysql")
}
func initializeMySQLProvider() error { func initializeMySQLProvider() error {
var err error var err error
logSender = fmt.Sprintf("dataprovider_%v", MySQLDataProviderName) logSender = fmt.Sprintf("dataprovider_%v", MySQLDataProviderName)

View file

@ -0,0 +1,17 @@
// +build nomysql
package dataprovider
import (
"errors"
"github.com/drakkan/sftpgo/utils"
)
func init() {
utils.AddFeature("-mysql")
}
func initializeMySQLProvider() error {
return errors.New("MySQL disabled at build time")
}

View file

@ -1,3 +1,5 @@
// +build !nopgsql
package dataprovider package dataprovider
import ( import (
@ -5,7 +7,11 @@ import (
"fmt" "fmt"
"strings" "strings"
// we import lib/pq here to be able to disable PostgreSQL support using a build tag
_ "github.com/lib/pq"
"github.com/drakkan/sftpgo/logger" "github.com/drakkan/sftpgo/logger"
"github.com/drakkan/sftpgo/utils"
) )
const ( const (
@ -26,6 +32,10 @@ type PGSQLProvider struct {
dbHandle *sql.DB dbHandle *sql.DB
} }
func init() {
utils.AddFeature("+pgsql")
}
func initializePGSQLProvider() error { func initializePGSQLProvider() error {
var err error var err error
logSender = fmt.Sprintf("dataprovider_%v", PGSQLDataProviderName) logSender = fmt.Sprintf("dataprovider_%v", PGSQLDataProviderName)

View file

@ -0,0 +1,17 @@
// +build nopgsql
package dataprovider
import (
"errors"
"github.com/drakkan/sftpgo/utils"
)
func init() {
utils.AddFeature("-pgsql")
}
func initializePGSQLProvider() error {
return errors.New("PostgreSQL disabled at build time")
}

View file

@ -1,3 +1,5 @@
// +build !nosqlite
package dataprovider package dataprovider
import ( import (
@ -6,6 +8,9 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
// we import go-sqlite3 here to be able to disable SQLite support using a build tag
_ "github.com/mattn/go-sqlite3"
"github.com/drakkan/sftpgo/logger" "github.com/drakkan/sftpgo/logger"
"github.com/drakkan/sftpgo/utils" "github.com/drakkan/sftpgo/utils"
) )
@ -41,6 +46,10 @@ type SQLiteProvider struct {
dbHandle *sql.DB dbHandle *sql.DB
} }
func init() {
utils.AddFeature("+sqlite")
}
func initializeSQLiteProvider(basePath string) error { func initializeSQLiteProvider(basePath string) error {
var err error var err error
var connectionString string var connectionString string

View file

@ -0,0 +1,17 @@
// +build nosqlite
package dataprovider
import (
"errors"
"github.com/drakkan/sftpgo/utils"
)
func init() {
utils.AddFeature("-sqlite")
}
func initializeSQLiteProvider(basePath string) error {
return errors.New("SQLite disabled at build time")
}

View file

@ -4,19 +4,18 @@ RUN apk add --no-cache git gcc g++ ca-certificates \
&& go get -d github.com/drakkan/sftpgo && go get -d github.com/drakkan/sftpgo
WORKDIR /go/src/github.com/drakkan/sftpgo WORKDIR /go/src/github.com/drakkan/sftpgo
ARG TAG ARG TAG
ARG FEATURES
# Use --build-arg TAG=LATEST for latest tag. Use e.g. --build-arg TAG=0.9.6 for a specific tag/commit. Otherwise HEAD (master) is built. # Use --build-arg TAG=LATEST for latest tag. Use e.g. --build-arg TAG=0.9.6 for a specific tag/commit. Otherwise HEAD (master) is built.
RUN git checkout $(if [ "${TAG}" = LATEST ]; then echo `git rev-list --tags --max-count=1`; elif [ -n "${TAG}" ]; then echo "${TAG}"; else echo HEAD; fi) RUN git checkout $(if [ "${TAG}" = LATEST ]; then echo `git rev-list --tags --max-count=1`; elif [ -n "${TAG}" ]; then echo "${TAG}"; else echo HEAD; fi)
RUN go build -i -ldflags "-s -w -X github.com/drakkan/sftpgo/utils.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/utils.date=`date -u +%FT%TZ`" -o /go/bin/sftpgo RUN go build -i $(if [ -n "${FEATURES}" ]; then echo "-tags ${FEATURES}"; fi) -ldflags "-s -w -X github.com/drakkan/sftpgo/utils.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/utils.date=`date -u +%FT%TZ`" -o /go/bin/sftpgo
FROM alpine:latest FROM alpine:latest
RUN apk add --no-cache ca-certificates su-exec \ RUN apk add --no-cache ca-certificates su-exec \
&& mkdir -p /data /etc/sftpgo /srv/sftpgo/config /srv/sftpgo/web /srv/sftpgo/backups && mkdir -p /data /etc/sftpgo /srv/sftpgo/config /srv/sftpgo/web /srv/sftpgo/backups
# ca-certificates is needed for Cloud Storage Support and to expose the REST API over HTTPS. # git and rsync are optional, uncomment the next line to add support for them if needed.
# If you install git then ca-certificates will be automatically installed as dependency. #RUN apk add --no-cache git rsync
# git, rsync and ca-certificates are optional, uncomment the next line to add support for them if needed.
#RUN apk add --no-cache git rsync ca-certificates
COPY --from=builder /go/bin/sftpgo /bin/ COPY --from=builder /go/bin/sftpgo /bin/
COPY --from=builder /go/src/github.com/drakkan/sftpgo/sftpgo.json /etc/sftpgo/sftpgo.json COPY --from=builder /go/src/github.com/drakkan/sftpgo/sftpgo.json /etc/sftpgo/sftpgo.json

View file

@ -13,7 +13,10 @@ sudo groupadd -g 1003 sftpgrp && \
# Edit sftpgo.json as you need # Edit sftpgo.json as you need
# Get and build SFTPGo image (add --build-arg TAG=LATEST to build the latest tag or e.g. TAG=0.9.6 for a specific tag/commit). # Get and build SFTPGo image.
# Add --build-arg TAG=LATEST to build the latest tag or e.g. TAG=0.9.6 for a specific tag/commit.
# Add --build-arg FEATURES=<features to disable> to disable some feature.
# Please take a look at the [build from source](./../../../docs/build-from-source.md) documentation for the complete list of the features that can be disabled.
git clone https://github.com/drakkan/sftpgo.git && \ git clone https://github.com/drakkan/sftpgo.git && \
cd sftpgo && \ cd sftpgo && \
sudo docker build -t sftpgo docker/sftpgo/alpine/ sudo docker build -t sftpgo docker/sftpgo/alpine/

View file

@ -4,16 +4,18 @@ LABEL maintainer="nicola.murino@gmail.com"
RUN go get -d github.com/drakkan/sftpgo RUN go get -d github.com/drakkan/sftpgo
WORKDIR /go/src/github.com/drakkan/sftpgo WORKDIR /go/src/github.com/drakkan/sftpgo
ARG TAG ARG TAG
ARG FEATURES
# Use --build-arg TAG=LATEST for latest tag. Use e.g. --build-arg TAG=0.9.6 for a specific tag/commit. Otherwise HEAD (master) is built. # Use --build-arg TAG=LATEST for latest tag. Use e.g. --build-arg TAG=0.9.6 for a specific tag/commit. Otherwise HEAD (master) is built.
RUN git checkout $(if [ "${TAG}" = LATEST ]; then echo `git rev-list --tags --max-count=1`; elif [ -n "${TAG}" ]; then echo "${TAG}"; else echo HEAD; fi) RUN git checkout $(if [ "${TAG}" = LATEST ]; then echo `git rev-list --tags --max-count=1`; elif [ -n "${TAG}" ]; then echo "${TAG}"; else echo HEAD; fi)
RUN go build -i -ldflags "-s -w -X github.com/drakkan/sftpgo/utils.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/utils.date=`date -u +%FT%TZ`" -o sftpgo RUN go build -i $(if [ -n "${FEATURES}" ]; then echo "-tags ${FEATURES}"; fi) -ldflags "-s -w -X github.com/drakkan/sftpgo/utils.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/utils.date=`date -u +%FT%TZ`" -o sftpgo
# now define the run environment # now define the run environment
FROM debian:latest FROM debian:latest
# ca-certificates is needed for Cloud Storage Support and to expose the REST API over HTTPS. # ca-certificates is needed for Cloud Storage Support and to expose the REST API over HTTPS.
# If you install git then ca-certificates will be automatically installed as dependency. RUN apt-get update && apt-get install -y ca-certificates
# git, rsync and ca-certificates are optional, uncomment the next line to add support for them if needed.
# git and rsync are optional, uncomment the next line to add support for them if needed.
#RUN apt-get update && apt-get install -y git rsync ca-certificates #RUN apt-get update && apt-get install -y git rsync ca-certificates
ARG BASE_DIR=/app ARG BASE_DIR=/app

View file

@ -8,13 +8,22 @@ You can build the container image using `docker build`, for example:
docker build -t="drakkan/sftpgo" . docker build -t="drakkan/sftpgo" .
``` ```
This will build master of github.com/drakkan/sftpgo. To build the latest tag you can add `--build-arg TAG=LATEST` This will build master of github.com/drakkan/sftpgo.
and to build a specific tag/commit you can use for example `TAG=0.9.6`, like this:
To build the latest tag you can add `--build-arg TAG=LATEST` and to build a specific tag/commit you can use for example `TAG=0.9.6`, like this:
```bash ```bash
docker build -t="drakkan/sftpgo" --build-arg TAG=0.9.6 . docker build -t="drakkan/sftpgo" --build-arg TAG=0.9.6 .
``` ```
To disable some features you can add `--build-arg FEATURES=<features to disable>`. For example you can disable SQLite support like this:
```bash
docker build -t="drakkan/sftpgo" --build-arg FEATURES=nosqlite .
```
Please take a look at the [build from source](./../../../docs/build-from-source.md) documentation for the complete list of the features that can be disabled.
Now create the required folders on the host system, for example: Now create the required folders on the host system, for example:
```bash ```bash

View file

@ -8,13 +8,23 @@ go get -u github.com/drakkan/sftpgo
Make sure [Git](https://git-scm.com/downloads) is installed on your machine and in your system's `PATH`. Make sure [Git](https://git-scm.com/downloads) is installed on your machine and in your system's `PATH`.
SFTPGo depends on [go-sqlite3](https://github.com/mattn/go-sqlite3) which is a CGO package and so it requires a `C` compiler at build time. The following build tags are available to disable some features:
- `nogcs`, disable Google Cloud Storage backend
- `nos3`, disable S3 Compabible Object Storage backends
- `nobolt`, disable Bolt data provider
- `nomysql`, disable MySQL data provider
- `nopgsql`, disable PostgreSQL data provider
- `nosqlite`, disable SQLite data provider
- `noportable`, disable portable mode
If no build tag is specified all the features will be included.
The optional [SQLite driver](https://github.com/mattn/go-sqlite3 "go-sqlite3") is a `CGO` package and so it requires a `C` compiler at build time.
On Linux and macOS, a compiler is easy to install or already installed. On Windows, you need to download [MinGW-w64](https://sourceforge.net/projects/mingw-w64/files/) and build SFTPGo from its command prompt. On Linux and macOS, a compiler is easy to install or already installed. On Windows, you need to download [MinGW-w64](https://sourceforge.net/projects/mingw-w64/files/) and build SFTPGo from its command prompt.
The compiler is a build time only dependency. It is not required at runtime. The compiler is a build time only dependency. It is not required at runtime.
If you don't need SQLite, you can also get/build SFTPGo setting the environment variable `GCO_ENABLED` to 0. This way, SQLite support will be disabled and PostgreSQL, MySQL, bbolt and memory data providers will keep working. In this way, you don't need a `C` compiler for building.
Version info, such as git commit and build date, can be embedded setting the following string variables at build time: Version info, such as git commit and build date, can be embedded setting the following string variables at build time:
- `github.com/drakkan/sftpgo/utils.commit` - `github.com/drakkan/sftpgo/utils.commit`
@ -23,12 +33,12 @@ Version info, such as git commit and build date, can be embedded setting the fol
For example, you can build using the following command: For example, you can build using the following command:
```bash ```bash
go build -i -ldflags "-s -w -X github.com/drakkan/sftpgo/utils.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/utils.date=`date -u +%FT%TZ`" -o sftpgo go build -i -tags nogcs,nos3,nosqlite -ldflags "-s -w -X github.com/drakkan/sftpgo/utils.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/utils.date=`date -u +%FT%TZ`" -o sftpgo
``` ```
You should get a version that includes git commit and build date like this one: You should get a version that includes git commit, build date and available features like this one:
```bash ```bash
$ sftpgo -v $ ./sftpgo -v
SFTPGo version: 0.9.0-dev-90607d4-dirty-2019-08-08T19:28:36Z SFTPGo 0.9.6-dev-15298b0-dirty-2020-05-22T21:25:51Z -gcs -s3 +bolt +mysql +pgsql -sqlite +portable
``` ```

View file

@ -2,7 +2,7 @@ openapi: 3.0.1
info: info:
title: SFTPGo title: SFTPGo
description: 'SFTPGo REST API' description: 'SFTPGo REST API'
version: 1.8.5 version: 1.8.6
servers: servers:
- url: /api/v1 - url: /api/v1
@ -1278,6 +1278,11 @@ components:
type: string type: string
commit_hash: commit_hash:
type: string type: string
features:
type: array
items:
type: string
description: Features for the current build. Available features are "portable", "bolt", "mysql", "sqlite", "pgsql", "s3", "gcs". If a feature is available it has a "+" prefix, otherwise a "-" prefix
securitySchemes: securitySchemes:
BasicAuth: BasicAuth:
type: http type: http

View file

@ -3,13 +3,7 @@
// https://github.com/drakkan/sftpgo/blob/master/README.md // https://github.com/drakkan/sftpgo/blob/master/README.md
package main // import "github.com/drakkan/sftpgo" package main // import "github.com/drakkan/sftpgo"
import ( import "github.com/drakkan/sftpgo/cmd"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
"github.com/drakkan/sftpgo/cmd"
)
func main() { func main() {
cmd.Execute() cmd.Execute()

View file

@ -2,16 +2,8 @@
package service package service
import ( import (
"fmt"
"math/rand"
"os"
"os/signal"
"path/filepath" "path/filepath"
"strings"
"syscall"
"time"
"github.com/grandcat/zeroconf"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/drakkan/sftpgo/config" "github.com/drakkan/sftpgo/config"
@ -141,107 +133,3 @@ func (s *Service) Stop() {
close(s.Shutdown) close(s.Shutdown)
logger.Debug(logSender, "", "Service stopped") logger.Debug(logSender, "", "Service stopped")
} }
// StartPortableMode starts the service in portable mode
func (s *Service) StartPortableMode(sftpdPort int, enabledSSHCommands []string, advertiseService, advertiseCredentials bool) error {
if s.PortableMode != 1 {
return fmt.Errorf("service is not configured for portable mode")
}
var err error
rand.Seed(time.Now().UnixNano())
if len(s.PortableUser.Username) == 0 {
s.PortableUser.Username = "user"
}
if len(s.PortableUser.PublicKeys) == 0 && len(s.PortableUser.Password) == 0 {
var b strings.Builder
for i := 0; i < 8; i++ {
b.WriteRune(chars[rand.Intn(len(chars))])
}
s.PortableUser.Password = b.String()
}
dataProviderConf := config.GetProviderConf()
dataProviderConf.Driver = dataprovider.MemoryDataProviderName
dataProviderConf.Name = ""
dataProviderConf.CredentialsPath = filepath.Join(os.TempDir(), "credentials")
config.SetProviderConf(dataProviderConf)
httpdConf := config.GetHTTPDConfig()
httpdConf.BindPort = 0
config.SetHTTPDConfig(httpdConf)
sftpdConf := config.GetSFTPDConfig()
sftpdConf.MaxAuthTries = 12
if sftpdPort > 0 {
sftpdConf.BindPort = sftpdPort
} else {
// dynamic ports starts from 49152
sftpdConf.BindPort = 49152 + rand.Intn(15000)
}
if utils.IsStringInSlice("*", enabledSSHCommands) {
sftpdConf.EnabledSSHCommands = sftpd.GetSupportedSSHCommands()
} else {
sftpdConf.EnabledSSHCommands = enabledSSHCommands
}
config.SetSFTPDConfig(sftpdConf)
err = s.Start()
if err != nil {
return err
}
var mDNSService *zeroconf.Server
if advertiseService {
version := utils.GetAppVersion()
meta := []string{
fmt.Sprintf("version=%v", version.GetVersionAsString()),
}
if advertiseCredentials {
logger.InfoToConsole("Advertising credentials via multicast DNS")
meta = append(meta, fmt.Sprintf("user=%v", s.PortableUser.Username))
if len(s.PortableUser.Password) > 0 {
meta = append(meta, fmt.Sprintf("password=%v", s.PortableUser.Password))
} else {
logger.InfoToConsole("Unable to advertise key based credentials via multicast DNS, we don't have the private key")
}
}
mDNSService, err = zeroconf.Register(
fmt.Sprintf("SFTPGo portable %v", sftpdConf.BindPort), // service instance name
"_sftp-ssh._tcp", // service type and protocol
"local.", // service domain
sftpdConf.BindPort, // service port
meta, // service metadata
nil, // register on all network interfaces
)
if err != nil {
mDNSService = nil
logger.WarnToConsole("Unable to advertise SFTP service via multicast DNS: %v", err)
} else {
logger.InfoToConsole("SFTP service advertised via multicast DNS")
}
}
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
go func() {
<-sig
if mDNSService != nil {
logger.InfoToConsole("unregistering multicast DNS service")
mDNSService.Shutdown()
}
s.Stop()
}()
logger.InfoToConsole("Portable mode ready, SFTP port: %v, user: %#v, password: %#v, public keys: %v, directory: %#v, "+
"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
}
func (s *Service) getPortableDirToServe() string {
var dirToServe string
if s.PortableUser.FsConfig.Provider == 1 {
dirToServe = s.PortableUser.FsConfig.S3Config.KeyPrefix
} else if s.PortableUser.FsConfig.Provider == 2 {
dirToServe = s.PortableUser.FsConfig.GCSConfig.KeyPrefix
} else {
dirToServe = s.PortableUser.HomeDir
}
return dirToServe
}

126
service/service_portable.go Normal file
View file

@ -0,0 +1,126 @@
// +build !noportable
package service
import (
"fmt"
"math/rand"
"os"
"os/signal"
"path/filepath"
"strings"
"syscall"
"time"
"github.com/grandcat/zeroconf"
"github.com/drakkan/sftpgo/config"
"github.com/drakkan/sftpgo/dataprovider"
"github.com/drakkan/sftpgo/logger"
"github.com/drakkan/sftpgo/sftpd"
"github.com/drakkan/sftpgo/utils"
)
// StartPortableMode starts the service in portable mode
func (s *Service) StartPortableMode(sftpdPort int, enabledSSHCommands []string, advertiseService, advertiseCredentials bool) error {
if s.PortableMode != 1 {
return fmt.Errorf("service is not configured for portable mode")
}
var err error
rand.Seed(time.Now().UnixNano())
if len(s.PortableUser.Username) == 0 {
s.PortableUser.Username = "user"
}
if len(s.PortableUser.PublicKeys) == 0 && len(s.PortableUser.Password) == 0 {
var b strings.Builder
for i := 0; i < 8; i++ {
b.WriteRune(chars[rand.Intn(len(chars))])
}
s.PortableUser.Password = b.String()
}
dataProviderConf := config.GetProviderConf()
dataProviderConf.Driver = dataprovider.MemoryDataProviderName
dataProviderConf.Name = ""
dataProviderConf.CredentialsPath = filepath.Join(os.TempDir(), "credentials")
config.SetProviderConf(dataProviderConf)
httpdConf := config.GetHTTPDConfig()
httpdConf.BindPort = 0
config.SetHTTPDConfig(httpdConf)
sftpdConf := config.GetSFTPDConfig()
sftpdConf.MaxAuthTries = 12
if sftpdPort > 0 {
sftpdConf.BindPort = sftpdPort
} else {
// dynamic ports starts from 49152
sftpdConf.BindPort = 49152 + rand.Intn(15000)
}
if utils.IsStringInSlice("*", enabledSSHCommands) {
sftpdConf.EnabledSSHCommands = sftpd.GetSupportedSSHCommands()
} else {
sftpdConf.EnabledSSHCommands = enabledSSHCommands
}
config.SetSFTPDConfig(sftpdConf)
err = s.Start()
if err != nil {
return err
}
var mDNSService *zeroconf.Server
if advertiseService {
version := utils.GetAppVersion()
meta := []string{
fmt.Sprintf("version=%v", version.GetVersionAsString()),
}
if advertiseCredentials {
logger.InfoToConsole("Advertising credentials via multicast DNS")
meta = append(meta, fmt.Sprintf("user=%v", s.PortableUser.Username))
if len(s.PortableUser.Password) > 0 {
meta = append(meta, fmt.Sprintf("password=%v", s.PortableUser.Password))
} else {
logger.InfoToConsole("Unable to advertise key based credentials via multicast DNS, we don't have the private key")
}
}
mDNSService, err = zeroconf.Register(
fmt.Sprintf("SFTPGo portable %v", sftpdConf.BindPort), // service instance name
"_sftp-ssh._tcp", // service type and protocol
"local.", // service domain
sftpdConf.BindPort, // service port
meta, // service metadata
nil, // register on all network interfaces
)
if err != nil {
mDNSService = nil
logger.WarnToConsole("Unable to advertise SFTP service via multicast DNS: %v", err)
} else {
logger.InfoToConsole("SFTP service advertised via multicast DNS")
}
}
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
go func() {
<-sig
if mDNSService != nil {
logger.InfoToConsole("unregistering multicast DNS service")
mDNSService.Shutdown()
}
s.Stop()
}()
logger.InfoToConsole("Portable mode ready, SFTP port: %v, user: %#v, password: %#v, public keys: %v, directory: %#v, "+
"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
}
func (s *Service) getPortableDirToServe() string {
var dirToServe string
if s.PortableUser.FsConfig.Provider == 1 {
dirToServe = s.PortableUser.FsConfig.S3Config.KeyPrefix
} else if s.PortableUser.FsConfig.Provider == 2 {
dirToServe = s.PortableUser.FsConfig.GCSConfig.KeyPrefix
} else {
dirToServe = s.PortableUser.HomeDir
}
return dirToServe
}

View file

@ -1,5 +1,7 @@
package utils package utils
import "strings"
const version = "0.9.6-dev" const version = "0.9.6-dev"
var ( var (
@ -10,21 +12,34 @@ var (
// VersionInfo defines version details // VersionInfo defines version details
type VersionInfo struct { type VersionInfo struct {
Version string `json:"version"` Version string `json:"version"`
BuildDate string `json:"build_date"` BuildDate string `json:"build_date"`
CommitHash string `json:"commit_hash"` CommitHash string `json:"commit_hash"`
Features []string `json:"features"`
} }
// GetVersionAsString returns the string representation of the VersionInfo struct // GetVersionAsString returns the string representation of the VersionInfo struct
func (v *VersionInfo) GetVersionAsString() string { func (v *VersionInfo) GetVersionAsString() string {
versionString := v.Version var sb strings.Builder
sb.WriteString(v.Version)
if len(v.CommitHash) > 0 { if len(v.CommitHash) > 0 {
versionString += "-" + v.CommitHash sb.WriteString("-")
sb.WriteString(v.CommitHash)
} }
if len(v.BuildDate) > 0 { if len(v.BuildDate) > 0 {
versionString += "-" + v.BuildDate sb.WriteString("-")
sb.WriteString(v.BuildDate)
} }
return versionString if len(v.Features) > 0 {
sb.WriteString(" ")
sb.WriteString(strings.Join(v.Features, " "))
}
return sb.String()
}
// AddFeature adds a feature description
func AddFeature(feature string) {
versionInfo.Features = append(versionInfo.Features, feature)
} }
func init() { func init() {

View file

@ -1,3 +1,5 @@
// +build !nogcs
package vfs package vfs
import ( import (
@ -19,6 +21,7 @@ import (
"github.com/drakkan/sftpgo/logger" "github.com/drakkan/sftpgo/logger"
"github.com/drakkan/sftpgo/metrics" "github.com/drakkan/sftpgo/metrics"
"github.com/drakkan/sftpgo/utils"
) )
var ( var (
@ -27,22 +30,6 @@ var (
gcsDefaultFieldsSelection = []string{"Name", "Size", "Deleted", "Updated"} gcsDefaultFieldsSelection = []string{"Name", "Size", "Deleted", "Updated"}
) )
// GCSFsConfig defines the configuration for Google Cloud Storage based filesystem
type GCSFsConfig struct {
Bucket string `json:"bucket,omitempty"`
// KeyPrefix is similar to a chroot directory for local filesystem.
// If specified the SFTP user will only see objects that starts with
// this prefix and so you can restrict access to a specific virtual
// folder. The prefix, if not empty, must not start with "/" and must
// end with "/".
// If empty the whole bucket contents will be available
KeyPrefix string `json:"key_prefix,omitempty"`
CredentialFile string `json:"-"`
Credentials string `json:"credentials,omitempty"`
AutomaticCredentials int `json:"automatic_credentials,omitempty"`
StorageClass string `json:"storage_class,omitempty"`
}
// GCSFs is a Fs implementation for Google Cloud Storage. // GCSFs is a Fs implementation for Google Cloud Storage.
type GCSFs struct { type GCSFs struct {
connectionID string connectionID string
@ -53,6 +40,10 @@ type GCSFs struct {
ctxLongTimeout time.Duration ctxLongTimeout time.Duration
} }
func init() {
utils.AddFeature("+gcs")
}
// NewGCSFs returns an GCSFs object that allows to interact with Google Cloud Storage // NewGCSFs returns an GCSFs object that allows to interact with Google Cloud Storage
func NewGCSFs(connectionID, localTempDir string, config GCSFsConfig) (Fs, error) { func NewGCSFs(connectionID, localTempDir string, config GCSFsConfig) (Fs, error) {
var err error var err error

18
vfs/gcsfs_disabled.go Normal file
View file

@ -0,0 +1,18 @@
// +build nogcs
package vfs
import (
"errors"
"github.com/drakkan/sftpgo/utils"
)
func init() {
utils.AddFeature("-gcs")
}
// NewGCSFs returns an error, GCS is disabled
func NewGCSFs(connectionID, localTempDir string, config GCSFsConfig) (Fs, error) {
return nil, errors.New("Google Cloud Storage disabled at build time")
}

View file

@ -1,3 +1,5 @@
// +build !nos3
package vfs package vfs
import ( import (
@ -22,33 +24,6 @@ import (
"github.com/drakkan/sftpgo/utils" "github.com/drakkan/sftpgo/utils"
) )
// S3FsConfig defines the configuration for S3 based filesystem
type S3FsConfig struct {
Bucket string `json:"bucket,omitempty"`
// KeyPrefix is similar to a chroot directory for local filesystem.
// If specified the SFTP user will only see objects that starts with
// this prefix and so you can restrict access to a specific virtual
// folder. The prefix, if not empty, must not start with "/" and must
// end with "/".
// If empty the whole bucket contents will be available
KeyPrefix string `json:"key_prefix,omitempty"`
Region string `json:"region,omitempty"`
AccessKey string `json:"access_key,omitempty"`
AccessSecret string `json:"access_secret,omitempty"`
Endpoint string `json:"endpoint,omitempty"`
StorageClass string `json:"storage_class,omitempty"`
// The buffer size (in MB) to use for multipart uploads. The minimum allowed part size is 5MB,
// and if this value is set to zero, the default value (5MB) for the AWS SDK will be used.
// The minimum allowed value is 5.
// Please note that if the upload bandwidth between the SFTP client and SFTPGo is greater than
// the upload bandwidth between SFTPGo and S3 then the SFTP client have to wait for the upload
// of the last parts to S3 after it ends the file upload to SFTPGo, and it may time out.
// Keep this in mind if you customize these parameters.
UploadPartSize int64 `json:"upload_part_size,omitempty"`
// How many parts are uploaded in parallel
UploadConcurrency int `json:"upload_concurrency,omitempty"`
}
// S3Fs is a Fs implementation for Amazon S3 compatible object storage. // S3Fs is a Fs implementation for Amazon S3 compatible object storage.
type S3Fs struct { type S3Fs struct {
connectionID string connectionID string
@ -59,6 +34,10 @@ type S3Fs struct {
ctxLongTimeout time.Duration ctxLongTimeout time.Duration
} }
func init() {
utils.AddFeature("+s3")
}
// NewS3Fs returns an S3Fs object that allows to interact with an s3 compatible // NewS3Fs returns an S3Fs object that allows to interact with an s3 compatible
// object storage // object storage
func NewS3Fs(connectionID, localTempDir string, config S3FsConfig) (Fs, error) { func NewS3Fs(connectionID, localTempDir string, config S3FsConfig) (Fs, error) {

18
vfs/s3fs_disabled.go Normal file
View file

@ -0,0 +1,18 @@
// +build nos3
package vfs
import (
"errors"
"github.com/drakkan/sftpgo/utils"
)
func init() {
utils.AddFeature("-s3")
}
// NewS3Fs returns an error, S3 is disabled
func NewS3Fs(connectionID, localTempDir string, config S3FsConfig) (Fs, error) {
return nil, errors.New("S3 disabled at build time")
}

View file

@ -44,6 +44,49 @@ type Fs interface {
Join(elem ...string) string Join(elem ...string) string
} }
// S3FsConfig defines the configuration for S3 based filesystem
type S3FsConfig struct {
Bucket string `json:"bucket,omitempty"`
// KeyPrefix is similar to a chroot directory for local filesystem.
// If specified the SFTP user will only see objects that starts with
// this prefix and so you can restrict access to a specific virtual
// folder. The prefix, if not empty, must not start with "/" and must
// end with "/".
// If empty the whole bucket contents will be available
KeyPrefix string `json:"key_prefix,omitempty"`
Region string `json:"region,omitempty"`
AccessKey string `json:"access_key,omitempty"`
AccessSecret string `json:"access_secret,omitempty"`
Endpoint string `json:"endpoint,omitempty"`
StorageClass string `json:"storage_class,omitempty"`
// The buffer size (in MB) to use for multipart uploads. The minimum allowed part size is 5MB,
// and if this value is set to zero, the default value (5MB) for the AWS SDK will be used.
// The minimum allowed value is 5.
// Please note that if the upload bandwidth between the SFTP client and SFTPGo is greater than
// the upload bandwidth between SFTPGo and S3 then the SFTP client have to wait for the upload
// of the last parts to S3 after it ends the file upload to SFTPGo, and it may time out.
// Keep this in mind if you customize these parameters.
UploadPartSize int64 `json:"upload_part_size,omitempty"`
// How many parts are uploaded in parallel
UploadConcurrency int `json:"upload_concurrency,omitempty"`
}
// GCSFsConfig defines the configuration for Google Cloud Storage based filesystem
type GCSFsConfig struct {
Bucket string `json:"bucket,omitempty"`
// KeyPrefix is similar to a chroot directory for local filesystem.
// If specified the SFTP user will only see objects that starts with
// this prefix and so you can restrict access to a specific virtual
// folder. The prefix, if not empty, must not start with "/" and must
// end with "/".
// If empty the whole bucket contents will be available
KeyPrefix string `json:"key_prefix,omitempty"`
CredentialFile string `json:"-"`
Credentials string `json:"credentials,omitempty"`
AutomaticCredentials int `json:"automatic_credentials,omitempty"`
StorageClass string `json:"storage_class,omitempty"`
}
// PipeWriter defines a wrapper for pipeat.PipeWriterAt. // PipeWriter defines a wrapper for pipeat.PipeWriterAt.
type PipeWriter struct { type PipeWriter struct {
writer *pipeat.PipeWriterAt writer *pipeat.PipeWriterAt