feat: authenticate NewWorkConn messages, similar to ping
This commit is contained in:
parent
5544e5f7d5
commit
4877c78312
@ -142,6 +142,10 @@ func (ctl *Control) HandleReqWorkConn(inMsg *msg.ReqWorkConn) {
|
|||||||
m := &msg.NewWorkConn{
|
m := &msg.NewWorkConn{
|
||||||
RunId: ctl.runId,
|
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 {
|
if err = msg.WriteMsg(workConn, m); err != nil {
|
||||||
xl.Warn("work connection write to server error: %v", err)
|
xl.Warn("work connection write to server error: %v", err)
|
||||||
workConn.Close()
|
workConn.Close()
|
||||||
|
@ -20,46 +20,63 @@ import (
|
|||||||
"github.com/fatedier/frp/models/msg"
|
"github.com/fatedier/frp/models/msg"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type BaseAuth struct {
|
||||||
|
authenticateHeartBeats bool
|
||||||
|
authenticateNewWorkConns bool
|
||||||
|
}
|
||||||
|
|
||||||
type Setter interface {
|
type Setter interface {
|
||||||
SetLogin(*msg.Login) error
|
SetLogin(*msg.Login) error
|
||||||
SetPing(*msg.Ping) error
|
SetPing(*msg.Ping) error
|
||||||
|
SetNewWorkConn(*msg.NewWorkConn) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthSetter(cfg config.ClientCommonConf) (authProvider Setter) {
|
func NewAuthSetter(cfg config.ClientCommonConf) (authProvider Setter) {
|
||||||
|
baseAuth := BaseAuth{
|
||||||
|
authenticateHeartBeats: cfg.AuthenticateHeartBeats,
|
||||||
|
authenticateNewWorkConns: cfg.AuthenticateNewWorkConns,
|
||||||
|
}
|
||||||
|
|
||||||
switch cfg.AuthenticationMethod {
|
switch cfg.AuthenticationMethod {
|
||||||
case consts.TokenAuthMethod:
|
case consts.TokenAuthMethod:
|
||||||
authProvider = NewTokenAuth(cfg.Token)
|
authProvider = NewTokenAuth(baseAuth, cfg.Token)
|
||||||
case consts.OidcAuthMethod:
|
case consts.OidcAuthMethod:
|
||||||
authProvider = NewOidcAuthSetter(
|
authProvider = NewOidcAuthSetter(
|
||||||
|
baseAuth,
|
||||||
cfg.OidcClientId,
|
cfg.OidcClientId,
|
||||||
cfg.OidcClientSecret,
|
cfg.OidcClientSecret,
|
||||||
cfg.OidcAudience,
|
cfg.OidcAudience,
|
||||||
cfg.OidcTokenEndpointUrl,
|
cfg.OidcTokenEndpointUrl,
|
||||||
cfg.AuthenticateHeartBeats,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return authProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
type Verifier interface {
|
type Verifier interface {
|
||||||
VerifyLogin(*msg.Login) error
|
VerifyLogin(*msg.Login) error
|
||||||
VerifyPing(*msg.Ping) error
|
VerifyPing(*msg.Ping) error
|
||||||
|
VerifyNewWorkConn(*msg.NewWorkConn) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthVerifier(cfg config.ServerCommonConf) (authVerifier Verifier) {
|
func NewAuthVerifier(cfg config.ServerCommonConf) (authVerifier Verifier) {
|
||||||
|
baseAuth := BaseAuth{
|
||||||
|
authenticateHeartBeats: cfg.AuthenticateHeartBeats,
|
||||||
|
authenticateNewWorkConns: cfg.AuthenticateNewWorkConns,
|
||||||
|
}
|
||||||
|
|
||||||
switch cfg.AuthenticationMethod {
|
switch cfg.AuthenticationMethod {
|
||||||
case consts.TokenAuthMethod:
|
case consts.TokenAuthMethod:
|
||||||
authVerifier = NewTokenAuth(cfg.Token)
|
authVerifier = NewTokenAuth(baseAuth, cfg.Token)
|
||||||
case consts.OidcAuthMethod:
|
case consts.OidcAuthMethod:
|
||||||
authVerifier = NewOidcAuthVerifier(
|
authVerifier = NewOidcAuthVerifier(
|
||||||
|
baseAuth,
|
||||||
cfg.OidcIssuer,
|
cfg.OidcIssuer,
|
||||||
cfg.OidcAudience,
|
cfg.OidcAudience,
|
||||||
cfg.OidcSkipExpiryCheck,
|
cfg.OidcSkipExpiryCheck,
|
||||||
cfg.OidcSkipIssuerCheck,
|
cfg.OidcSkipIssuerCheck,
|
||||||
cfg.AuthenticateHeartBeats,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return authVerifier
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type OidcAuthProvider struct {
|
type OidcAuthProvider struct {
|
||||||
|
BaseAuth
|
||||||
|
|
||||||
tokenGenerator *clientcredentials.Config
|
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{
|
tokenGenerator := &clientcredentials.Config{
|
||||||
ClientID: clientId,
|
ClientID: clientId,
|
||||||
ClientSecret: clientSecret,
|
ClientSecret: clientSecret,
|
||||||
@ -38,8 +39,8 @@ func NewOidcAuthSetter(clientId string, clientSecret string, audience string, to
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &OidcAuthProvider{
|
return &OidcAuthProvider{
|
||||||
|
BaseAuth: baseAuth,
|
||||||
tokenGenerator: tokenGenerator,
|
tokenGenerator: tokenGenerator,
|
||||||
authenticateHeartBeats: authenticateHeartBeats,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,13 +66,23 @@ func (auth *OidcAuthProvider) SetPing(pingMsg *msg.Ping) (err error) {
|
|||||||
return err
|
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 {
|
type OidcAuthConsumer struct {
|
||||||
|
BaseAuth
|
||||||
|
|
||||||
verifier *oidc.IDTokenVerifier
|
verifier *oidc.IDTokenVerifier
|
||||||
authenticateHeartBeats bool
|
|
||||||
subjectFromLogin string
|
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)
|
provider, err := oidc.NewProvider(context.Background(), issuer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -83,8 +94,8 @@ func NewOidcAuthVerifier(issuer string, audience string, skipExpiryCheck bool, s
|
|||||||
SkipIssuerCheck: skipIssuerCheck,
|
SkipIssuerCheck: skipIssuerCheck,
|
||||||
}
|
}
|
||||||
return &OidcAuthConsumer{
|
return &OidcAuthConsumer{
|
||||||
|
BaseAuth: baseAuth,
|
||||||
verifier: provider.Verifier(&verifierConf),
|
verifier: provider.Verifier(&verifierConf),
|
||||||
authenticateHeartBeats: authenticateHeartBeats,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,12 +108,8 @@ func (auth *OidcAuthConsumer) VerifyLogin(loginMsg *msg.Login) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *OidcAuthConsumer) VerifyPing(pingMsg *msg.Ping) (err error) {
|
func (auth *OidcAuthConsumer) verifyPostLoginToken(privilegeKey string) (err error) {
|
||||||
if !auth.authenticateHeartBeats {
|
token, err := auth.verifier.Verify(context.Background(), privilegeKey)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
token, err := auth.verifier.Verify(context.Background(), pingMsg.PrivilegeKey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid OIDC token in ping: %v", err)
|
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
|
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)
|
||||||
|
}
|
||||||
|
@ -22,37 +22,46 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type TokenAuthSetterVerifier struct {
|
type TokenAuthSetterVerifier struct {
|
||||||
Token string
|
BaseAuth
|
||||||
|
|
||||||
|
token string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTokenAuth(token string) *TokenAuthSetterVerifier {
|
func NewTokenAuth(baseAuth BaseAuth, token string) *TokenAuthSetterVerifier {
|
||||||
return &TokenAuthSetterVerifier{
|
return &TokenAuthSetterVerifier{
|
||||||
Token: token,
|
BaseAuth: baseAuth,
|
||||||
|
token: token,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *TokenAuthSetterVerifier) SetLogin(loginMsg *msg.Login) (err error) {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *TokenAuthSetterVerifier) SetPing(*msg.Ping) error {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type TokenAuthConsumer struct {
|
func (auth *TokenAuthSetterVerifier) SetNewWorkConn(*msg.NewWorkConn) error {
|
||||||
Token string
|
// NewWorkConn doesn't include authentication in token method
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *TokenAuthSetterVerifier) VerifyLogin(loginMsg *msg.Login) error {
|
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 fmt.Errorf("token in login doesn't match token from configuration")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *TokenAuthSetterVerifier) VerifyPing(*msg.Ping) error {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,9 @@ type ClientCommonConf struct {
|
|||||||
// AuthenticateHeartBeats specifies whether to include authentication token in
|
// AuthenticateHeartBeats specifies whether to include authentication token in
|
||||||
// heartbeats sent to frps. By default, this value is false.
|
// heartbeats sent to frps. By default, this value is false.
|
||||||
AuthenticateHeartBeats bool `json:"authenticate_heartbeats"`
|
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
|
// OidcClientId specifies the client ID to use to get a token in OIDC
|
||||||
// authentication if AuthenticationMethod == "oidc". By default, this value
|
// authentication if AuthenticationMethod == "oidc". By default, this value
|
||||||
@ -158,6 +161,7 @@ func GetDefaultClientConf() ClientCommonConf {
|
|||||||
Token: "",
|
Token: "",
|
||||||
AuthenticationMethod: "token",
|
AuthenticationMethod: "token",
|
||||||
AuthenticateHeartBeats: false,
|
AuthenticateHeartBeats: false,
|
||||||
|
AuthenticateNewWorkConns: false,
|
||||||
OidcClientId: "",
|
OidcClientId: "",
|
||||||
OidcClientSecret: "",
|
OidcClientSecret: "",
|
||||||
OidcAudience: "",
|
OidcAudience: "",
|
||||||
@ -248,6 +252,12 @@ func UnmarshalClientConfFromIni(content string) (cfg ClientCommonConf, err error
|
|||||||
cfg.AuthenticateHeartBeats = false
|
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 {
|
if tmpStr, ok = conf.Get("common", "oidc_client_id"); ok {
|
||||||
cfg.OidcClientId = tmpStr
|
cfg.OidcClientId = tmpStr
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,9 @@ type ServerCommonConf struct {
|
|||||||
// AuthenticateHeartBeats specifies whether to expect and verify authentication
|
// AuthenticateHeartBeats specifies whether to expect and verify authentication
|
||||||
// token in heartbeats sent from frpc. By default, this value is false.
|
// token in heartbeats sent from frpc. By default, this value is false.
|
||||||
AuthenticateHeartBeats bool `json:"authenticate_heartbeats"`
|
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
|
// 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
|
// will be used to load public keys to verify signature and will be compared
|
||||||
@ -199,6 +202,7 @@ func GetDefaultServerConf() ServerCommonConf {
|
|||||||
Token: "",
|
Token: "",
|
||||||
AuthenticationMethod: "token",
|
AuthenticationMethod: "token",
|
||||||
AuthenticateHeartBeats: false,
|
AuthenticateHeartBeats: false,
|
||||||
|
AuthenticateNewWorkConns: false,
|
||||||
OidcIssuer: "",
|
OidcIssuer: "",
|
||||||
OidcAudience: "",
|
OidcAudience: "",
|
||||||
OidcSkipExpiryCheck: false,
|
OidcSkipExpiryCheck: false,
|
||||||
@ -374,6 +378,12 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error
|
|||||||
cfg.AuthenticateHeartBeats = false
|
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 {
|
if tmpStr, ok = conf.Get("common", "oidc_issuer"); ok {
|
||||||
cfg.OidcIssuer = tmpStr
|
cfg.OidcIssuer = tmpStr
|
||||||
}
|
}
|
||||||
|
@ -121,6 +121,7 @@ type CloseProxy struct {
|
|||||||
|
|
||||||
type NewWorkConn struct {
|
type NewWorkConn struct {
|
||||||
RunId string `json:"run_id"`
|
RunId string `json:"run_id"`
|
||||||
|
PrivilegeKey string `json:"privilege_key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReqWorkConn struct {
|
type ReqWorkConn struct {
|
||||||
|
@ -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)
|
xl.Warn("No client control found for run id [%s]", newMsg.RunId)
|
||||||
return
|
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)
|
ctl.RegisterWorkConn(workConn)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user