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 29217f66ee6aee63d2c03ac86de4ad437876317d [formerly ebff3e9d79ac9eca44d7b3caf7814be62c784d43] [formerly 9b95d9e986254d55405cd0e9484dcbbadc54c87b [formerly d13fd2878c38a46f91da30de150624200f0b32e9]] [formerly 3ec8fb12d8b6e1942ebae6abb00c5f15b03d6412 [formerly 6a70bdaf457f50896dd9826608666a39babae666] [formerly 063a6fe9d4991b7b6c257ae081288ea40efbe8b5 [formerly 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.


Former-commit-id: 252e65171f70ee87238b5542e6af81d90bdaed6b [formerly fa843827feaab389550f32ba3a629e1968bcea3d] [formerly 942986226dbb56ef1cb4dff24445406cfa699d2d [formerly ed62451ea0]]
Former-commit-id: e87377dd6f30012b0d602b592100a7deb39a8632 [formerly f8198aa8a51fd5e727c31df0918ab62024520cef]
Former-commit-id: 019de07d53c3da16354e228330c14efb0dfb2122
pull/726/head
maweck 2018-08-08 11:06:16 +02:00 committed by Henrique Dias
parent 769e634bdd
commit 50dcf35eda
4 changed files with 77 additions and 21 deletions

View File

@ -2,18 +2,7 @@ package main
import ( import (
"fmt" "fmt"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/asdine/storm" "github.com/asdine/storm"
"gopkg.in/natefinch/lumberjack.v2"
"github.com/filebrowser/filebrowser" "github.com/filebrowser/filebrowser"
"github.com/filebrowser/filebrowser/bolt" "github.com/filebrowser/filebrowser/bolt"
h "github.com/filebrowser/filebrowser/http" h "github.com/filebrowser/filebrowser/http"
@ -21,7 +10,14 @@ import (
"github.com/hacdias/fileutils" "github.com/hacdias/fileutils"
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
"github.com/spf13/viper" "github.com/spf13/viper"
) "gopkg.in/natefinch/lumberjack.v2"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"path/filepath"
"strings")
var ( var (
addr string addr string
@ -38,13 +34,17 @@ var (
recaptchakey string recaptchakey string
recaptchasecret string recaptchasecret string
port int port int
noAuth bool auth struct {
allowCommands bool method string
allowEdit bool loginHeader string
allowNew bool }
allowPublish bool noAuth bool
showVer bool allowCommands bool
alterRecaptcha bool allowEdit bool
allowNew bool
allowPublish bool
showVer bool
alterRecaptcha bool
) )
func init() { func init() {
@ -63,6 +63,8 @@ func init() {
flag.BoolVar(&allowCommands, "allow-commands", true, "Default allow commands option for new users") 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(&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.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(&allowNew, "allow-new", true, "Default allow new option for new users")
flag.BoolVar(&noAuth, "no-auth", false, "Disables authentication") flag.BoolVar(&noAuth, "no-auth", false, "Disables authentication")
flag.BoolVar(&alterRecaptcha, "alternative-recaptcha", false, "Use recaptcha.net for serving and handling, useful in China") 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("AllowPublish", true)
viper.SetDefault("StaticGen", "") viper.SetDefault("StaticGen", "")
viper.SetDefault("Locale", "") viper.SetDefault("Locale", "")
viper.SetDefault("AuthMethod", "default")
viper.SetDefault("LoginHeader", "X-Fowarded-User")
viper.SetDefault("NoAuth", false) viper.SetDefault("NoAuth", false)
viper.SetDefault("BaseURL", "") viper.SetDefault("BaseURL", "")
viper.SetDefault("PrefixURL", "") viper.SetDefault("PrefixURL", "")
@ -104,6 +108,8 @@ func setupViper() {
viper.BindPFlag("AllowPublish", flag.Lookup("allow-publish")) viper.BindPFlag("AllowPublish", flag.Lookup("allow-publish"))
viper.BindPFlag("Locale", flag.Lookup("locale")) viper.BindPFlag("Locale", flag.Lookup("locale"))
viper.BindPFlag("StaticGen", flag.Lookup("staticgen")) 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("NoAuth", flag.Lookup("no-auth"))
viper.BindPFlag("BaseURL", flag.Lookup("baseurl")) viper.BindPFlag("BaseURL", flag.Lookup("baseurl"))
viper.BindPFlag("PrefixURL", flag.Lookup("prefixurl")) 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. // Builds the address and a listener.
laddr := viper.GetString("Address") + ":" + viper.GetString("Port") laddr := viper.GetString("Address") + ":" + viper.GetString("Port")
listener, err := net.Listen("tcp", laddr) listener, err := net.Listen("tcp", laddr)
@ -196,6 +214,8 @@ func handler() http.Handler {
} }
fm := &filebrowser.FileBrowser{ fm := &filebrowser.FileBrowser{
AuthMethod: viper.GetString("AuthMethod"),
LoginHeader: viper.GetString("LoginHeader"),
NoAuth: viper.GetBool("NoAuth"), NoAuth: viper.GetBool("NoAuth"),
BaseURL: viper.GetString("BaseURL"), BaseURL: viper.GetString("BaseURL"),
PrefixURL: viper.GetString("PrefixURL"), PrefixURL: viper.GetString("PrefixURL"),

4
doc.go
View File

@ -16,6 +16,10 @@ to import "github.com/filebrowser/filebrowser/bolt".
m := &fm.FileBrowser{ m := &fm.FileBrowser{
NoAuth: false, NoAuth: false,
Auth: {
Method: "default",
LoginHeader: "X-Fowarded-User"
},
DefaultUser: &fm.User{ DefaultUser: &fm.User{
AllowCommands: true, AllowCommands: true,
AllowEdit: true, AllowEdit: true,

View File

@ -71,6 +71,16 @@ type FileBrowser struct {
// there will only exist one user, called "admin". // there will only exist one user, called "admin".
NoAuth bool 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. // ReCaptcha host, key and secret.
ReCaptchaHost string ReCaptchaHost string
ReCaptchaKey string ReCaptchaKey string

View File

@ -51,20 +51,32 @@ func reCaptcha(host, secret, response string) (bool, error) {
// authHandler processes the authentication for the user. // authHandler processes the authentication for the user.
func authHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) { func authHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) {
// NoAuth instances shouldn't call this method.
if c.NoAuth { if c.NoAuth {
// NoAuth instances shouldn't call this method.
return 0, nil 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. // Receive the credentials from the request and unmarshal them.
var cred cred var cred cred
if r.Body == nil { if r.Body == nil {
return http.StatusForbidden, nil return http.StatusForbidden, nil
} }
err := json.NewDecoder(r.Body).Decode(&cred) err := json.NewDecoder(r.Body).Decode(&cred)
if err != nil { if err != nil {
return http.StatusForbidden, nil return http.StatusForbidden, err
} }
// If ReCaptcha is enabled, check the code. // 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 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) { keyFunc := func(token *jwt.Token) (interface{}, error) {
return c.Key, nil return c.Key, nil
} }