diff --git a/README.md b/README.md index b73a9eab..8497b196 100644 --- a/README.md +++ b/README.md @@ -292,14 +292,29 @@ Usage: sftpgo portable [flags] Flags: - -d, --directory string Path to the directory to serve. This can be an absolute path or a path relative to the current directory (default ".") - -h, --help help for portable - -p, --password string Leave empty to use an auto generated value - -g, --permissions strings User's permissions. "*" means any permission (default [list,download]) + -C, --advertise-credentials If the service is advertised via multicast DNS this flag allows to put username/password inside the advertised TXT record + -S, --advertise-service Advertise SFTP/SCP 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 ".") + -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 - --scp Enable SCP - -s, --sftpd-port int 0 means a random non privileged port - -u, --username string Leave empty to use an auto generated value + --scp Enable SCP + -s, --sftpd-port int 0 means a random non privileged port + -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. + +Here is an example of the advertised service including credentials as seen using `avahi-browse`: + +``` += enp0s31f6 IPv4 SFTPGo portable 53705 SFTP File Transfer local + hostname = [p1.local] + address = [192.168.1.230] + port = [53705] + txt = ["password=EWOo6pJe" "user=user" "version=0.9.3-dev-b409523-dirty-2019-10-26T13:43:32Z"] ``` ## Account's configuration properties @@ -448,6 +463,7 @@ The logs can be divided into the following categories: - [cobra](https://github.com/spf13/cobra) - [xid](https://github.com/rs/xid) - [nathanaelle/password](https://github.com/nathanaelle/password) +- [ZeroConf](https://github.com/grandcat/zeroconf) - [SB Admin 2](https://github.com/BlackrockDigital/startbootstrap-sb-admin-2) Some code was initially taken from [Pterodactyl sftp server](https://github.com/pterodactyl/sftp-server) diff --git a/cmd/portable.go b/cmd/portable.go index bd4b54c6..71d93e62 100644 --- a/cmd/portable.go +++ b/cmd/portable.go @@ -9,14 +9,17 @@ import ( ) var ( - directoryToServe string - portableSFTPDPort int - portableEnableSCP bool - portableUsername string - portablePassword string - portablePublicKeys []string - portablePermissions []string - portableCmd = &cobra.Command{ + directoryToServe string + portableSFTPDPort int + portableEnableSCP bool + portableAdvertiseService bool + portableAdvertiseCredentials bool + portableUsername string + portablePassword string + portableLogFile string + portablePublicKeys []string + portablePermissions []string + portableCmd = &cobra.Command{ Use: "portable", Short: "Serve a single directory", Long: `To serve the current working directory with auto generated credentials simply use: @@ -32,7 +35,7 @@ Please take a look at the usage below to customize the serving parameters`, service := service.Service{ ConfigDir: defaultConfigDir, ConfigFile: defaultConfigName, - LogFilePath: defaultLogFile, + LogFilePath: portableLogFile, LogMaxSize: defaultLogMaxSize, LogMaxBackups: defaultLogMaxBackup, LogMaxAge: defaultLogMaxAge, @@ -48,7 +51,8 @@ Please take a look at the usage below to customize the serving parameters`, HomeDir: portableDir, }, } - if err := service.StartPortableMode(portableSFTPDPort, portableEnableSCP); err == nil { + if err := service.StartPortableMode(portableSFTPDPort, portableEnableSCP, portableAdvertiseService, + portableAdvertiseCredentials); err == nil { service.Wait() } }, @@ -59,11 +63,16 @@ func init() { 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") - portableCmd.Flags().BoolVarP(&portableEnableSCP, "scp", "", false, "Enable SCP") + portableCmd.Flags().BoolVar(&portableEnableSCP, "scp", false, "Enable SCP") portableCmd.Flags().StringVarP(&portableUsername, "username", "u", "", "Leave empty to use an auto generated value") portableCmd.Flags().StringVarP(&portablePassword, "password", "p", "", "Leave empty to use an auto generated value") + portableCmd.Flags().StringVarP(&portableLogFile, logFilePathFlag, "l", "", "Leave empty to disable logging") 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().BoolVarP(&portableAdvertiseService, "advertise-service", "S", true, + "Advertise SFTP/SCP service using multicast DNS") + portableCmd.Flags().BoolVarP(&portableAdvertiseCredentials, "advertise-credentials", "C", false, + "If the service is advertised via multicast DNS this flag allows to put username/password inside the advertised TXT record") rootCmd.AddCommand(portableCmd) } diff --git a/go.mod b/go.mod index 63deebad..a8d94371 100644 --- a/go.mod +++ b/go.mod @@ -4,19 +4,19 @@ go 1.12 require ( github.com/alexedwards/argon2id v0.0.0-20190612080829-01a59b2b8802 + github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/go-chi/chi v4.0.2+incompatible github.com/go-chi/render v1.0.1 github.com/go-sql-driver/mysql v1.4.1 + github.com/grandcat/zeroconf v0.0.0-20190424104450-85eadb44205c github.com/lib/pq v1.2.0 github.com/magiconair/properties v1.8.1 // indirect github.com/mattn/go-sqlite3 v1.11.0 + github.com/miekg/dns v1.1.22 // indirect github.com/nathanaelle/password v1.0.0 - github.com/pelletier/go-toml v1.5.0 // indirect + github.com/pelletier/go-toml v1.6.0 // indirect github.com/pkg/sftp v1.10.2-0.20191014030235-4350932b9896 - github.com/prometheus/client_golang v1.1.0 - github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect - github.com/prometheus/common v0.7.0 // indirect - github.com/prometheus/procfs v0.0.5 // indirect + github.com/prometheus/client_golang v1.2.1 github.com/rs/xid v1.2.1 github.com/rs/zerolog v1.15.0 github.com/spf13/afero v1.2.2 // indirect @@ -26,8 +26,8 @@ require ( github.com/spf13/viper v1.4.0 go.etcd.io/bbolt v1.3.3 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 - golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 // indirect - golang.org/x/sys v0.0.0-20191010194322-b09406accb47 + golang.org/x/net v0.0.0-20191021144547-ec77196f6094 // indirect + golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 google.golang.org/appengine v1.6.5 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) diff --git a/go.sum b/go.sum index 2913f58f..f3eb82e3 100644 --- a/go.sum +++ b/go.sum @@ -13,7 +13,12 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReGkA= +github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -56,6 +61,8 @@ github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grandcat/zeroconf v0.0.0-20190424104450-85eadb44205c h1:svzQzfVE9t7Y1CGULS5PsMWs4/H4Au/ZTJzU/0CKgqc= +github.com/grandcat/zeroconf v0.0.0-20190424104450-85eadb44205c/go.mod h1:YjKB0WsLXlMkO9p+wGTCoPIDGRJH0mz7E526PxkQVxI= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -87,6 +94,8 @@ github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.22 h1:Jm64b3bO9kP43ddLjL2EY3Io6bmy1qGb9Xxz6TqS6rc= +github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -99,8 +108,8 @@ github.com/nathanaelle/password v1.0.0 h1:1Etka3uuBvATlCb72f7P5vsgedus+C91Fgff1o github.com/nathanaelle/password v1.0.0/go.mod h1:wt9xV3xwQmc3Qi0ofowmzR7N+kF1L4cguCuWjAfdj1Q= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.5.0 h1:5BakdOZdtKJ1FFk6QdL8iSGrMWsXgchNJcrnarjbmJQ= -github.com/pelletier/go-toml v1.5.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= +github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4= +github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -111,8 +120,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.2.1 h1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI= +github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= @@ -120,13 +129,11 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= @@ -179,6 +186,7 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -191,12 +199,15 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191021144547-ec77196f6094 h1:5O4U9trLjNpuhpynaDsqwCk+Tw6seqJz1EbqbnzHrc8= +golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -206,9 +217,11 @@ golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -218,6 +231,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= diff --git a/logger/logger.go b/logger/logger.go index af3411e8..9cae4a9b 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -54,12 +54,7 @@ func InitLogger(logFilePath string, logMaxSize int, logMaxBackups int, logMaxAge MaxAge: logMaxAge, Compress: logCompress, }) - consoleOutput := zerolog.ConsoleWriter{ - Out: os.Stdout, - TimeFormat: dateFormat, - NoColor: runtime.GOOS == "windows", - } - consoleLogger = zerolog.New(consoleOutput).With().Timestamp().Logger().Level(level) + EnableConsoleLogger(level) } else { logger = zerolog.New(logSyncWrapper{ output: os.Stdout, @@ -69,6 +64,22 @@ func InitLogger(logFilePath string, logMaxSize int, logMaxBackups int, logMaxAge logger = logger.With().Timestamp().Logger().Level(level) } +// DisableLogger disable the main logger. +// ConsoleLogger will not be affected +func DisableLogger() { + logger = zerolog.Nop() +} + +// EnableConsoleLogger enables the console logger +func EnableConsoleLogger(level zerolog.Level) { + consoleOutput := zerolog.ConsoleWriter{ + Out: os.Stdout, + TimeFormat: dateFormat, + NoColor: runtime.GOOS == "windows", + } + consoleLogger = zerolog.New(consoleOutput).With().Timestamp().Logger().Level(level) +} + // Log logs at the specified level for the specified sender func Log(level LogLevel, sender string, connectionID string, format string, v ...interface{}) { switch level { diff --git a/service/service.go b/service/service.go index 7a917e9c..cac83d19 100644 --- a/service/service.go +++ b/service/service.go @@ -5,8 +5,9 @@ import ( "fmt" "math/rand" "os" - "path/filepath" + "os/signal" "strings" + "syscall" "time" "github.com/drakkan/sftpgo/config" @@ -15,7 +16,7 @@ import ( "github.com/drakkan/sftpgo/logger" "github.com/drakkan/sftpgo/sftpd" "github.com/drakkan/sftpgo/utils" - "github.com/rs/xid" + "github.com/grandcat/zeroconf" "github.com/rs/zerolog" ) @@ -49,6 +50,12 @@ func (s *Service) Start() error { logLevel = zerolog.InfoLevel } logger.InitLogger(s.LogFilePath, s.LogMaxSize, s.LogMaxBackups, s.LogMaxAge, s.LogCompress, logLevel) + if s.PortableMode == 1 { + logger.EnableConsoleLogger(logLevel) + if len(s.LogFilePath) == 0 { + logger.DisableLogger() + } + } version := utils.GetAppVersion() logger.Info(logSender, "", "starting SFTPGo %v, config dir: %v, config file: %v, log max size: %v log max backups: %v "+ "log max age: %v log verbose: %v, log compress: %v", version.GetVersionAsString(), s.ConfigDir, s.ConfigFile, s.LogMaxSize, @@ -106,11 +113,6 @@ func (s *Service) Start() error { logger.DebugToConsole("HTTP server not started, disabled in config file") } } - if s.PortableMode == 1 { - logger.InfoToConsole("Portable mode ready, SFTP port: %v, user: %#v, password: %#v, public keys: %v, directory: %#v, permissions: %v,"+ - " SCP enabled: %v", sftpdConf.BindPort, s.PortableUser.Username, s.PortableUser.Password, s.PortableUser.PublicKeys, - s.PortableUser.HomeDir, s.PortableUser.Permissions, sftpdConf.IsSCPEnabled) - } return nil } @@ -126,11 +128,12 @@ func (s *Service) Stop() { } // StartPortableMode starts the service in portable mode -func (s *Service) StartPortableMode(sftpdPort int, enableSCP bool) error { - rand.Seed(time.Now().UnixNano()) +func (s *Service) StartPortableMode(sftpdPort int, enableSCP, 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" } @@ -141,9 +144,6 @@ func (s *Service) StartPortableMode(sftpdPort int, enableSCP bool) error { } s.PortableUser.Password = b.String() } - tempDir := os.TempDir() - instanceID := xid.New().String() - s.LogFilePath = filepath.Join(tempDir, instanceID+".log") dataProviderConf := config.GetProviderConf() dataProviderConf.Driver = dataprovider.MemoryDataProviderName config.SetProviderConf(dataProviderConf) @@ -161,5 +161,53 @@ func (s *Service) StartPortableMode(sftpdPort int, enableSCP bool) error { sftpdConf.IsSCPEnabled = enableSCP config.SetSFTPDConfig(sftpdConf) - return s.Start() + err = s.Start() + if err == nil { + var mDNSService *zeroconf.Server + var err error + 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 protocl + "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 service via multicast DNS: %v", err) + } else { + logger.InfoToConsole("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, SCP enabled: %v", sftpdConf.BindPort, s.PortableUser.Username, s.PortableUser.Password, + s.PortableUser.PublicKeys, s.PortableUser.HomeDir, s.PortableUser.Permissions, sftpdConf.IsSCPEnabled) + } + return err }