mirror of
https://github.com/ncarlier/webhookd.git
synced 2025-04-09 15:08:26 +00:00
feat(security): add http basic auth
This commit is contained in:
parent
ac754d93ac
commit
77a8946115
8
.env
8
.env
|
@ -37,4 +37,12 @@ APP_HTTP_NOTIFIER_USER=api:key-xxxxxxxxxxxxxxxxxxxxxxxxxx
|
|||
# Defaults: localhost:25
|
||||
APP_SMTP_NOTIFIER_HOST=localhost:25
|
||||
|
||||
# Authentication Method
|
||||
# Defaults: none
|
||||
# Values:
|
||||
# - "none" : No authentication (defaults).
|
||||
# - "basic": HTTP Basic authentication.
|
||||
AUTH=none
|
||||
|
||||
# Authentication Parameter
|
||||
AUTH_PARAM=username:password
|
53
config.go
53
config.go
|
@ -1,31 +1,64 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/ncarlier/webhookd/pkg/auth"
|
||||
)
|
||||
|
||||
// Config contain global configuration
|
||||
type Config struct {
|
||||
ListenAddr *string
|
||||
NbWorkers *int
|
||||
Debug *bool
|
||||
Timeout *int
|
||||
ScriptDir *string
|
||||
ListenAddr *string
|
||||
NbWorkers *int
|
||||
Debug *bool
|
||||
Timeout *int
|
||||
ScriptDir *string
|
||||
Authentication *string
|
||||
AuthenticationParam *string
|
||||
}
|
||||
|
||||
type authFlag struct {
|
||||
selectedMethod auth.Method
|
||||
}
|
||||
|
||||
func (c authFlag) Set(arg string) error {
|
||||
fmt.Println(arg)
|
||||
return errors.New("fall")
|
||||
}
|
||||
|
||||
func (c authFlag) String() string {
|
||||
return "test"
|
||||
}
|
||||
|
||||
var config = &Config{
|
||||
ListenAddr: flag.String("listen", getEnv("LISTEN_ADDR", ":8080"), "HTTP service address (e.g.address, ':8080')"),
|
||||
NbWorkers: flag.Int("nb-workers", getIntEnv("NB_WORKERS", 2), "The number of workers to start"),
|
||||
Debug: flag.Bool("debug", getBoolEnv("DEBUG", false), "Output debug logs"),
|
||||
Timeout: flag.Int("timeout", getIntEnv("HOOK_TIMEOUT", 10), "Hook maximum delay before timeout (in second)"),
|
||||
ScriptDir: flag.String("scripts", getEnv("SCRIPTS_DIR", "scripts"), "Scripts directory"),
|
||||
ListenAddr: flag.String("listen", getEnv("LISTEN_ADDR", ":8080"), "HTTP service address (e.g.address, ':8080')"),
|
||||
NbWorkers: flag.Int("nb-workers", getIntEnv("NB_WORKERS", 2), "The number of workers to start"),
|
||||
Debug: flag.Bool("debug", getBoolEnv("DEBUG", false), "Output debug logs"),
|
||||
Timeout: flag.Int("timeout", getIntEnv("HOOK_TIMEOUT", 10), "Hook maximum delay before timeout (in second)"),
|
||||
ScriptDir: flag.String("scripts", getEnv("SCRIPTS_DIR", "scripts"), "Scripts directory"),
|
||||
Authentication: flag.String("auth", getEnv("AUTH", "none"), ""),
|
||||
AuthenticationParam: flag.String("authparam", getEnv("AUTH_PARAM", ""), func() string {
|
||||
authdocwriter := bytes.NewBufferString("Authentication method. Available methods: ")
|
||||
|
||||
for key, method := range auth.AvailableMethods {
|
||||
authdocwriter.WriteRune('\n')
|
||||
authdocwriter.WriteString(key)
|
||||
authdocwriter.WriteRune(':')
|
||||
authdocwriter.WriteString(method.Usage())
|
||||
}
|
||||
return authdocwriter.String()
|
||||
}()),
|
||||
}
|
||||
|
||||
func init() {
|
||||
flag.StringVar(config.ListenAddr, "l", *config.ListenAddr, "HTTP service (e.g address: ':8080')")
|
||||
flag.BoolVar(config.Debug, "d", *config.Debug, "Output debug logs")
|
||||
|
||||
}
|
||||
|
||||
func getEnv(key, fallback string) string {
|
||||
|
|
19
main.go
19
main.go
|
@ -12,6 +12,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ncarlier/webhookd/pkg/api"
|
||||
"github.com/ncarlier/webhookd/pkg/auth"
|
||||
"github.com/ncarlier/webhookd/pkg/logger"
|
||||
"github.com/ncarlier/webhookd/pkg/worker"
|
||||
)
|
||||
|
@ -34,6 +35,20 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
var authmethod auth.Method
|
||||
name := *config.Authentication
|
||||
if _, ok := auth.AvailableMethods[name]; ok {
|
||||
authmethod = auth.AvailableMethods[name]
|
||||
if err := authmethod.ParseParam(*config.AuthenticationParam); err != nil {
|
||||
fmt.Println("Authentication parameter is not valid:", err.Error())
|
||||
fmt.Println(authmethod.Usage())
|
||||
os.Exit(2)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Authentication name is not valid:", name)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
level := "info"
|
||||
if *config.Debug {
|
||||
level = "debug"
|
||||
|
@ -41,6 +56,8 @@ func main() {
|
|||
logger.Init(level)
|
||||
|
||||
logger.Debug.Println("Starting webhookd server...")
|
||||
logger.Info.Println("Using Authentication:", name)
|
||||
authmethod.Init(*config.Debug)
|
||||
|
||||
router := http.NewServeMux()
|
||||
router.Handle("/", api.Index(*config.Timeout, *config.ScriptDir))
|
||||
|
@ -52,7 +69,7 @@ func main() {
|
|||
|
||||
server := &http.Server{
|
||||
Addr: *config.ListenAddr,
|
||||
Handler: tracing(nextRequestID)(logging(logger.Debug)(router)),
|
||||
Handler: authmethod.Middleware()(tracing(nextRequestID)(logging(logger.Debug)(router))),
|
||||
ErrorLog: logger.Error,
|
||||
}
|
||||
|
||||
|
|
19
pkg/auth/authmethod.go
Normal file
19
pkg/auth/authmethod.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package auth
|
||||
|
||||
import "net/http"
|
||||
|
||||
// Method an interface describing an authentication method
|
||||
type Method interface {
|
||||
Init(debug bool)
|
||||
Usage() string
|
||||
ParseParam(string) error
|
||||
Middleware() func(http.Handler) http.Handler
|
||||
}
|
||||
|
||||
var (
|
||||
// AvailableMethods Returns a map of available auth methods
|
||||
AvailableMethods = map[string]Method{
|
||||
"none": new(noAuth),
|
||||
"basic": new(basicAuth),
|
||||
}
|
||||
)
|
69
pkg/auth/basic.go
Normal file
69
pkg/auth/basic.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/ncarlier/webhookd/pkg/logger"
|
||||
)
|
||||
|
||||
type basicAuth struct {
|
||||
username string
|
||||
password string
|
||||
debug bool
|
||||
authheader string
|
||||
}
|
||||
|
||||
func (c *basicAuth) Init(debug bool) {
|
||||
c.debug = debug
|
||||
logger.Warning.Println("\u001B[33mBasic Auth: Debug mode enabled. Might Leak sentitive information in log output.\u001B[0m")
|
||||
}
|
||||
|
||||
func (c *basicAuth) Usage() string {
|
||||
return "HTTP Basic Auth. Usage: -auth basic -authparam <username>:<password>[:<realm>] (example: -auth basic -authparam foo:bar)"
|
||||
}
|
||||
|
||||
func (c *basicAuth) ParseParam(param string) error {
|
||||
res := strings.Split(param, ":")
|
||||
realm := "Authentication required."
|
||||
switch len(res) {
|
||||
case 3:
|
||||
realm = res[2]
|
||||
fallthrough
|
||||
case 2:
|
||||
c.username, c.password = res[0], res[1]
|
||||
c.authheader = fmt.Sprintf("Basic realm=\"%s\"", realm)
|
||||
return nil
|
||||
}
|
||||
return errors.New("Invalid Auth param")
|
||||
|
||||
}
|
||||
|
||||
// BasicAuth HTTP Basic Auth implementation
|
||||
func (c *basicAuth) Middleware() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if username, password, ok := r.BasicAuth(); ok && username == c.username && password == c.password {
|
||||
if c.debug {
|
||||
logger.Debug.Printf("HTTP Basic Auth: %s:%s PASSED\n", username, password)
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
} else if !ok {
|
||||
if c.debug {
|
||||
logger.Debug.Println("HTTP Basic Auth: Auth header not present.")
|
||||
}
|
||||
w.Header().Add("WWW-Authenticate", c.authheader)
|
||||
w.WriteHeader(401)
|
||||
w.Write([]byte("Authentication required."))
|
||||
} else {
|
||||
if c.debug {
|
||||
logger.Debug.Printf("HTTP Basic Auth: Invalid credentials: %s:%s \n", username, password)
|
||||
}
|
||||
w.WriteHeader(403)
|
||||
w.Write([]byte("Forbidden."))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
25
pkg/auth/none.go
Normal file
25
pkg/auth/none.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type noAuth struct {
|
||||
}
|
||||
|
||||
func (c *noAuth) Usage() string {
|
||||
return "No Auth. Usage: --auth none"
|
||||
}
|
||||
|
||||
func (c *noAuth) Init(_ bool) {}
|
||||
|
||||
func (c *noAuth) ParseParam(_ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NoAuth A Nop Auth middleware
|
||||
func (c *noAuth) Middleware() func(http.Handler) http.Handler {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return h
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user