From 29e9935ca73c7fd2d4160fce5c27641426fae21c Mon Sep 17 00:00:00 2001 From: Guy Lewin Date: Tue, 25 Feb 2020 12:03:34 -0500 Subject: [PATCH] style: move auth configuration to auth.go and its implementations --- client/service.go | 2 +- models/auth/auth.go | 86 +++++++++++++++++---------- models/auth/oidc.go | 97 ++++++++++++++++++++++++------- models/auth/token.go | 21 +++++-- models/config/client_common.go | 92 +++++++++-------------------- models/config/server_common.go | 103 ++++++++++----------------------- server/service.go | 2 +- 7 files changed, 210 insertions(+), 193 deletions(-) diff --git a/client/service.go b/client/service.go index 18416d2e..297d3a36 100644 --- a/client/service.go +++ b/client/service.go @@ -73,7 +73,7 @@ func NewService(cfg config.ClientCommonConf, pxyCfgs map[string]config.ProxyConf ctx, cancel := context.WithCancel(context.Background()) svr = &Service{ - authSetter: auth.NewAuthSetter(cfg), + authSetter: auth.NewAuthSetter(cfg.AuthClientConfig), cfg: cfg, cfgFile: cfgFile, pxyCfgs: pxyCfgs, diff --git a/models/auth/auth.go b/models/auth/auth.go index 941f0035..a11609fc 100644 --- a/models/auth/auth.go +++ b/models/auth/auth.go @@ -15,14 +15,58 @@ package auth import ( - "github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/consts" "github.com/fatedier/frp/models/msg" ) -type baseAuth struct { - authenticateHeartBeats bool - authenticateNewWorkConns bool +type baseConfig struct { + // AuthenticationMethod specifies what authentication method to use to + // authenticate frpc with frps. If "token" is specified - token will be + // read into login message. If "oidc" is specified - OIDC (Open ID Connect) + // token will be issued using OIDC settings. By default, this value is "token". + AuthenticationMethod string `json:"authentication_method"` + // AuthenticateHeartBeats specifies whether to include authentication token in + // heartbeats sent to frps. By default, this value is false. + AuthenticateHeartBeats bool `json:"authenticate_heartbeats"` + // AuthenticateNewWorkConns specifies whether to include authentication token in + // new work connections sent to frps. By default, this value is false. + AuthenticateNewWorkConns bool `json:"authenticate_new_work_conns"` +} + +type AuthClientConfig struct { + baseConfig + oidcClientConfig + tokenConfig +} + +func GetDefaultClientConf() AuthClientConfig { + return AuthClientConfig{ + baseConfig: baseConfig{ + AuthenticationMethod: "token", + AuthenticateHeartBeats: false, + AuthenticateNewWorkConns: false, + }, + oidcClientConfig: getDefaultOidcClientConf(), + tokenConfig: getDefaultTokenConf(), + } +} + +type AuthServerConfig struct { + baseConfig + oidcServerConfig + tokenConfig +} + +func GetDefaultServerConf() AuthServerConfig { + return AuthServerConfig{ + baseConfig: baseConfig{ + AuthenticationMethod: "token", + AuthenticateHeartBeats: false, + AuthenticateNewWorkConns: false, + }, + oidcServerConfig: getDefaultOidcServerConf(), + tokenConfig: getDefaultTokenConf(), + } } type Setter interface { @@ -31,23 +75,12 @@ type Setter interface { SetNewWorkConn(*msg.NewWorkConn) error } -func NewAuthSetter(cfg config.ClientCommonConf) (authProvider Setter) { - base := baseAuth{ - authenticateHeartBeats: cfg.AuthenticateHeartBeats, - authenticateNewWorkConns: cfg.AuthenticateNewWorkConns, - } - +func NewAuthSetter(cfg AuthClientConfig) (authProvider Setter) { switch cfg.AuthenticationMethod { case consts.TokenAuthMethod: - authProvider = NewTokenAuth(base, cfg.Token) + authProvider = NewTokenAuth(cfg.baseConfig, cfg.tokenConfig) case consts.OidcAuthMethod: - authProvider = NewOidcAuthSetter( - base, - cfg.OidcClientId, - cfg.OidcClientSecret, - cfg.OidcAudience, - cfg.OidcTokenEndpointUrl, - ) + authProvider = NewOidcAuthSetter(cfg.baseConfig, cfg.oidcClientConfig) } return authProvider @@ -59,23 +92,12 @@ type Verifier interface { VerifyNewWorkConn(*msg.NewWorkConn) error } -func NewAuthVerifier(cfg config.ServerCommonConf) (authVerifier Verifier) { - base := baseAuth{ - authenticateHeartBeats: cfg.AuthenticateHeartBeats, - authenticateNewWorkConns: cfg.AuthenticateNewWorkConns, - } - +func NewAuthVerifier(cfg AuthServerConfig) (authVerifier Verifier) { switch cfg.AuthenticationMethod { case consts.TokenAuthMethod: - authVerifier = NewTokenAuth(base, cfg.Token) + authVerifier = NewTokenAuth(cfg.baseConfig, cfg.tokenConfig) case consts.OidcAuthMethod: - authVerifier = NewOidcAuthVerifier( - base, - cfg.OidcIssuer, - cfg.OidcAudience, - cfg.OidcSkipExpiryCheck, - cfg.OidcSkipIssuerCheck, - ) + authVerifier = NewOidcAuthVerifier(cfg.baseConfig, cfg.oidcServerConfig) } return authVerifier diff --git a/models/auth/oidc.go b/models/auth/oidc.go index f662b4c2..aa929954 100644 --- a/models/auth/oidc.go +++ b/models/auth/oidc.go @@ -24,22 +24,79 @@ import ( "golang.org/x/oauth2/clientcredentials" ) +type oidcClientConfig struct { + // OidcClientId specifies the client ID to use to get a token in OIDC + // authentication if AuthenticationMethod == "oidc". By default, this value + // is "". + OidcClientId string `json:"oidc_client_id"` + // OidcClientSecret specifies the client secret to use to get a token in OIDC + // authentication if AuthenticationMethod == "oidc". By default, this value + // is "". + OidcClientSecret string `json:"oidc_client_secret"` + // OidcAudience specifies the audience of the token in OIDC authentication + //if AuthenticationMethod == "oidc". By default, this value is "". + OidcAudience string `json:"oidc_audience"` + // OidcTokenEndpointUrl specifies the URL which implements OIDC Token Endpoint. + // It will be used to get an OIDC token if AuthenticationMethod == "oidc". + // By default, this value is "". + OidcTokenEndpointUrl string `json:"oidc_token_endpoint_url"` +} + +func getDefaultOidcClientConf() oidcClientConfig { + return oidcClientConfig{ + OidcClientId: "", + OidcClientSecret: "", + OidcAudience: "", + OidcTokenEndpointUrl: "", + } +} + +type oidcServerConfig struct { + // OidcIssuer specifies the issuer to verify OIDC tokens with. This issuer + // will be used to load public keys to verify signature and will be compared + // with the issuer claim in the OIDC token. It will be used if + // AuthenticationMethod == "oidc". By default, this value is "". + OidcIssuer string `json:"oidc_issuer"` + // OidcAudience specifies the audience OIDC tokens should contain when validated. + // If this value is empty, audience ("client ID") verification will be skipped. + // It will be used when AuthenticationMethod == "oidc". By default, this + // value is "". + OidcAudience string `json:"oidc_audience"` + // OidcSkipExpiryCheck specifies whether to skip checking if the OIDC token is + // expired. It will be used when AuthenticationMethod == "oidc". By default, this + // value is false. + OidcSkipExpiryCheck bool `json:"oidc_skip_expiry_check"` + // OidcSkipIssuerCheck specifies whether to skip checking if the OIDC token's + // issuer claim matches the issuer specified in OidcIssuer. It will be used when + // AuthenticationMethod == "oidc". By default, this value is false. + OidcSkipIssuerCheck bool `json:"oidc_skip_issuer_check"` +} + +func getDefaultOidcServerConf() oidcServerConfig { + return oidcServerConfig{ + OidcIssuer: "", + OidcAudience: "", + OidcSkipExpiryCheck: false, + OidcSkipIssuerCheck: false, + } +} + type OidcAuthProvider struct { - baseAuth + baseConfig tokenGenerator *clientcredentials.Config } -func NewOidcAuthSetter(base baseAuth, clientId string, clientSecret string, audience string, tokenEndpointUrl string) *OidcAuthProvider { +func NewOidcAuthSetter(baseCfg baseConfig, cfg oidcClientConfig) *OidcAuthProvider { tokenGenerator := &clientcredentials.Config{ - ClientID: clientId, - ClientSecret: clientSecret, - Scopes: []string{audience}, - TokenURL: tokenEndpointUrl, + ClientID: cfg.OidcClientId, + ClientSecret: cfg.OidcClientSecret, + Scopes: []string{cfg.OidcAudience}, + TokenURL: cfg.OidcTokenEndpointUrl, } return &OidcAuthProvider{ - baseAuth: base, + baseConfig: baseCfg, tokenGenerator: tokenGenerator, } } @@ -58,7 +115,7 @@ func (auth *OidcAuthProvider) SetLogin(loginMsg *msg.Login) (err error) { } func (auth *OidcAuthProvider) SetPing(pingMsg *msg.Ping) (err error) { - if !auth.authenticateHeartBeats { + if !auth.AuthenticateHeartBeats { return nil } @@ -67,7 +124,7 @@ func (auth *OidcAuthProvider) SetPing(pingMsg *msg.Ping) (err error) { } func (auth *OidcAuthProvider) SetNewWorkConn(newWorkConnMsg *msg.NewWorkConn) (err error) { - if !auth.authenticateNewWorkConns { + if !auth.AuthenticateNewWorkConns { return nil } @@ -76,26 +133,26 @@ func (auth *OidcAuthProvider) SetNewWorkConn(newWorkConnMsg *msg.NewWorkConn) (e } type OidcAuthConsumer struct { - baseAuth + baseConfig verifier *oidc.IDTokenVerifier subjectFromLogin string } -func NewOidcAuthVerifier(base baseAuth, issuer string, audience string, skipExpiryCheck bool, skipIssuerCheck bool) *OidcAuthConsumer { - provider, err := oidc.NewProvider(context.Background(), issuer) +func NewOidcAuthVerifier(baseCfg baseConfig, cfg oidcServerConfig) *OidcAuthConsumer { + provider, err := oidc.NewProvider(context.Background(), cfg.OidcIssuer) if err != nil { panic(err) } verifierConf := oidc.Config{ - ClientID: audience, - SkipClientIDCheck: audience == "", - SkipExpiryCheck: skipExpiryCheck, - SkipIssuerCheck: skipIssuerCheck, + ClientID: cfg.OidcAudience, + SkipClientIDCheck: cfg.OidcAudience == "", + SkipExpiryCheck: cfg.OidcSkipExpiryCheck, + SkipIssuerCheck: cfg.OidcSkipIssuerCheck, } return &OidcAuthConsumer{ - baseAuth: base, - verifier: provider.Verifier(&verifierConf), + baseConfig: baseCfg, + verifier: provider.Verifier(&verifierConf), } } @@ -123,7 +180,7 @@ func (auth *OidcAuthConsumer) verifyPostLoginToken(privilegeKey string) (err err } func (auth *OidcAuthConsumer) VerifyPing(pingMsg *msg.Ping) (err error) { - if !auth.authenticateHeartBeats { + if !auth.AuthenticateHeartBeats { return nil } @@ -131,7 +188,7 @@ func (auth *OidcAuthConsumer) VerifyPing(pingMsg *msg.Ping) (err error) { } func (auth *OidcAuthConsumer) VerifyNewWorkConn(newWorkConnMsg *msg.NewWorkConn) (err error) { - if !auth.authenticateNewWorkConns { + if !auth.AuthenticateNewWorkConns { return nil } diff --git a/models/auth/token.go b/models/auth/token.go index 3cdc704e..d7999f4d 100644 --- a/models/auth/token.go +++ b/models/auth/token.go @@ -21,16 +21,29 @@ import ( "github.com/fatedier/frp/utils/util" ) +type tokenConfig struct { + // Token specifies the authorization token used to create keys to be sent + // to the server. The server must have a matching token for authorization + // to succeed. By default, this value is "". + Token string `json:"token"` +} + +func getDefaultTokenConf() tokenConfig { + return tokenConfig{ + Token: "", + } +} + type TokenAuthSetterVerifier struct { - baseAuth + baseConfig token string } -func NewTokenAuth(base baseAuth, token string) *TokenAuthSetterVerifier { +func NewTokenAuth(baseCfg baseConfig, cfg tokenConfig) *TokenAuthSetterVerifier { return &TokenAuthSetterVerifier{ - baseAuth: base, - token: token, + baseConfig: baseCfg, + token: cfg.Token, } } diff --git a/models/config/client_common.go b/models/config/client_common.go index 5c68e56f..76103f85 100644 --- a/models/config/client_common.go +++ b/models/config/client_common.go @@ -21,12 +21,15 @@ import ( "strings" ini "github.com/vaughan0/go-ini" + + "github.com/fatedier/frp/models/auth" ) // ClientCommonConf contains information for a client service. It is // recommended to use GetDefaultClientConf instead of creating this object // directly, so that all unspecified fields have reasonable default values. type ClientCommonConf struct { + auth.AuthClientConfig // ServerAddr specifies the address of the server to connect to. By // default, this value is "0.0.0.0". ServerAddr string `json:"server_addr"` @@ -56,38 +59,6 @@ type ClientCommonConf struct { // DisableLogColor disables log colors when LogWay == "console" when set to // true. By default, this value is false. DisableLogColor bool `json:"disable_log_color"` - // Token specifies the authorization token used to create keys to be sent - // to the server. The server must have a matching token for authorization - // to succeed. By default, this value is "". - Token string `json:"token"` - // AuthenticationMethod specifies what authentication method to use to - // authenticate frpc with frps. If "token" is specified - token will be - // read into login message. If "oidc" is specified - OIDC (Open ID Connect) - // token will be issued using OIDC settings. By default, this value is "token". - AuthenticationMethod string `json:"authentication_method"` - // AuthenticateHeartBeats specifies whether to include authentication token in - // heartbeats sent to frps. By default, this value is false. - AuthenticateHeartBeats bool `json:"authenticate_heartbeats"` - // AuthenticateNewWorkConns specifies whether to include authentication token in - // new work connections sent to frps. By default, this value is false. - AuthenticateNewWorkConns bool `json:"authenticate_new_work_conns"` - - // OidcClientId specifies the client ID to use to get a token in OIDC - // authentication if AuthenticationMethod == "oidc". By default, this value - // is "". - OidcClientId string `json:"oidc_client_id"` - // OidcClientSecret specifies the client secret to use to get a token in OIDC - // authentication if AuthenticationMethod == "oidc". By default, this value - // is "". - OidcClientSecret string `json:"oidc_client_secret"` - // OidcAudience specifies the audience of the token in OIDC authentication - //if AuthenticationMethod == "oidc". By default, this value is "". - OidcAudience string `json:"oidc_audience"` - // OidcTokenEndpointUrl specifies the URL which implements OIDC Token Endpoint. - // It will be used to get an OIDC token if AuthenticationMethod == "oidc". - // By default, this value is "". - OidcTokenEndpointUrl string `json:"oidc_token_endpoint_url"` - // AdminAddr specifies the address that the admin server binds to. By // default, this value is "127.0.0.1". AdminAddr string `json:"admin_addr"` @@ -150,38 +121,31 @@ type ClientCommonConf struct { // GetDefaultClientConf returns a client configuration with default values. func GetDefaultClientConf() ClientCommonConf { return ClientCommonConf{ - ServerAddr: "0.0.0.0", - ServerPort: 7000, - HttpProxy: os.Getenv("http_proxy"), - LogFile: "console", - LogWay: "console", - LogLevel: "info", - LogMaxDays: 3, - DisableLogColor: false, - Token: "", - AuthenticationMethod: "token", - AuthenticateHeartBeats: false, - AuthenticateNewWorkConns: false, - OidcClientId: "", - OidcClientSecret: "", - OidcAudience: "", - OidcTokenEndpointUrl: "", - AdminAddr: "127.0.0.1", - AdminPort: 0, - AdminUser: "", - AdminPwd: "", - AssetsDir: "", - PoolCount: 1, - TcpMux: true, - User: "", - DnsServer: "", - LoginFailExit: true, - Start: make(map[string]struct{}), - Protocol: "tcp", - TLSEnable: false, - HeartBeatInterval: 30, - HeartBeatTimeout: 90, - Metas: make(map[string]string), + AuthClientConfig: auth.GetDefaultClientConf(), + ServerAddr: "0.0.0.0", + ServerPort: 7000, + HttpProxy: os.Getenv("http_proxy"), + LogFile: "console", + LogWay: "console", + LogLevel: "info", + LogMaxDays: 3, + DisableLogColor: false, + AdminAddr: "127.0.0.1", + AdminPort: 0, + AdminUser: "", + AdminPwd: "", + AssetsDir: "", + PoolCount: 1, + TcpMux: true, + User: "", + DnsServer: "", + LoginFailExit: true, + Start: make(map[string]struct{}), + Protocol: "tcp", + TLSEnable: false, + HeartBeatInterval: 30, + HeartBeatTimeout: 90, + Metas: make(map[string]string), } } diff --git a/models/config/server_common.go b/models/config/server_common.go index 065c78f8..254f80ac 100644 --- a/models/config/server_common.go +++ b/models/config/server_common.go @@ -21,6 +21,7 @@ import ( ini "github.com/vaughan0/go-ini" + "github.com/fatedier/frp/models/auth" plugin "github.com/fatedier/frp/models/plugin/server" "github.com/fatedier/frp/utils/util" ) @@ -29,6 +30,7 @@ import ( // recommended to use GetDefaultServerConf instead of creating this object // directly, so that all unspecified fields have reasonable default values. type ServerCommonConf struct { + auth.AuthServerConfig // BindAddr specifies the address that the server binds to. By default, // this value is "0.0.0.0". BindAddr string `json:"bind_addr"` @@ -101,40 +103,6 @@ type ServerCommonConf struct { // DetailedErrorsToClient defines whether to send the specific error (with // debug info) to frpc. By default, this value is true. DetailedErrorsToClient bool `json:"detailed_errors_to_client"` - // Token specifies the authorization token used to authenticate keys - // received from clients. Clients must have a matching token to be - // authorized to use the server. By default, this value is "". - Token string `json:"token"` - // AuthenticationMethod specifies what authentication method to use to - // authenticate frpc with frps. If "token" is specified - token comparison - // will be used. If "oidc" is specified - OIDC (Open ID Connect) will be - // used. By default, this value is "token". - AuthenticationMethod string `json:"authentication_method"` - // AuthenticateHeartBeats specifies whether to expect and verify authentication - // token in heartbeats sent from frpc. By default, this value is false. - AuthenticateHeartBeats bool `json:"authenticate_heartbeats"` - // AuthenticateNewWorkConns specifies whether to expect and verify authentication - // token in new work connections sent from frpc. By default, this value is false. - AuthenticateNewWorkConns bool `json:"authenticate_new_work_conns"` - - // OidcIssuer specifies the issuer to verify OIDC tokens with. This issuer - // will be used to load public keys to verify signature and will be compared - // with the issuer claim in the OIDC token. It will be used if - // AuthenticationMethod == "oidc". By default, this value is "". - OidcIssuer string `json:"oidc_issuer"` - // OidcAudience specifies the audience OIDC tokens should contain when validated. - // If this value is empty, audience ("client ID") verification will be skipped. - // It will be used when AuthenticationMethod == "oidc". By default, this - // value is "". - OidcAudience string `json:"oidc_audience"` - // OidcSkipExpiryCheck specifies whether to skip checking if the OIDC token is - // expired. It will be used when AuthenticationMethod == "oidc". By default, this - // value is false. - OidcSkipExpiryCheck bool `json:"oidc_skip_expiry_check"` - // OidcSkipIssuerCheck specifies whether to skip checking if the OIDC token's - // issuer claim matches the issuer specified in OidcIssuer. It will be used when - // AuthenticationMethod == "oidc". By default, this value is false. - OidcSkipIssuerCheck bool `json:"oidc_skip_issuer_check"` // SubDomainHost specifies the domain that will be attached to sub-domains // requested by the client when using Vhost proxying. For example, if this @@ -180,43 +148,36 @@ type ServerCommonConf struct { // defaults. func GetDefaultServerConf() ServerCommonConf { return ServerCommonConf{ - BindAddr: "0.0.0.0", - BindPort: 7000, - BindUdpPort: 0, - KcpBindPort: 0, - ProxyBindAddr: "0.0.0.0", - VhostHttpPort: 0, - VhostHttpsPort: 0, - VhostHttpTimeout: 60, - DashboardAddr: "0.0.0.0", - DashboardPort: 0, - DashboardUser: "admin", - DashboardPwd: "admin", - AssetsDir: "", - LogFile: "console", - LogWay: "console", - LogLevel: "info", - LogMaxDays: 3, - DisableLogColor: false, - DetailedErrorsToClient: true, - Token: "", - AuthenticationMethod: "token", - AuthenticateHeartBeats: false, - AuthenticateNewWorkConns: false, - OidcIssuer: "", - OidcAudience: "", - OidcSkipExpiryCheck: false, - OidcSkipIssuerCheck: false, - SubDomainHost: "", - TcpMux: true, - AllowPorts: make(map[int]struct{}), - MaxPoolCount: 5, - MaxPortsPerClient: 0, - TlsOnly: false, - HeartBeatTimeout: 90, - UserConnTimeout: 10, - Custom404Page: "", - HTTPPlugins: make(map[string]plugin.HTTPPluginOptions), + AuthServerConfig: auth.GetDefaultServerConf(), + BindAddr: "0.0.0.0", + BindPort: 7000, + BindUdpPort: 0, + KcpBindPort: 0, + ProxyBindAddr: "0.0.0.0", + VhostHttpPort: 0, + VhostHttpsPort: 0, + VhostHttpTimeout: 60, + DashboardAddr: "0.0.0.0", + DashboardPort: 0, + DashboardUser: "admin", + DashboardPwd: "admin", + AssetsDir: "", + LogFile: "console", + LogWay: "console", + LogLevel: "info", + LogMaxDays: 3, + DisableLogColor: false, + DetailedErrorsToClient: true, + SubDomainHost: "", + TcpMux: true, + AllowPorts: make(map[int]struct{}), + MaxPoolCount: 5, + MaxPortsPerClient: 0, + TlsOnly: false, + HeartBeatTimeout: 90, + UserConnTimeout: 10, + Custom404Page: "", + HTTPPlugins: make(map[string]plugin.HTTPPluginOptions), } } diff --git a/server/service.go b/server/service.go index 40712038..8f11099f 100644 --- a/server/service.go +++ b/server/service.go @@ -109,7 +109,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) { UdpPortManager: ports.NewPortManager("udp", cfg.ProxyBindAddr, cfg.AllowPorts), }, httpVhostRouter: vhost.NewVhostRouters(), - authVerifier: auth.NewAuthVerifier(cfg), + authVerifier: auth.NewAuthVerifier(cfg.AuthServerConfig), tlsConfig: generateTLSConfig(), cfg: cfg, }