feat(client): Add Google's Identity-Aware-Proxy in client config (#600)
* client: config: add Google Identity-Aware-Proxy support * Add correct sum and mod * Update README.md * Update README.md * client: config: add Google Identity-Aware-Proxy support * Add correct sum and mod * Update README.md * Update README.md * Change IAP acronym to Identity-Aware-Proxy * Change IAP acronym to Identity-Aware-Proxy in README.md * Fix conflict in go.mod * merge readme from twin/master * Fix typo error in readme.md * Update client/config.go --------- Co-authored-by: TwiN <twin@linux.com>
This commit is contained in:
@ -13,6 +13,7 @@ import (
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/clientcredentials"
|
||||
"google.golang.org/api/idtoken"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -23,6 +24,7 @@ var (
|
||||
ErrInvalidDNSResolver = errors.New("invalid DNS resolver specified. Required format is {proto}://{ip}:{port}")
|
||||
ErrInvalidDNSResolverPort = errors.New("invalid DNS resolver port")
|
||||
ErrInvalidClientOAuth2Config = errors.New("invalid oauth2 configuration: must define all fields for client credentials flow (token-url, client-id, client-secret, scopes)")
|
||||
ErrInvalidClientIAPConfig = errors.New("invalid Identity-Aware-Proxy configuration: must define all fields for Google Identity-Aware-Proxy programmatic authentication (audience)")
|
||||
|
||||
defaultConfig = Config{
|
||||
Insecure: false,
|
||||
@ -58,6 +60,9 @@ type Config struct {
|
||||
// See configureOAuth2 for more details.
|
||||
OAuth2Config *OAuth2Config `yaml:"oauth2,omitempty"`
|
||||
|
||||
// IAPConfig is the Google Cloud Identity-Aware-Proxy configuration used for the client. (e.g. audience)
|
||||
IAPConfig *IAPConfig `yaml:"identity-aware-proxy,omitempty"`
|
||||
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
@ -76,6 +81,11 @@ type OAuth2Config struct {
|
||||
Scopes []string `yaml:"scopes"` // e.g. ["openid"]
|
||||
}
|
||||
|
||||
// IAPConfig is the configuration for the Google Cloud Identity-Aware-Proxy
|
||||
type IAPConfig struct {
|
||||
Audience string `yaml:"audience"` // e.g. "toto.apps.googleusercontent.com"
|
||||
}
|
||||
|
||||
// ValidateAndSetDefaults validates the client configuration and sets the default values if necessary
|
||||
func (c *Config) ValidateAndSetDefaults() error {
|
||||
if c.Timeout < time.Millisecond {
|
||||
@ -90,6 +100,9 @@ func (c *Config) ValidateAndSetDefaults() error {
|
||||
if c.HasOAuth2Config() && !c.OAuth2Config.isValid() {
|
||||
return ErrInvalidClientOAuth2Config
|
||||
}
|
||||
if c.HasIAPConfig() && !c.IAPConfig.isValid() {
|
||||
return ErrInvalidClientIAPConfig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -130,6 +143,16 @@ func (c *Config) HasOAuth2Config() bool {
|
||||
return c.OAuth2Config != nil
|
||||
}
|
||||
|
||||
// HasIAPConfig returns true if the client has IAP configuration parameters
|
||||
func (c *Config) HasIAPConfig() bool {
|
||||
return c.IAPConfig != nil
|
||||
}
|
||||
|
||||
// isValid() returns true if the IAP configuration is valid
|
||||
func (c *IAPConfig) isValid() bool {
|
||||
return len(c.Audience) > 0
|
||||
}
|
||||
|
||||
// isValid() returns true if the OAuth2 configuration is valid
|
||||
func (c *OAuth2Config) isValid() bool {
|
||||
return len(c.TokenURL) > 0 && len(c.ClientID) > 0 && len(c.ClientSecret) > 0 && len(c.Scopes) > 0
|
||||
@ -178,13 +201,56 @@ func (c *Config) getHTTPClient() *http.Client {
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.HasOAuth2Config() {
|
||||
if c.HasOAuth2Config() && c.HasIAPConfig() {
|
||||
log.Println("[client][getHTTPClient] Error: Both Identity-Aware-Proxy and Oauth2 configuration are present.")
|
||||
} else if c.HasOAuth2Config() {
|
||||
c.httpClient = configureOAuth2(c.httpClient, *c.OAuth2Config)
|
||||
} else if c.HasIAPConfig() {
|
||||
c.httpClient = configureIAP(c.httpClient, *c.IAPConfig)
|
||||
}
|
||||
}
|
||||
return c.httpClient
|
||||
}
|
||||
|
||||
// validateIAPToken returns a boolean that will define if the google identity-aware-proxy token can be fetch
|
||||
// and if is it valid.
|
||||
func validateIAPToken(ctx context.Context, c IAPConfig) bool {
|
||||
ts, err := idtoken.NewTokenSource(ctx, c.Audience)
|
||||
if err != nil {
|
||||
log.Println("[client][ValidateIAPToken] Claiming Identity token failed. error:", err.Error())
|
||||
return false
|
||||
}
|
||||
tok, err := ts.Token()
|
||||
if err != nil {
|
||||
log.Println("[client][ValidateIAPToken] Get Identity-Aware-Proxy token failed. error:", err.Error())
|
||||
return false
|
||||
}
|
||||
payload, err := idtoken.Validate(ctx, tok.AccessToken, c.Audience)
|
||||
_ = payload
|
||||
if err != nil {
|
||||
log.Println("[client][ValidateIAPToken] Token Validation failed. error:", err.Error())
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// configureIAP returns an HTTP client that will obtain and refresh Identity-Aware-Proxy tokens as necessary.
|
||||
// The returned Client and its Transport should not be modified.
|
||||
func configureIAP(httpClient *http.Client, c IAPConfig) *http.Client {
|
||||
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, httpClient)
|
||||
if validateIAPToken(ctx, c) {
|
||||
ts, err := idtoken.NewTokenSource(ctx, c.Audience)
|
||||
if err != nil {
|
||||
log.Println("[client][ConfigureIAP] Claiming Token Source failed. error:", err.Error())
|
||||
return httpClient
|
||||
}
|
||||
client := oauth2.NewClient(ctx, ts)
|
||||
client.Timeout = httpClient.Timeout
|
||||
return client
|
||||
}
|
||||
return httpClient
|
||||
}
|
||||
|
||||
// configureOAuth2 returns an HTTP client that will obtain and refresh tokens as necessary.
|
||||
// The returned Client and its Transport should not be modified.
|
||||
func configureOAuth2(httpClient *http.Client, c OAuth2Config) *http.Client {
|
||||
@ -195,5 +261,7 @@ func configureOAuth2(httpClient *http.Client, c OAuth2Config) *http.Client {
|
||||
TokenURL: c.TokenURL,
|
||||
}
|
||||
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, httpClient)
|
||||
return oauth2cfg.Client(ctx)
|
||||
client := oauth2cfg.Client(ctx)
|
||||
client.Timeout = httpClient.Timeout
|
||||
return client
|
||||
}
|
||||
|
Reference in New Issue
Block a user