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:
parent
15298b0409
commit
ad53429cf1
26 changed files with 406 additions and 189 deletions
|
@ -1,3 +1,5 @@
|
|||
// +build !noportable
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
|
@ -14,6 +16,7 @@ import (
|
|||
"github.com/drakkan/sftpgo/dataprovider"
|
||||
"github.com/drakkan/sftpgo/service"
|
||||
"github.com/drakkan/sftpgo/sftpd"
|
||||
"github.com/drakkan/sftpgo/utils"
|
||||
"github.com/drakkan/sftpgo/vfs"
|
||||
)
|
||||
|
||||
|
@ -138,6 +141,8 @@ Please take a look at the usage below to customize the serving parameters`,
|
|||
)
|
||||
|
||||
func init() {
|
||||
utils.AddFeature("+portable")
|
||||
|
||||
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")
|
||||
portableCmd.Flags().IntVarP(&portableSFTPDPort, "sftpd-port", "s", 0, "0 means a random non privileged port")
|
||||
|
|
9
cmd/portable_disabled.go
Normal file
9
cmd/portable_disabled.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
// +build noportable
|
||||
|
||||
package cmd
|
||||
|
||||
import "github.com/drakkan/sftpgo/utils"
|
||||
|
||||
func init() {
|
||||
utils.AddFeature("-portable")
|
||||
}
|
|
@ -63,7 +63,7 @@ func init() {
|
|||
version := utils.GetAppVersion()
|
||||
rootCmd.Flags().BoolP("version", "v", false, "")
|
||||
rootCmd.Version = version.GetVersionAsString()
|
||||
rootCmd.SetVersionTemplate(`{{printf "SFTPGo version: "}}{{printf "%s" .Version}}
|
||||
rootCmd.SetVersionTemplate(`{{printf "SFTPGo "}}{{printf "%s" .Version}}
|
||||
`)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// +build !nobolt
|
||||
|
||||
package dataprovider
|
||||
|
||||
import (
|
||||
|
@ -52,6 +54,10 @@ type compatUserV2 struct {
|
|||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
utils.AddFeature("+bolt")
|
||||
}
|
||||
|
||||
func initializeBoltProvider(basePath string) error {
|
||||
var err error
|
||||
logSender = fmt.Sprintf("dataprovider_%v", BoltDataProviderName)
|
||||
|
|
17
dataprovider/bolt_disabled.go
Normal file
17
dataprovider/bolt_disabled.go
Normal 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")
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
// +build !nomysql
|
||||
|
||||
package dataprovider
|
||||
|
||||
import (
|
||||
|
@ -6,7 +8,11 @@ import (
|
|||
"strings"
|
||||
"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/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -28,6 +34,10 @@ type MySQLProvider struct {
|
|||
dbHandle *sql.DB
|
||||
}
|
||||
|
||||
func init() {
|
||||
utils.AddFeature("+mysql")
|
||||
}
|
||||
|
||||
func initializeMySQLProvider() error {
|
||||
var err error
|
||||
logSender = fmt.Sprintf("dataprovider_%v", MySQLDataProviderName)
|
||||
|
|
17
dataprovider/mysql_disabled.go
Normal file
17
dataprovider/mysql_disabled.go
Normal 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")
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
// +build !nopgsql
|
||||
|
||||
package dataprovider
|
||||
|
||||
import (
|
||||
|
@ -5,7 +7,11 @@ import (
|
|||
"fmt"
|
||||
"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/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -26,6 +32,10 @@ type PGSQLProvider struct {
|
|||
dbHandle *sql.DB
|
||||
}
|
||||
|
||||
func init() {
|
||||
utils.AddFeature("+pgsql")
|
||||
}
|
||||
|
||||
func initializePGSQLProvider() error {
|
||||
var err error
|
||||
logSender = fmt.Sprintf("dataprovider_%v", PGSQLDataProviderName)
|
||||
|
|
17
dataprovider/pgsql_disabled.go
Normal file
17
dataprovider/pgsql_disabled.go
Normal 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")
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
// +build !nosqlite
|
||||
|
||||
package dataprovider
|
||||
|
||||
import (
|
||||
|
@ -6,6 +8,9 @@ import (
|
|||
"path/filepath"
|
||||
"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/utils"
|
||||
)
|
||||
|
@ -41,6 +46,10 @@ type SQLiteProvider struct {
|
|||
dbHandle *sql.DB
|
||||
}
|
||||
|
||||
func init() {
|
||||
utils.AddFeature("+sqlite")
|
||||
}
|
||||
|
||||
func initializeSQLiteProvider(basePath string) error {
|
||||
var err error
|
||||
var connectionString string
|
||||
|
|
17
dataprovider/sqlite_disabled.go
Normal file
17
dataprovider/sqlite_disabled.go
Normal 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")
|
||||
}
|
|
@ -4,19 +4,18 @@ RUN apk add --no-cache git gcc g++ ca-certificates \
|
|||
&& go get -d github.com/drakkan/sftpgo
|
||||
WORKDIR /go/src/github.com/drakkan/sftpgo
|
||||
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.
|
||||
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
|
||||
|
||||
RUN apk add --no-cache ca-certificates su-exec \
|
||||
&& 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.
|
||||
# If you install git then ca-certificates will be automatically installed as dependency.
|
||||
# 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
|
||||
# git and rsync are optional, uncomment the next line to add support for them if needed.
|
||||
#RUN apk add --no-cache git rsync
|
||||
|
||||
COPY --from=builder /go/bin/sftpgo /bin/
|
||||
COPY --from=builder /go/src/github.com/drakkan/sftpgo/sftpgo.json /etc/sftpgo/sftpgo.json
|
||||
|
|
|
@ -13,7 +13,10 @@ sudo groupadd -g 1003 sftpgrp && \
|
|||
|
||||
# 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 && \
|
||||
cd sftpgo && \
|
||||
sudo docker build -t sftpgo docker/sftpgo/alpine/
|
||||
|
|
|
@ -4,16 +4,18 @@ LABEL maintainer="nicola.murino@gmail.com"
|
|||
RUN go get -d github.com/drakkan/sftpgo
|
||||
WORKDIR /go/src/github.com/drakkan/sftpgo
|
||||
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.
|
||||
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
|
||||
FROM debian:latest
|
||||
|
||||
# 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.
|
||||
# git, rsync and ca-certificates are optional, uncomment the next line to add support for them if needed.
|
||||
RUN apt-get update && apt-get install -y ca-certificates
|
||||
|
||||
# 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
|
||||
|
||||
ARG BASE_DIR=/app
|
||||
|
|
|
@ -8,13 +8,22 @@ You can build the container image using `docker build`, for example:
|
|||
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`
|
||||
and to build a specific tag/commit you can use for example `TAG=0.9.6`, like this:
|
||||
This will build master of github.com/drakkan/sftpgo.
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
```bash
|
||||
|
|
|
@ -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`.
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
- `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:
|
||||
|
||||
```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
|
||||
$ sftpgo -v
|
||||
SFTPGo version: 0.9.0-dev-90607d4-dirty-2019-08-08T19:28:36Z
|
||||
$ ./sftpgo -v
|
||||
SFTPGo 0.9.6-dev-15298b0-dirty-2020-05-22T21:25:51Z -gcs -s3 +bolt +mysql +pgsql -sqlite +portable
|
||||
```
|
|
@ -2,7 +2,7 @@ openapi: 3.0.1
|
|||
info:
|
||||
title: SFTPGo
|
||||
description: 'SFTPGo REST API'
|
||||
version: 1.8.5
|
||||
version: 1.8.6
|
||||
|
||||
servers:
|
||||
- url: /api/v1
|
||||
|
@ -1278,6 +1278,11 @@ components:
|
|||
type: string
|
||||
commit_hash:
|
||||
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:
|
||||
BasicAuth:
|
||||
type: http
|
||||
|
|
8
main.go
8
main.go
|
@ -3,13 +3,7 @@
|
|||
// https://github.com/drakkan/sftpgo/blob/master/README.md
|
||||
package main // import "github.com/drakkan/sftpgo"
|
||||
|
||||
import (
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/lib/pq"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
|
||||
"github.com/drakkan/sftpgo/cmd"
|
||||
)
|
||||
import "github.com/drakkan/sftpgo/cmd"
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
|
|
|
@ -2,16 +2,8 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/grandcat/zeroconf"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"github.com/drakkan/sftpgo/config"
|
||||
|
@ -141,107 +133,3 @@ func (s *Service) Stop() {
|
|||
close(s.Shutdown)
|
||||
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
126
service/service_portable.go
Normal 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
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package utils
|
||||
|
||||
import "strings"
|
||||
|
||||
const version = "0.9.6-dev"
|
||||
|
||||
var (
|
||||
|
@ -10,21 +12,34 @@ var (
|
|||
|
||||
// VersionInfo defines version details
|
||||
type VersionInfo struct {
|
||||
Version string `json:"version"`
|
||||
BuildDate string `json:"build_date"`
|
||||
CommitHash string `json:"commit_hash"`
|
||||
Version string `json:"version"`
|
||||
BuildDate string `json:"build_date"`
|
||||
CommitHash string `json:"commit_hash"`
|
||||
Features []string `json:"features"`
|
||||
}
|
||||
|
||||
// GetVersionAsString returns the string representation of the VersionInfo struct
|
||||
func (v *VersionInfo) GetVersionAsString() string {
|
||||
versionString := v.Version
|
||||
var sb strings.Builder
|
||||
sb.WriteString(v.Version)
|
||||
if len(v.CommitHash) > 0 {
|
||||
versionString += "-" + v.CommitHash
|
||||
sb.WriteString("-")
|
||||
sb.WriteString(v.CommitHash)
|
||||
}
|
||||
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() {
|
||||
|
|
23
vfs/gcsfs.go
23
vfs/gcsfs.go
|
@ -1,3 +1,5 @@
|
|||
// +build !nogcs
|
||||
|
||||
package vfs
|
||||
|
||||
import (
|
||||
|
@ -19,6 +21,7 @@ import (
|
|||
|
||||
"github.com/drakkan/sftpgo/logger"
|
||||
"github.com/drakkan/sftpgo/metrics"
|
||||
"github.com/drakkan/sftpgo/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -27,22 +30,6 @@ var (
|
|||
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.
|
||||
type GCSFs struct {
|
||||
connectionID string
|
||||
|
@ -53,6 +40,10 @@ type GCSFs struct {
|
|||
ctxLongTimeout time.Duration
|
||||
}
|
||||
|
||||
func init() {
|
||||
utils.AddFeature("+gcs")
|
||||
}
|
||||
|
||||
// NewGCSFs returns an GCSFs object that allows to interact with Google Cloud Storage
|
||||
func NewGCSFs(connectionID, localTempDir string, config GCSFsConfig) (Fs, error) {
|
||||
var err error
|
||||
|
|
18
vfs/gcsfs_disabled.go
Normal file
18
vfs/gcsfs_disabled.go
Normal 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")
|
||||
}
|
33
vfs/s3fs.go
33
vfs/s3fs.go
|
@ -1,3 +1,5 @@
|
|||
// +build !nos3
|
||||
|
||||
package vfs
|
||||
|
||||
import (
|
||||
|
@ -22,33 +24,6 @@ import (
|
|||
"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.
|
||||
type S3Fs struct {
|
||||
connectionID string
|
||||
|
@ -59,6 +34,10 @@ type S3Fs struct {
|
|||
ctxLongTimeout time.Duration
|
||||
}
|
||||
|
||||
func init() {
|
||||
utils.AddFeature("+s3")
|
||||
}
|
||||
|
||||
// NewS3Fs returns an S3Fs object that allows to interact with an s3 compatible
|
||||
// object storage
|
||||
func NewS3Fs(connectionID, localTempDir string, config S3FsConfig) (Fs, error) {
|
||||
|
|
18
vfs/s3fs_disabled.go
Normal file
18
vfs/s3fs_disabled.go
Normal 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")
|
||||
}
|
43
vfs/vfs.go
43
vfs/vfs.go
|
@ -44,6 +44,49 @@ type Fs interface {
|
|||
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.
|
||||
type PipeWriter struct {
|
||||
writer *pipeat.PipeWriterAt
|
||||
|
|
Loading…
Reference in a new issue