feat: authenticate NewWorkConn messages, similar to ping

This commit is contained in:
Guy Lewin 2020-02-22 11:14:00 -05:00
parent 5544e5f7d5
commit 4877c78312
8 changed files with 180 additions and 101 deletions

View File

@ -142,6 +142,10 @@ func (ctl *Control) HandleReqWorkConn(inMsg *msg.ReqWorkConn) {
m := &msg.NewWorkConn{
RunId: ctl.runId,
}
if err = ctl.authSetter.SetNewWorkConn(m); err != nil {
xl.Warn("error during NewWorkConn authentication: %v", err)
return
}
if err = msg.WriteMsg(workConn, m); err != nil {
xl.Warn("work connection write to server error: %v", err)
workConn.Close()

View File

@ -20,46 +20,63 @@ import (
"github.com/fatedier/frp/models/msg"
)
type BaseAuth struct {
authenticateHeartBeats bool
authenticateNewWorkConns bool
}
type Setter interface {
SetLogin(*msg.Login) error
SetPing(*msg.Ping) error
SetNewWorkConn(*msg.NewWorkConn) error
}
func NewAuthSetter(cfg config.ClientCommonConf) (authProvider Setter) {
baseAuth := BaseAuth{
authenticateHeartBeats: cfg.AuthenticateHeartBeats,
authenticateNewWorkConns: cfg.AuthenticateNewWorkConns,
}
switch cfg.AuthenticationMethod {
case consts.TokenAuthMethod:
authProvider = NewTokenAuth(cfg.Token)
authProvider = NewTokenAuth(baseAuth, cfg.Token)
case consts.OidcAuthMethod:
authProvider = NewOidcAuthSetter(
baseAuth,
cfg.OidcClientId,
cfg.OidcClientSecret,
cfg.OidcAudience,
cfg.OidcTokenEndpointUrl,
cfg.AuthenticateHeartBeats,
)
}
return
return authProvider
}
type Verifier interface {
VerifyLogin(*msg.Login) error
VerifyPing(*msg.Ping) error
VerifyNewWorkConn(*msg.NewWorkConn) error
}
func NewAuthVerifier(cfg config.ServerCommonConf) (authVerifier Verifier) {
baseAuth := BaseAuth{
authenticateHeartBeats: cfg.AuthenticateHeartBeats,
authenticateNewWorkConns: cfg.AuthenticateNewWorkConns,
}
switch cfg.AuthenticationMethod {
case consts.TokenAuthMethod:
authVerifier = NewTokenAuth(cfg.Token)
authVerifier = NewTokenAuth(baseAuth, cfg.Token)
case consts.OidcAuthMethod:
authVerifier = NewOidcAuthVerifier(
baseAuth,
cfg.OidcIssuer,
cfg.OidcAudience,
cfg.OidcSkipExpiryCheck,
cfg.OidcSkipIssuerCheck,
cfg.AuthenticateHeartBeats,
)
}
return
return authVerifier
}

View File

@ -25,11 +25,12 @@ import (
)
type OidcAuthProvider struct {
BaseAuth
tokenGenerator *clientcredentials.Config
authenticateHeartBeats bool
}
func NewOidcAuthSetter(clientId string, clientSecret string, audience string, tokenEndpointUrl string, authenticateHeartBeats bool) *OidcAuthProvider {
func NewOidcAuthSetter(baseAuth BaseAuth, clientId string, clientSecret string, audience string, tokenEndpointUrl string) *OidcAuthProvider {
tokenGenerator := &clientcredentials.Config{
ClientID: clientId,
ClientSecret: clientSecret,
@ -38,8 +39,8 @@ func NewOidcAuthSetter(clientId string, clientSecret string, audience string, to
}
return &OidcAuthProvider{
BaseAuth: baseAuth,
tokenGenerator: tokenGenerator,
authenticateHeartBeats: authenticateHeartBeats,
}
}
@ -65,13 +66,23 @@ func (auth *OidcAuthProvider) SetPing(pingMsg *msg.Ping) (err error) {
return err
}
func (auth *OidcAuthProvider) SetNewWorkConn(newWorkConnMsg *msg.NewWorkConn) (err error) {
if !auth.authenticateNewWorkConns {
return nil
}
newWorkConnMsg.PrivilegeKey, err = auth.generateAccessToken()
return err
}
type OidcAuthConsumer struct {
BaseAuth
verifier *oidc.IDTokenVerifier
authenticateHeartBeats bool
subjectFromLogin string
}
func NewOidcAuthVerifier(issuer string, audience string, skipExpiryCheck bool, skipIssuerCheck bool, authenticateHeartBeats bool) *OidcAuthConsumer {
func NewOidcAuthVerifier(baseAuth BaseAuth, issuer string, audience string, skipExpiryCheck bool, skipIssuerCheck bool) *OidcAuthConsumer {
provider, err := oidc.NewProvider(context.Background(), issuer)
if err != nil {
panic(err)
@ -83,8 +94,8 @@ func NewOidcAuthVerifier(issuer string, audience string, skipExpiryCheck bool, s
SkipIssuerCheck: skipIssuerCheck,
}
return &OidcAuthConsumer{
BaseAuth: baseAuth,
verifier: provider.Verifier(&verifierConf),
authenticateHeartBeats: authenticateHeartBeats,
}
}
@ -97,12 +108,8 @@ func (auth *OidcAuthConsumer) VerifyLogin(loginMsg *msg.Login) (err error) {
return nil
}
func (auth *OidcAuthConsumer) VerifyPing(pingMsg *msg.Ping) (err error) {
if !auth.authenticateHeartBeats {
return nil
}
token, err := auth.verifier.Verify(context.Background(), pingMsg.PrivilegeKey)
func (auth *OidcAuthConsumer) verifyPostLoginToken(privilegeKey string) (err error) {
token, err := auth.verifier.Verify(context.Background(), privilegeKey)
if err != nil {
return fmt.Errorf("invalid OIDC token in ping: %v", err)
}
@ -114,3 +121,19 @@ func (auth *OidcAuthConsumer) VerifyPing(pingMsg *msg.Ping) (err error) {
}
return nil
}
func (auth *OidcAuthConsumer) VerifyPing(pingMsg *msg.Ping) (err error) {
if !auth.authenticateHeartBeats {
return nil
}
return auth.verifyPostLoginToken(pingMsg.PrivilegeKey)
}
func (auth *OidcAuthConsumer) VerifyNewWorkConn(newWorkConnMsg *msg.NewWorkConn) (err error) {
if !auth.authenticateNewWorkConns {
return nil
}
return auth.verifyPostLoginToken(newWorkConnMsg.PrivilegeKey)
}

View File

@ -22,37 +22,46 @@ import (
)
type TokenAuthSetterVerifier struct {
Token string
BaseAuth
token string
}
func NewTokenAuth(token string) *TokenAuthSetterVerifier {
func NewTokenAuth(baseAuth BaseAuth, token string) *TokenAuthSetterVerifier {
return &TokenAuthSetterVerifier{
Token: token,
BaseAuth: baseAuth,
token: token,
}
}
func (auth *TokenAuthSetterVerifier) SetLogin(loginMsg *msg.Login) (err error) {
loginMsg.PrivilegeKey = util.GetAuthKey(auth.Token, loginMsg.Timestamp)
loginMsg.PrivilegeKey = util.GetAuthKey(auth.token, loginMsg.Timestamp)
return nil
}
func (auth *TokenAuthSetterVerifier) SetPing(*msg.Ping) error {
// ping doesn't include authentication in token method
// Ping doesn't include authentication in token method
return nil
}
type TokenAuthConsumer struct {
Token string
func (auth *TokenAuthSetterVerifier) SetNewWorkConn(*msg.NewWorkConn) error {
// NewWorkConn doesn't include authentication in token method
return nil
}
func (auth *TokenAuthSetterVerifier) VerifyLogin(loginMsg *msg.Login) error {
if util.GetAuthKey(auth.Token, loginMsg.Timestamp) != loginMsg.PrivilegeKey {
if util.GetAuthKey(auth.token, loginMsg.Timestamp) != loginMsg.PrivilegeKey {
return fmt.Errorf("token in login doesn't match token from configuration")
}
return nil
}
func (auth *TokenAuthSetterVerifier) VerifyPing(*msg.Ping) error {
// ping doesn't include authentication in token method
// Ping doesn't include authentication in token method
return nil
}
func (auth *TokenAuthSetterVerifier) VerifyNewWorkConn(*msg.NewWorkConn) error {
// NewWorkConn doesn't include authentication in token method
return nil
}

View File

@ -68,6 +68,9 @@ type ClientCommonConf struct {
// 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
@ -158,6 +161,7 @@ func GetDefaultClientConf() ClientCommonConf {
Token: "",
AuthenticationMethod: "token",
AuthenticateHeartBeats: false,
AuthenticateNewWorkConns: false,
OidcClientId: "",
OidcClientSecret: "",
OidcAudience: "",
@ -248,6 +252,12 @@ func UnmarshalClientConfFromIni(content string) (cfg ClientCommonConf, err error
cfg.AuthenticateHeartBeats = false
}
if tmpStr, ok = conf.Get("common", "authenticate_new_work_conns"); ok && tmpStr == "true" {
cfg.AuthenticateNewWorkConns = true
} else {
cfg.AuthenticateNewWorkConns = false
}
if tmpStr, ok = conf.Get("common", "oidc_client_id"); ok {
cfg.OidcClientId = tmpStr
}

View File

@ -113,6 +113,9 @@ type ServerCommonConf struct {
// 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
@ -199,6 +202,7 @@ func GetDefaultServerConf() ServerCommonConf {
Token: "",
AuthenticationMethod: "token",
AuthenticateHeartBeats: false,
AuthenticateNewWorkConns: false,
OidcIssuer: "",
OidcAudience: "",
OidcSkipExpiryCheck: false,
@ -374,6 +378,12 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error
cfg.AuthenticateHeartBeats = false
}
if tmpStr, ok = conf.Get("common", "authenticate_new_work_conns"); ok && tmpStr == "true" {
cfg.AuthenticateNewWorkConns = true
} else {
cfg.AuthenticateNewWorkConns = false
}
if tmpStr, ok = conf.Get("common", "oidc_issuer"); ok {
cfg.OidcIssuer = tmpStr
}

View File

@ -121,6 +121,7 @@ type CloseProxy struct {
type NewWorkConn struct {
RunId string `json:"run_id"`
PrivilegeKey string `json:"privilege_key"`
}
type ReqWorkConn struct {

View File

@ -435,6 +435,11 @@ func (svr *Service) RegisterWorkConn(workConn net.Conn, newMsg *msg.NewWorkConn)
xl.Warn("No client control found for run id [%s]", newMsg.RunId)
return
}
// Check auth.
if err := svr.authVerifier.VerifyNewWorkConn(newMsg); err != nil {
xl.Warn("Invalid authentication in NewWorkConn message on run id [%s]", newMsg.RunId)
return
}
ctl.RegisterWorkConn(workConn)
return
}