mirror of
https://github.com/Luzifer/nginx-sso.git
synced 2024-12-20 12:51:17 +00:00
Move MFA plugins to own modules
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
8d968ce29d
commit
282a95c2e9
6 changed files with 48 additions and 39 deletions
11
core.go
11
core.go
|
@ -1,7 +1,16 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/Luzifer/nginx-sso/plugins/auth/google"
|
import (
|
||||||
|
"github.com/Luzifer/nginx-sso/plugins/auth/google"
|
||||||
|
"github.com/Luzifer/nginx-sso/plugins/mfa/duo"
|
||||||
|
"github.com/Luzifer/nginx-sso/plugins/mfa/totp"
|
||||||
|
mfa_yubikey "github.com/Luzifer/nginx-sso/plugins/mfa/yubikey"
|
||||||
|
)
|
||||||
|
|
||||||
func registerModules() {
|
func registerModules() {
|
||||||
registerAuthenticator(google.New(cookieStore))
|
registerAuthenticator(google.New(cookieStore))
|
||||||
|
|
||||||
|
registerMFAProvider(duo.New())
|
||||||
|
registerMFAProvider(totp.New())
|
||||||
|
registerMFAProvider(mfa_yubikey.New())
|
||||||
}
|
}
|
||||||
|
|
4
mfa.go
4
mfa.go
|
@ -10,11 +10,9 @@ import (
|
||||||
"github.com/Luzifer/nginx-sso/plugins"
|
"github.com/Luzifer/nginx-sso/plugins"
|
||||||
)
|
)
|
||||||
|
|
||||||
const mfaLoginFieldName = "mfa-token"
|
|
||||||
|
|
||||||
var mfaLoginField = plugins.LoginField{
|
var mfaLoginField = plugins.LoginField{
|
||||||
Label: "MFA Token",
|
Label: "MFA Token",
|
||||||
Name: mfaLoginFieldName,
|
Name: plugins.MFALoginFieldName,
|
||||||
Placeholder: "(optional)",
|
Placeholder: "(optional)",
|
||||||
Type: "text",
|
Type: "text",
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package plugins
|
||||||
|
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
|
const MFALoginFieldName = "mfa-token"
|
||||||
|
|
||||||
type MFAProvider interface {
|
type MFAProvider interface {
|
||||||
// ProviderID needs to return an unique string to identify
|
// ProviderID needs to return an unique string to identify
|
||||||
// this special MFA provider
|
// this special MFA provider
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package duo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
@ -21,29 +21,29 @@ const (
|
||||||
|
|
||||||
var mfaDuoTrustedIPHeaders = []string{"X-Forwarded-For", "X-Real-IP"}
|
var mfaDuoTrustedIPHeaders = []string{"X-Forwarded-For", "X-Real-IP"}
|
||||||
|
|
||||||
func init() {
|
type MFADuo struct {
|
||||||
registerMFAProvider(&mfaDuo{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type mfaDuo struct {
|
|
||||||
IKey string `yaml:"ikey"`
|
IKey string `yaml:"ikey"`
|
||||||
SKey string `yaml:"skey"`
|
SKey string `yaml:"skey"`
|
||||||
Host string `yaml:"host"`
|
Host string `yaml:"host"`
|
||||||
UserAgent string `yaml:"user_agent"`
|
UserAgent string `yaml:"user_agent"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func New() *MFADuo {
|
||||||
|
return &MFADuo{}
|
||||||
|
}
|
||||||
|
|
||||||
// ProviderID needs to return an unique string to identify
|
// ProviderID needs to return an unique string to identify
|
||||||
// this special MFA provider
|
// this special MFA provider
|
||||||
func (m mfaDuo) ProviderID() (id string) { return "duo" }
|
func (m MFADuo) ProviderID() (id string) { return "duo" }
|
||||||
|
|
||||||
// Configure loads the configuration for the Authenticator from the
|
// Configure loads the configuration for the Authenticator from the
|
||||||
// global config.yaml file which is passed as a byte-slice.
|
// global config.yaml file which is passed as a byte-slice.
|
||||||
// If no configuration for the Authenticator is supplied the function
|
// If no configuration for the Authenticator is supplied the function
|
||||||
// needs to return the plugins.ErrProviderUnconfigured
|
// needs to return the plugins.ErrProviderUnconfigured
|
||||||
func (m *mfaDuo) Configure(yamlSource []byte) (err error) {
|
func (m *MFADuo) Configure(yamlSource []byte) (err error) {
|
||||||
envelope := struct {
|
envelope := struct {
|
||||||
MFA struct {
|
MFA struct {
|
||||||
Duo *mfaDuo `yaml:"duo"`
|
Duo *MFADuo `yaml:"duo"`
|
||||||
} `yaml:"mfa"`
|
} `yaml:"mfa"`
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ func (m *mfaDuo) Configure(yamlSource []byte) (err error) {
|
||||||
|
|
||||||
// ValidateMFA takes the user from the login cookie and performs a
|
// ValidateMFA takes the user from the login cookie and performs a
|
||||||
// validation against the provided MFA configuration for this user
|
// validation against the provided MFA configuration for this user
|
||||||
func (m mfaDuo) ValidateMFA(res http.ResponseWriter, r *http.Request, user string, mfaCfgs []plugins.MFAConfig) error {
|
func (m MFADuo) ValidateMFA(res http.ResponseWriter, r *http.Request, user string, mfaCfgs []plugins.MFAConfig) error {
|
||||||
var keyInput string
|
var keyInput string
|
||||||
|
|
||||||
// Look for mfaConfigs with own provider name
|
// Look for mfaConfigs with own provider name
|
||||||
|
@ -81,7 +81,7 @@ func (m mfaDuo) ValidateMFA(res http.ResponseWriter, r *http.Request, user strin
|
||||||
duo := authapi.NewAuthApi(*duoapi.NewDuoApi(m.IKey, m.SKey, m.Host, m.UserAgent, duoapi.SetTimeout(mfaDuoRequestTimeout)))
|
duo := authapi.NewAuthApi(*duoapi.NewDuoApi(m.IKey, m.SKey, m.Host, m.UserAgent, duoapi.SetTimeout(mfaDuoRequestTimeout)))
|
||||||
|
|
||||||
for key, values := range r.Form {
|
for key, values := range r.Form {
|
||||||
if strings.HasSuffix(key, mfaLoginFieldName) && len(values[0]) > 0 {
|
if strings.HasSuffix(key, plugins.MFALoginFieldName) && len(values[0]) > 0 {
|
||||||
keyInput = values[0]
|
keyInput = values[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ func (m mfaDuo) ValidateMFA(res http.ResponseWriter, r *http.Request, user strin
|
||||||
return plugins.ErrNoValidUserFound
|
return plugins.ErrNoValidUserFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m mfaDuo) findIP(r *http.Request) (string, error) {
|
func (m MFADuo) findIP(r *http.Request) (string, error) {
|
||||||
for _, hdr := range mfaDuoTrustedIPHeaders {
|
for _, hdr := range mfaDuoTrustedIPHeaders {
|
||||||
if value := r.Header.Get(hdr); value != "" {
|
if value := r.Header.Get(hdr); value != "" {
|
||||||
return m.parseIP(value)
|
return m.parseIP(value)
|
||||||
|
@ -118,7 +118,7 @@ func (m mfaDuo) findIP(r *http.Request) (string, error) {
|
||||||
return m.parseIP(r.RemoteAddr)
|
return m.parseIP(r.RemoteAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m mfaDuo) parseIP(s string) (string, error) {
|
func (m MFADuo) parseIP(s string) (string, error) {
|
||||||
ip, _, err := net.SplitHostPort(s)
|
ip, _, err := net.SplitHostPort(s)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return ip, nil
|
return ip, nil
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package totp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -12,27 +12,27 @@ import (
|
||||||
"github.com/Luzifer/nginx-sso/plugins"
|
"github.com/Luzifer/nginx-sso/plugins"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
type MFATOTP struct{}
|
||||||
registerMFAProvider(&mfaTOTP{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type mfaTOTP struct{}
|
|
||||||
|
|
||||||
// ProviderID needs to return an unique string to identify
|
// ProviderID needs to return an unique string to identify
|
||||||
// this special MFA provider
|
// this special MFA provider
|
||||||
func (m mfaTOTP) ProviderID() (id string) {
|
func (m MFATOTP) ProviderID() (id string) {
|
||||||
return "totp"
|
return "totp"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func New() *MFATOTP {
|
||||||
|
return &MFATOTP{}
|
||||||
|
}
|
||||||
|
|
||||||
// Configure loads the configuration for the Authenticator from the
|
// Configure loads the configuration for the Authenticator from the
|
||||||
// global config.yaml file which is passed as a byte-slice.
|
// global config.yaml file which is passed as a byte-slice.
|
||||||
// If no configuration for the Authenticator is supplied the function
|
// If no configuration for the Authenticator is supplied the function
|
||||||
// needs to return the plugins.ErrProviderUnconfigured
|
// needs to return the plugins.ErrProviderUnconfigured
|
||||||
func (m mfaTOTP) Configure(yamlSource []byte) (err error) { return nil }
|
func (m MFATOTP) Configure(yamlSource []byte) (err error) { return nil }
|
||||||
|
|
||||||
// ValidateMFA takes the user from the login cookie and performs a
|
// ValidateMFA takes the user from the login cookie and performs a
|
||||||
// validation against the provided MFA configuration for this user
|
// validation against the provided MFA configuration for this user
|
||||||
func (m mfaTOTP) ValidateMFA(res http.ResponseWriter, r *http.Request, user string, mfaCfgs []plugins.MFAConfig) error {
|
func (m MFATOTP) ValidateMFA(res http.ResponseWriter, r *http.Request, user string, mfaCfgs []plugins.MFAConfig) error {
|
||||||
// Look for mfaConfigs with own provider name
|
// Look for mfaConfigs with own provider name
|
||||||
for _, c := range mfaCfgs {
|
for _, c := range mfaCfgs {
|
||||||
// Provider has been renamed, keep "google" for backwards compatibility
|
// Provider has been renamed, keep "google" for backwards compatibility
|
||||||
|
@ -46,7 +46,7 @@ func (m mfaTOTP) ValidateMFA(res http.ResponseWriter, r *http.Request, user stri
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, values := range r.Form {
|
for key, values := range r.Form {
|
||||||
if strings.HasSuffix(key, mfaLoginFieldName) && values[0] == token {
|
if strings.HasSuffix(key, plugins.MFALoginFieldName) && values[0] == token {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ func (m mfaTOTP) ValidateMFA(res http.ResponseWriter, r *http.Request, user stri
|
||||||
return plugins.ErrNoValidUserFound
|
return plugins.ErrNoValidUserFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m mfaTOTP) exec(c plugins.MFAConfig) (string, error) {
|
func (m MFATOTP) exec(c plugins.MFAConfig) (string, error) {
|
||||||
secret := c.AttributeString("secret")
|
secret := c.AttributeString("secret")
|
||||||
|
|
||||||
// By default use Google Authenticator compatible settings
|
// By default use Google Authenticator compatible settings
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package yubikey
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -11,27 +11,27 @@ import (
|
||||||
"github.com/Luzifer/nginx-sso/plugins"
|
"github.com/Luzifer/nginx-sso/plugins"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
type MFAYubikey struct {
|
||||||
registerMFAProvider(&mfaYubikey{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type mfaYubikey struct {
|
|
||||||
ClientID string `yaml:"client_id"`
|
ClientID string `yaml:"client_id"`
|
||||||
SecretKey string `yaml:"secret_key"`
|
SecretKey string `yaml:"secret_key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func New() *MFAYubikey {
|
||||||
|
return &MFAYubikey{}
|
||||||
|
}
|
||||||
|
|
||||||
// ProviderID needs to return an unique string to identify
|
// ProviderID needs to return an unique string to identify
|
||||||
// this special MFA provider
|
// this special MFA provider
|
||||||
func (m mfaYubikey) ProviderID() (id string) { return "yubikey" }
|
func (m MFAYubikey) ProviderID() (id string) { return "yubikey" }
|
||||||
|
|
||||||
// Configure loads the configuration for the Authenticator from the
|
// Configure loads the configuration for the Authenticator from the
|
||||||
// global config.yaml file which is passed as a byte-slice.
|
// global config.yaml file which is passed as a byte-slice.
|
||||||
// If no configuration for the Authenticator is supplied the function
|
// If no configuration for the Authenticator is supplied the function
|
||||||
// needs to return the plugins.ErrProviderUnconfigured
|
// needs to return the plugins.ErrProviderUnconfigured
|
||||||
func (m *mfaYubikey) Configure(yamlSource []byte) (err error) {
|
func (m *MFAYubikey) Configure(yamlSource []byte) (err error) {
|
||||||
envelope := struct {
|
envelope := struct {
|
||||||
MFA struct {
|
MFA struct {
|
||||||
Yubikey *mfaYubikey `yaml:"yubikey"`
|
Yubikey *MFAYubikey `yaml:"yubikey"`
|
||||||
} `yaml:"mfa"`
|
} `yaml:"mfa"`
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ func (m *mfaYubikey) Configure(yamlSource []byte) (err error) {
|
||||||
|
|
||||||
// ValidateMFA takes the user from the login cookie and performs a
|
// ValidateMFA takes the user from the login cookie and performs a
|
||||||
// validation against the provided MFA configuration for this user
|
// validation against the provided MFA configuration for this user
|
||||||
func (m mfaYubikey) ValidateMFA(res http.ResponseWriter, r *http.Request, user string, mfaCfgs []plugins.MFAConfig) error {
|
func (m MFAYubikey) ValidateMFA(res http.ResponseWriter, r *http.Request, user string, mfaCfgs []plugins.MFAConfig) error {
|
||||||
var keyInput string
|
var keyInput string
|
||||||
|
|
||||||
yubiAuth, err := yubigo.NewYubiAuth(m.ClientID, m.SecretKey)
|
yubiAuth, err := yubigo.NewYubiAuth(m.ClientID, m.SecretKey)
|
||||||
|
@ -65,7 +65,7 @@ func (m mfaYubikey) ValidateMFA(res http.ResponseWriter, r *http.Request, user s
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, values := range r.Form {
|
for key, values := range r.Form {
|
||||||
if strings.HasSuffix(key, mfaLoginFieldName) && strings.HasPrefix(values[0], c.AttributeString("device")) {
|
if strings.HasSuffix(key, plugins.MFALoginFieldName) && strings.HasPrefix(values[0], c.AttributeString("device")) {
|
||||||
keyInput = values[0]
|
keyInput = values[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue