Skip to content

Commit

Permalink
added read only and upload only mode, also made basic auth so one can…
Browse files Browse the repository at this point in the history
… use custom user and password
  • Loading branch information
Patrick Hener committed Oct 12, 2021
1 parent 1e4ebfc commit b28b7c2
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 70 deletions.
44 changes: 24 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
![Version](https://img.shields.io/badge/Version-v0.1.3-green)
![Version](https://img.shields.io/badge/Version-v0.1.4-green)
[![GitHub](https://img.shields.io/github/license/patrickhener/goshs)](https://github.com/patrickhener/goshs/blob/master/LICENSE)
![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/patrickhener/goshs)
[![GitHub issues](https://img.shields.io/github/issues-raw/patrickhener/goshs)](https://github.com/patrickhener/goshs/issues)
Expand Down Expand Up @@ -46,33 +46,37 @@ make build
# Usage

```bash
Usage: goshs [options]
goshs v0.1.4
Usage: ./goshs [options]

Web server options:
-i The ip/if-name to listen on (default: 0.0.0.0)
-p The port to listen on (default: 8000)
-d The web root directory (default: current working path)
-w Also serve using webdav protocol (default: false)
-wp The port to listen on for webdav (default: 8001)
-i, --ip The ip/if-name to listen on (default: 0.0.0.0)
-p, --port The port to listen on (default: 8000)
-d, --dir The web root directory (default: current working path)
-w, --webdav Also serve using webdav protocol (default: false)
-wp, --webdav-port The port to listen on for webdav (default: 8001)
-ro, --read-only Read only mode, no upload possible (default: false)
-uo, --upload-only Upload only mode, no download possible (default: false)

TLS options:
-s Use TLS
-ss Use a self-signed certificate
-sk Path to server key
-sc Path to server certificate
-s, --ssl Use TLS
-ss, --self-signed Use a self-signed certificate
-sk, --server-key Path to server key
-sc, --server-cert Path to server certificate

Authentication options:
-P Use basic authentication password (user: gopher)
-b, --basic-auth Use basic authentication (user:pass)

Misc options:
-v Print the current goshs version
-v Print the current goshs version

Usage examples:
Start with default values: ./goshs
Start with different port: ./goshs -p 8080
Start with self-signed cert: ./goshs -s -ss
Start with custom cert: ./goshs -s -sk <path to key> -sc <path to cert>
Start with basic auth: ./goshs -P $up3r$3cur3
Start with default values: ./goshs
Start with wevdav support: ./goshs -w
Start with different port: ./goshs -p 8080
Start with self-signed cert: ./goshs -s -ss
Start with custom cert: ./goshs -s -sk <path to key> -sc <path to cert>
Start with basic auth: ./goshs -b secret-user:$up3r$3cur3
```

# Examples
Expand All @@ -95,9 +99,9 @@ Usage examples:

**Password protect the service**

`goshs -P VeryS3cureP4$$w0rd`
`goshs -b secret-user:VeryS3cureP4$$w0rd`

*Please note:* goshs uses HTTP basic authentication. It is recommended to use SSL option with basic authentication to prevent from credentials beeing transfered in cleartext over the line. User is `gopher`.
*Please note:* goshs uses HTTP basic authentication. It is recommended to use SSL option with basic authentication to prevent from credentials beeing transfered in cleartext over the line.

**Use TLS connection**

Expand Down
52 changes: 36 additions & 16 deletions internal/myhttp/fileserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,20 +60,23 @@ type item struct {

// FileServer holds the fileserver information
type FileServer struct {
IP string
Port int
WebdavPort int
Webroot string
SSL bool
SelfSigned bool
MyKey string
MyCert string
BasicAuth string
Version string
IP string
Port int
WebdavPort int
Webroot string
SSL bool
SelfSigned bool
MyKey string
MyCert string
User string
Pass string
Version string
Fingerprint256 string
Fingerprint1 string
Hub *mysock.Hub
Clipboard *myclipboard.Clipboard
Fingerprint1 string
UploadOnly bool
ReadOnly bool
Hub *mysock.Hub
Clipboard *myclipboard.Clipboard
}

type httperror struct {
Expand All @@ -94,7 +97,7 @@ func (fs *FileServer) BasicAuthMiddleware(next http.Handler) http.Handler {
return
}

if username != "gopher" || password != fs.BasicAuth {
if username != fs.User || password != fs.Pass {
http.Error(w, "Not authorized", http.StatusUnauthorized)
return
}
Expand Down Expand Up @@ -162,11 +165,11 @@ func (fs *FileServer) Start(what string) {
go fs.Hub.Run()

// Check BasicAuth and use middleware
if fs.BasicAuth != "" && what == "web" {
if fs.User != "" && what == "web" {
if !fs.SSL {
log.Printf("WARNING!: You are using basic auth without SSL. Your credentials will be transferred in cleartext. Consider using -s, too.\n")
}
log.Printf("Using 'gopher:%+v' as basic auth\n", fs.BasicAuth)
log.Printf("Using basic auth with user '%s' and password '%s'\n", fs.User, fs.Pass)
// Use middleware
mux.Use(fs.BasicAuthMiddleware)
}
Expand Down Expand Up @@ -300,6 +303,10 @@ func (fs *FileServer) handler(w http.ResponseWriter, req *http.Request) {

// upload handles the POST request to upload files
func (fs *FileServer) upload(w http.ResponseWriter, req *http.Request) {
if fs.ReadOnly {
fs.handleError(w, req, fmt.Errorf("%s", "Upload not allowed due to 'read only' option"), http.StatusForbidden)
return
}
// Get url so you can extract Headline and title
upath := req.URL.Path

Expand Down Expand Up @@ -366,6 +373,10 @@ func (fs *FileServer) upload(w http.ResponseWriter, req *http.Request) {

// bulkDownload will provide zip archived download bundle of multiple selected files
func (fs *FileServer) bulkDownload(w http.ResponseWriter, req *http.Request) {
if fs.UploadOnly {
fs.handleError(w, req, fmt.Errorf("%s", "Bulk download not allowed due to 'upload only' option"), http.StatusForbidden)
return
}
// make slice and query files from request
var filesCleaned []string
files := req.URL.Query()["file"]
Expand Down Expand Up @@ -533,6 +544,11 @@ func (fs *FileServer) processDir(w http.ResponseWriter, req *http.Request, file
d.IsSubdirectory = false
}

// upload only mode empty directory
if fs.UploadOnly {
d = &directory{}
}

// Construct template
tem := &indexTemplate{
Directory: d,
Expand All @@ -550,6 +566,10 @@ func (fs *FileServer) processDir(w http.ResponseWriter, req *http.Request, file
}

func (fs *FileServer) sendFile(w http.ResponseWriter, req *http.Request, file *os.File) {
if fs.UploadOnly {
fs.handleError(w, req, fmt.Errorf("%s", "Download not allowed due to 'upload only' option"), http.StatusForbidden)
return
}
// Extract download parameter
download := req.URL.Query()
if _, ok := download["download"]; ok {
Expand Down
133 changes: 99 additions & 34 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/patrickhener/goshs/internal/myutils"
)

const goshsVersion = "v0.1.3"
const goshsVersion = "v0.1.4"

var (
port = 8000
Expand All @@ -28,53 +28,83 @@ var (
basicAuth = ""
webdav = false
webdavPort = 8001
uploadOnly = false
readOnly = false
)

// Man page
func usage() func() {
return func() {

fmt.Printf(`
goshs %s
Usage: %s [options]
Web server options:
-i, --ip The ip/if-name to listen on (default: 0.0.0.0)
-p, --port The port to listen on (default: 8000)
-d, --dir The web root directory (default: current working path)
-w, --webdav Also serve using webdav protocol (default: false)
-wp, --webdav-port The port to listen on for webdav (default: 8001)
-ro, --read-only Read only mode, no upload possible (default: false)
-uo, --upload-only Upload only mode, no download possible (default: false)
TLS options:
-s, --ssl Use TLS
-ss, --self-signed Use a self-signed certificate
-sk, --server-key Path to server key
-sc, --server-cert Path to server certificate
Authentication options:
-b, --basic-auth Use basic authentication (user:pass)
Misc options:
-v Print the current goshs version
Usage examples:
Start with default values: ./goshs
Start with wevdav support: ./goshs -w
Start with different port: ./goshs -p 8080
Start with self-signed cert: ./goshs -s -ss
Start with custom cert: ./goshs -s -sk <path to key> -sc <path to cert>
Start with basic auth: ./goshs -b secret-user:$up3r$3cur3
`, goshsVersion, os.Args[0])
}
}

// Flag handling
func init() {
wd, _ := os.Getwd()

// flags
flag.StringVar(&ip, "i", ip, "ip")
flag.StringVar(&ip, "ip", ip, "ip")
flag.IntVar(&port, "p", port, "port")
flag.IntVar(&port, "port", port, "port")
flag.StringVar(&webroot, "d", wd, "web root")
flag.StringVar(&webroot, "dir", wd, "web root")
flag.BoolVar(&ssl, "s", ssl, "tls")
flag.BoolVar(&ssl, "ssl", ssl, "tls")
flag.BoolVar(&selfsigned, "ss", selfsigned, "self-signed")
flag.BoolVar(&selfsigned, "self-signed", selfsigned, "self-signed")
flag.StringVar(&myKey, "sk", myKey, "server key")
flag.StringVar(&myKey, "server-key", myKey, "server key")
flag.StringVar(&myCert, "sc", myCert, "server cert")
flag.StringVar(&basicAuth, "P", basicAuth, "basic auth")
flag.StringVar(&myCert, "server-cert", myCert, "server cert")
flag.StringVar(&basicAuth, "b", basicAuth, "basic auth")
flag.StringVar(&basicAuth, "basic-auth", basicAuth, "basic auth")
flag.BoolVar(&webdav, "w", webdav, "enable webdav")
flag.BoolVar(&webdav, "webdav", webdav, "enable webdav")
flag.IntVar(&webdavPort, "wp", webdavPort, "webdav port")
flag.IntVar(&webdavPort, "webdav-port", webdavPort, "webdav port")
flag.BoolVar(&uploadOnly, "uo", uploadOnly, "upload only")
flag.BoolVar(&uploadOnly, "upload-only", uploadOnly, "upload only")
flag.BoolVar(&readOnly, "ro", readOnly, "read only")
flag.BoolVar(&readOnly, "read-only", readOnly, "read only")
version := flag.Bool("v", false, "goshs version")

flag.Usage = func() {
fmt.Printf("goshs %s\n", goshsVersion)
fmt.Printf("Usage: %s [options]\n\n", os.Args[0])
fmt.Println("Web server options:")
fmt.Println("\t-i\tThe ip/if-name to listen on\t\t(default: 0.0.0.0)")
fmt.Println("\t-p\tThe port to listen on\t\t\t(default: 8000)")
fmt.Println("\t-d\tThe web root directory\t\t\t(default: current working path)")
fmt.Println("\t-w\tAlso serve using webdav protocol\t(default: false)")
fmt.Println("\t-wp\tThe port to listen on for webdav\t(default: 8001)")
fmt.Println("")
fmt.Println("TLS options:")
fmt.Println("\t-s\tUse TLS")
fmt.Println("\t-ss\tUse a self-signed certificate")
fmt.Println("\t-sk\tPath to server key")
fmt.Println("\t-sc\tPath to server certificate")
fmt.Println("")
fmt.Println("Authentication options:")
fmt.Println("\t-P\tUse basic authentication password (user: gopher)")
fmt.Println("")
fmt.Println("Misc options:")
fmt.Println("\t-v\tPrint the current goshs version")
fmt.Println("")
fmt.Println("Usage examples:")
fmt.Println("\tStart with default values:\t./goshs")
fmt.Println("\tStart with different port:\t./goshs -p 8080")
fmt.Println("\tStart with self-signed cert:\t./goshs -s -ss")
fmt.Println("\tStart with custom cert:\t\t./goshs -s -sk <path to key> -sc <path to cert>")
fmt.Println("\tStart with basic auth:\t\t./goshs -P $up3r$3cur3")
}
flag.Usage = usage()

flag.Parse()

Expand All @@ -88,20 +118,52 @@ func init() {
if !strings.Contains(ip, ".") {
addr, err := myutils.GetInterfaceIpv4Addr(ip)
if err != nil {
fmt.Println(err)
log.Fatal(err)
os.Exit(-1)
}

if addr == "" {
fmt.Println("IP address cannot be found for provided interface")
log.Println("IP address cannot be found for provided interface")
os.Exit(-1)
}

ip = addr
}

// Sanity check for upload only vs read only
if uploadOnly && readOnly {
log.Println("You can only select either 'upload only' or 'read only', not both.")
os.Exit(-1)
}

if webdav {
log.Println("WARNING: upload/read-only mode deactivated due to use of 'webdav' mode")
uploadOnly = false
readOnly = false
}
}

// Sanity checks if basic auth has the right format
func parseBasicAuth() (string, string) {
auth := strings.SplitN(basicAuth, ":", 2)
if len(auth) < 2 {
fmt.Println("Wrong basic auth format. Please provide user:password seperated by a colon")
os.Exit(-1)
}
user := auth[0]
pass := auth[1]
return user, pass

}

func main() {
user := ""
pass := ""
// check for basic auth
if basicAuth != "" {
user, pass = parseBasicAuth()
}

done := make(chan os.Signal, 1)
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)

Expand All @@ -116,7 +178,10 @@ func main() {
SelfSigned: selfsigned,
MyCert: myCert,
MyKey: myKey,
BasicAuth: basicAuth,
User: user,
Pass: pass,
UploadOnly: uploadOnly,
ReadOnly: readOnly,
Version: goshsVersion,
}

Expand Down

0 comments on commit b28b7c2

Please sign in to comment.