feat: proxy auth support (#485)
* Change the order of commands to be able to cache more layers in case of multiple builds triggered in a row
* Fix #471
* Format Code
* Revert "Change the order of commands to be able to cache more layers in case of multiple builds triggered in a row"
This reverts commit 01362f34ee.
* Adjustment based on the review
* Rename "login-header" to "loginHeader" and prepare auth.method to accept "none" as a value
* Fixed line break
* Readd "lumberjack.v2" import which was removed by gofmt
Sorry - I do my tests and run "gofmt" before comitting the changes - It sadly seems like it is messing up the imports over and over again.
			
			
				pull/504/head
			
			
		
							parent
							
								
									b90e7b8d26
								
							
						
					
					
						commit
						ed62451ea0
					
				|  | @ -2,18 +2,7 @@ package main | |||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/asdine/storm" | ||||
| 
 | ||||
| 	"gopkg.in/natefinch/lumberjack.v2" | ||||
| 
 | ||||
| 	"github.com/filebrowser/filebrowser" | ||||
| 	"github.com/filebrowser/filebrowser/bolt" | ||||
| 	h "github.com/filebrowser/filebrowser/http" | ||||
|  | @ -21,7 +10,14 @@ import ( | |||
| 	"github.com/hacdias/fileutils" | ||||
| 	flag "github.com/spf13/pflag" | ||||
| 	"github.com/spf13/viper" | ||||
| ) | ||||
| 	"gopkg.in/natefinch/lumberjack.v2" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings") | ||||
| 
 | ||||
| var ( | ||||
| 	addr            string | ||||
|  | @ -38,13 +34,17 @@ var ( | |||
| 	recaptchakey    string | ||||
| 	recaptchasecret string | ||||
| 	port            int | ||||
| 	noAuth          bool | ||||
| 	allowCommands   bool | ||||
| 	allowEdit       bool | ||||
| 	allowNew        bool | ||||
| 	allowPublish    bool | ||||
| 	showVer         bool | ||||
| 	alterRecaptcha  bool | ||||
| 	auth            struct { | ||||
| 		method      string | ||||
| 		loginHeader string | ||||
| 	} | ||||
| 	noAuth         bool | ||||
| 	allowCommands  bool | ||||
| 	allowEdit      bool | ||||
| 	allowNew       bool | ||||
| 	allowPublish   bool | ||||
| 	showVer        bool | ||||
| 	alterRecaptcha bool | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
|  | @ -63,6 +63,8 @@ func init() { | |||
| 	flag.BoolVar(&allowCommands, "allow-commands", true, "Default allow commands option for new users") | ||||
| 	flag.BoolVar(&allowEdit, "allow-edit", true, "Default allow edit option for new users") | ||||
| 	flag.BoolVar(&allowPublish, "allow-publish", true, "Default allow publish option for new users") | ||||
| 	flag.StringVar(&auth.method, "auth.method", "default", "Switch between 'none', 'default' and 'proxy' authentication.") | ||||
| 	flag.StringVar(&auth.loginHeader, "auth.loginHeader", "X-Forwarded-User", "The header name used for proxy authentication.") | ||||
| 	flag.BoolVar(&allowNew, "allow-new", true, "Default allow new option for new users") | ||||
| 	flag.BoolVar(&noAuth, "no-auth", false, "Disables authentication") | ||||
| 	flag.BoolVar(&alterRecaptcha, "alternative-recaptcha", false, "Use recaptcha.net for serving and handling, useful in China") | ||||
|  | @ -84,6 +86,8 @@ func setupViper() { | |||
| 	viper.SetDefault("AllowPublish", true) | ||||
| 	viper.SetDefault("StaticGen", "") | ||||
| 	viper.SetDefault("Locale", "") | ||||
| 	viper.SetDefault("AuthMethod", "default") | ||||
| 	viper.SetDefault("LoginHeader", "X-Fowarded-User") | ||||
| 	viper.SetDefault("NoAuth", false) | ||||
| 	viper.SetDefault("BaseURL", "") | ||||
| 	viper.SetDefault("PrefixURL", "") | ||||
|  | @ -104,6 +108,8 @@ func setupViper() { | |||
| 	viper.BindPFlag("AllowPublish", flag.Lookup("allow-publish")) | ||||
| 	viper.BindPFlag("Locale", flag.Lookup("locale")) | ||||
| 	viper.BindPFlag("StaticGen", flag.Lookup("staticgen")) | ||||
| 	viper.BindPFlag("AuthMethod", flag.Lookup("auth.method")) | ||||
| 	viper.BindPFlag("LoginHeader", flag.Lookup("auth.loginHeader")) | ||||
| 	viper.BindPFlag("NoAuth", flag.Lookup("no-auth")) | ||||
| 	viper.BindPFlag("BaseURL", flag.Lookup("baseurl")) | ||||
| 	viper.BindPFlag("PrefixURL", flag.Lookup("prefixurl")) | ||||
|  | @ -168,6 +174,18 @@ func main() { | |||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	// Validate the provided config before moving forward
 | ||||
| 	if viper.GetString("AuthMethod") != "none" && viper.GetString("AuthMethod") != "default" && viper.GetString("AuthMethod") != "proxy" { | ||||
| 		log.Fatal("The property 'auth.method' needs to be set to 'default' or 'proxy'.") | ||||
| 	} | ||||
| 
 | ||||
| 	if viper.GetString("AuthMethod") == "proxy" { | ||||
| 		if viper.GetString("LoginHeader") == "" { | ||||
| 			log.Fatal("The 'loginHeader' needs to be specified when 'proxy' authentication is used.") | ||||
| 		} | ||||
| 		log.Println("[WARN] Filebrowser authentication is configured to 'proxy' authentication. This can cause a huge security issue if the infrastructure is not configured correctly.") | ||||
| 	} | ||||
| 
 | ||||
| 	// Builds the address and a listener.
 | ||||
| 	laddr := viper.GetString("Address") + ":" + viper.GetString("Port") | ||||
| 	listener, err := net.Listen("tcp", laddr) | ||||
|  | @ -196,6 +214,8 @@ func handler() http.Handler { | |||
| 	} | ||||
| 
 | ||||
| 	fm := &filebrowser.FileBrowser{ | ||||
| 		AuthMethod:      viper.GetString("AuthMethod"), | ||||
| 		LoginHeader:     viper.GetString("LoginHeader"), | ||||
| 		NoAuth:          viper.GetBool("NoAuth"), | ||||
| 		BaseURL:         viper.GetString("BaseURL"), | ||||
| 		PrefixURL:       viper.GetString("PrefixURL"), | ||||
|  |  | |||
							
								
								
									
										4
									
								
								doc.go
								
								
								
								
							
							
						
						
									
										4
									
								
								doc.go
								
								
								
								
							|  | @ -16,6 +16,10 @@ to import "github.com/filebrowser/filebrowser/bolt". | |||
| 
 | ||||
| 	m := &fm.FileBrowser{ | ||||
| 		NoAuth: false, | ||||
| 		Auth: { | ||||
| 			Method: "default", | ||||
| 			LoginHeader: "X-Fowarded-User" | ||||
| 		}, | ||||
| 		DefaultUser: &fm.User{ | ||||
| 			AllowCommands: true, | ||||
| 			AllowEdit:     true, | ||||
|  |  | |||
|  | @ -71,6 +71,16 @@ type FileBrowser struct { | |||
| 	// there will only exist one user, called "admin".
 | ||||
| 	NoAuth bool | ||||
| 
 | ||||
| 	// Define if which of the following authentication mechansims should be used:
 | ||||
| 	// - 'default', which requires a user and a password.
 | ||||
| 	// - 'proxy', which requires a valid user and the user name has to be provided through an
 | ||||
| 	//   http header.
 | ||||
| 	// - 'none', which allows anyone to access the filebrowser instance.
 | ||||
| 	AuthMethod string | ||||
| 
 | ||||
| 	// When 'AuthMethod' is set to 'proxy' the header configured below is used to identify the user.
 | ||||
| 	LoginHeader string | ||||
| 
 | ||||
| 	// ReCaptcha host, key and secret.
 | ||||
| 	ReCaptchaHost   string | ||||
| 	ReCaptchaKey    string | ||||
|  |  | |||
							
								
								
									
										26
									
								
								http/auth.go
								
								
								
								
							
							
						
						
									
										26
									
								
								http/auth.go
								
								
								
								
							|  | @ -51,20 +51,32 @@ func reCaptcha(host, secret, response string) (bool, error) { | |||
| 
 | ||||
| // authHandler processes the authentication for the user.
 | ||||
| func authHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) { | ||||
| 	// NoAuth instances shouldn't call this method.
 | ||||
| 	if c.NoAuth { | ||||
| 		// NoAuth instances shouldn't call this method.
 | ||||
| 		return 0, nil | ||||
| 	} | ||||
| 
 | ||||
| 	if c.AuthMethod == "proxy" { | ||||
| 		// Receive the Username from the Header and check if it exists.
 | ||||
| 		u, err := c.Store.Users.GetByUsername(r.Header.Get(c.LoginHeader), c.NewFS) | ||||
| 		if err != nil { | ||||
| 			return http.StatusForbidden, nil | ||||
| 		} | ||||
| 
 | ||||
| 		c.User = u | ||||
| 		return printToken(c, w) | ||||
| 	} | ||||
| 
 | ||||
| 	// Receive the credentials from the request and unmarshal them.
 | ||||
| 	var cred cred | ||||
| 
 | ||||
| 	if r.Body == nil { | ||||
| 		return http.StatusForbidden, nil | ||||
| 	} | ||||
| 
 | ||||
| 	err := json.NewDecoder(r.Body).Decode(&cred) | ||||
| 	if err != nil { | ||||
| 		return http.StatusForbidden, nil | ||||
| 		return http.StatusForbidden, err | ||||
| 	} | ||||
| 
 | ||||
| 	// If ReCaptcha is enabled, check the code.
 | ||||
|  | @ -171,6 +183,16 @@ func validateAuth(c *fb.Context, r *http.Request) (bool, *fb.User) { | |||
| 		return true, c.User | ||||
| 	} | ||||
| 
 | ||||
| 	// If proxy auth is used do not verify the JWT token if the header is provided.
 | ||||
| 	if c.AuthMethod == "proxy" { | ||||
| 		u, err := c.Store.Users.GetByUsername(r.Header.Get(c.LoginHeader), c.NewFS) | ||||
| 		if err != nil { | ||||
| 			return false, nil | ||||
| 		} | ||||
| 		c.User = u | ||||
| 		return true, c.User | ||||
| 	} | ||||
| 
 | ||||
| 	keyFunc := func(token *jwt.Token) (interface{}, error) { | ||||
| 		return c.Key, nil | ||||
| 	} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 maweck
						maweck