Skip to content

feat(provider): [118694466] add SAML, OIDC for STS client #2742

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/2742.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
provider: add SAML, OIDC for STS client
```
9 changes: 7 additions & 2 deletions tencentcloud/connectivity/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,17 +563,22 @@ func (me *TencentCloudClient) UseCamClient() *cam.Client {
}

// UseStsClient returns sts client for service
func (me *TencentCloudClient) UseStsClient() *sts.Client {
func (me *TencentCloudClient) UseStsClient(stsExtInfo ...StsExtInfo) *sts.Client {
/*
me.Credential will changed, don't cache it
if me.stsConn != nil {
return me.stsConn
}
*/

var logRoundTripper LogRoundTripper
if len(stsExtInfo) != 0 {
logRoundTripper.Authorization = stsExtInfo[0].Authorization
}

cpf := me.NewClientProfile(300)
me.stsConn, _ = sts.NewClient(me.Credential, me.Region, cpf)
me.stsConn.WithHttpTransport(&LogRoundTripper{})
me.stsConn.WithHttpTransport(&logRoundTripper)

return me.stsConn
}
Expand Down
11 changes: 10 additions & 1 deletion tencentcloud/connectivity/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,18 @@ func SetReqClient(name string) {
}

type LogRoundTripper struct {
InstanceId string
InstanceId string
Authorization string
}

type IacExtInfo struct {
InstanceId string
}

type StsExtInfo struct {
Authorization string
}

func (me *LogRoundTripper) RoundTrip(request *http.Request) (response *http.Response, errRet error) {

var inBytes, outBytes []byte
Expand Down Expand Up @@ -60,6 +65,10 @@ func (me *LogRoundTripper) RoundTrip(request *http.Request) (response *http.Resp
reqClientFormat = fmt.Sprintf("%s,id=%s", ReqClient, me.InstanceId)
}

if me.Authorization != "" {
request.Header.Set("Authorization", me.Authorization)
}

request.Header.Set("X-TC-RequestClient", reqClientFormat)
inBytes = []byte(fmt.Sprintf("%s, request: ", request.Header[headName]))
requestBody, errRet := ioutil.ReadAll(bodyReader)
Expand Down
221 changes: 212 additions & 9 deletions tencentcloud/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,14 @@ const (
PROVIDER_DOMAIN = "TENCENTCLOUD_DOMAIN"
//internal version: replace envYunti begin, please do not modify this annotation and refrain from inserting any code between the beginning and end lines of the annotation.
//internal version: replace envYunti end, please do not modify this annotation and refrain from inserting any code between the beginning and end lines of the annotation.
PROVIDER_ASSUME_ROLE_ARN = "TENCENTCLOUD_ASSUME_ROLE_ARN"
PROVIDER_ASSUME_ROLE_SESSION_NAME = "TENCENTCLOUD_ASSUME_ROLE_SESSION_NAME"
PROVIDER_ASSUME_ROLE_SESSION_DURATION = "TENCENTCLOUD_ASSUME_ROLE_SESSION_DURATION"
PROVIDER_SHARED_CREDENTIALS_DIR = "TENCENTCLOUD_SHARED_CREDENTIALS_DIR"
PROVIDER_PROFILE = "TENCENTCLOUD_PROFILE"
PROVIDER_ASSUME_ROLE_ARN = "TENCENTCLOUD_ASSUME_ROLE_ARN"
PROVIDER_ASSUME_ROLE_SESSION_NAME = "TENCENTCLOUD_ASSUME_ROLE_SESSION_NAME"
PROVIDER_ASSUME_ROLE_SESSION_DURATION = "TENCENTCLOUD_ASSUME_ROLE_SESSION_DURATION"
PROVIDER_ASSUME_ROLE_SAML_ASSERTION = "TENCENTCLOUD_ASSUME_ROLE_SAML_ASSERTION"
PROVIDER_ASSUME_ROLE_PRINCIPAL_ARN = "TENCENTCLOUD_ASSUME_ROLE_PRINCIPAL_ARN"
PROVIDER_ASSUME_ROLE_WEB_IDENTITY_TOKEN = "TENCENTCLOUD_ASSUME_ROLE_WEB_IDENTITY_TOKEN"
PROVIDER_SHARED_CREDENTIALS_DIR = "TENCENTCLOUD_SHARED_CREDENTIALS_DIR"
PROVIDER_PROFILE = "TENCENTCLOUD_PROFILE"
)

const (
Expand Down Expand Up @@ -233,6 +236,94 @@ func Provider() *schema.Provider {
},
},
},
"assume_role_with_saml": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
ConflictsWith: []string{"assume_role_with_web_identity"},
Description: "The `assume_role_with_saml` block. If provided, terraform will attempt to assume this role using the supplied credentials.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"saml_assertion": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc(PROVIDER_ASSUME_ROLE_SAML_ASSERTION, nil),
Description: "SAML assertion information encoded in base64. It can be sourced from the `PROVIDER_ASSUME_ROLE_SAML_ASSERTION`.",
},
"principal_arn": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc(PROVIDER_ASSUME_ROLE_PRINCIPAL_ARN, nil),
Description: "Player Access Description Name. It can be sourced from the `PROVIDER_ASSUME_ROLE_PRINCIPAL_ARN`.",
},
"role_arn": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc(PROVIDER_ASSUME_ROLE_ARN, nil),
Description: "The ARN of the role to assume. It can be sourced from the `TENCENTCLOUD_ASSUME_ROLE_ARN`.",
},
"session_name": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc(PROVIDER_ASSUME_ROLE_SESSION_NAME, nil),
Description: "The session name to use when making the AssumeRole call. It can be sourced from the `TENCENTCLOUD_ASSUME_ROLE_SESSION_NAME`.",
},
"session_duration": {
Type: schema.TypeInt,
Required: true,
DefaultFunc: func() (interface{}, error) {
if v := os.Getenv(PROVIDER_ASSUME_ROLE_SESSION_DURATION); v != "" {
return strconv.Atoi(v)
}
return 7200, nil
},
ValidateFunc: tccommon.ValidateIntegerInRange(0, 43200),
Description: "The duration of the session when making the AssumeRoleWithSAML call. Its value ranges from 0 to 43200(seconds), and default is 7200 seconds. It can be sourced from the `TENCENTCLOUD_ASSUME_ROLE_SESSION_DURATION`.",
},
},
},
},
"assume_role_with_web_identity": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
ConflictsWith: []string{"assume_role_with_saml"},
Description: "The `assume_role_with_web_identity` block. If provided, terraform will attempt to assume this role using the supplied credentials.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"web_identity_token": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc(PROVIDER_ASSUME_ROLE_WEB_IDENTITY_TOKEN, nil),
Description: "OIDC token issued by IdP. It can be sourced from the `PROVIDER_ASSUME_ROLE_WEB_IDENTITY_TOKEN`.",
},
"role_arn": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc(PROVIDER_ASSUME_ROLE_ARN, nil),
Description: "The ARN of the role to assume. It can be sourced from the `TENCENTCLOUD_ASSUME_ROLE_ARN`.",
},
"session_name": {
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc(PROVIDER_ASSUME_ROLE_SESSION_NAME, nil),
Description: "The session name to use when making the AssumeRole call. It can be sourced from the `TENCENTCLOUD_ASSUME_ROLE_SESSION_NAME`.",
},
"session_duration": {
Type: schema.TypeInt,
Required: true,
DefaultFunc: func() (interface{}, error) {
if v := os.Getenv(PROVIDER_ASSUME_ROLE_SESSION_DURATION); v != "" {
return strconv.Atoi(v)
}
return 7200, nil
},
ValidateFunc: tccommon.ValidateIntegerInRange(0, 43200),
Description: "The duration of the session when making the AssumeRoleWithWebIdentity call. Its value ranges from 0 to 43200(seconds), and default is 7200 seconds. It can be sourced from the `TENCENTCLOUD_ASSUME_ROLE_SESSION_DURATION`.",
},
},
},
},
"shared_credentials_dir": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -2012,6 +2103,7 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
domain string
)

needSecret := true
if v, ok := d.GetOk("secret_id"); ok {
secretId = v.(string)
} else {
Expand Down Expand Up @@ -2079,7 +2171,6 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
if assumeRoleArn != "" && assumeRoleSessionName != "" {
assumeRoleSessionDuration = 7200
assumeRolePolicy = ""

_ = genClientWithSTS(&tcClient, assumeRoleArn, assumeRoleSessionName, assumeRoleSessionDuration, assumeRolePolicy)
}

Expand All @@ -2099,7 +2190,28 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
assumeRoleSessionDuration = 7200
}

_ = genClientWithSTS(&tcClient, envRoleArn, envSessionName, assumeRoleSessionDuration, "")
// get assume role with saml from env
envSamlAssertion := os.Getenv(PROVIDER_ASSUME_ROLE_SAML_ASSERTION)
envPrincipalArn := os.Getenv(PROVIDER_ASSUME_ROLE_PRINCIPAL_ARN)
// get assume role with web identity from env
envWebIdentityToken := os.Getenv(PROVIDER_ASSUME_ROLE_WEB_IDENTITY_TOKEN)

if envSamlAssertion == "" && envPrincipalArn == "" && envWebIdentityToken == "" {
// use assume role
_ = genClientWithSTS(&tcClient, envRoleArn, envSessionName, assumeRoleSessionDuration, "")
} else if envSamlAssertion != "" && envPrincipalArn != "" && envWebIdentityToken != "" {
return nil, fmt.Errorf("can not set `TENCENTCLOUD_ASSUME_ROLE_SAML_ASSERTION`, `TENCENTCLOUD_ASSUME_ROLE_PRINCIPAL_ARN`, `TENCENTCLOUD_ASSUME_ROLE_WEB_IDENTITY_TOKEN` at the same time.\n")
} else if envSamlAssertion != "" && envPrincipalArn != "" {
// use assume role with saml
_ = genClientWithSamlSTS(&tcClient, envRoleArn, envSessionName, assumeRoleSessionDuration, envSamlAssertion, envPrincipalArn)
needSecret = false
} else if envWebIdentityToken != "" {
// use assume role with oidc
_ = genClientWithOidcSTS(&tcClient, envRoleArn, envSessionName, assumeRoleSessionDuration, envWebIdentityToken)
needSecret = false
} else {
return nil, fmt.Errorf("get `assume_role` from env error.\n")
}
}

// get assume role from tf
Expand All @@ -2116,8 +2228,45 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
}
}

if secretId == "" || secretKey == "" {
return nil, fmt.Errorf("Please set your `secret_id` and `secret_key`.")
var (
assumeRoleSamlAssertion string
assumeRolePrincipalArn string
assumeRoleWebIdentityToken string
)

// get assume role with saml from tf
if v, ok := d.GetOk("assume_role_with_saml"); ok {
assumeRoleWithSamlList := v.([]interface{})
if len(assumeRoleWithSamlList) == 1 {
assumeRoleWithSaml := assumeRoleWithSamlList[0].(map[string]interface{})
assumeRoleSamlAssertion = assumeRoleWithSaml["saml_assertion"].(string)
assumeRolePrincipalArn = assumeRoleWithSaml["principal_arn"].(string)
assumeRoleArn = assumeRoleWithSaml["role_arn"].(string)
assumeRoleSessionName = assumeRoleWithSaml["session_name"].(string)
assumeRoleSessionDuration = assumeRoleWithSaml["session_duration"].(int)

_ = genClientWithSamlSTS(&tcClient, assumeRoleArn, assumeRoleSessionName, assumeRoleSessionDuration, assumeRoleSamlAssertion, assumeRolePrincipalArn)
needSecret = false
}
}

// get assume role with web identity from tf
if v, ok := d.GetOk("assume_role_with_web_identity"); ok {
assumeRoleWithWebIdentityList := v.([]interface{})
if len(assumeRoleWithWebIdentityList) == 1 {
assumeRoleWithWebIdentity := assumeRoleWithWebIdentityList[0].(map[string]interface{})
assumeRoleWebIdentityToken = assumeRoleWithWebIdentity["web_identity_token"].(string)
assumeRoleArn = assumeRoleWithWebIdentity["role_arn"].(string)
assumeRoleSessionName = assumeRoleWithWebIdentity["session_name"].(string)
assumeRoleSessionDuration = assumeRoleWithWebIdentity["session_duration"].(int)

_ = genClientWithOidcSTS(&tcClient, assumeRoleArn, assumeRoleSessionName, assumeRoleSessionDuration, assumeRoleWebIdentityToken)
needSecret = false
}
}

if needSecret && (secretId == "" || secretKey == "") {
return nil, fmt.Errorf("Please set your `secret_id` and `secret_key`.\n")
}

return &tcClient, nil
Expand Down Expand Up @@ -2149,6 +2298,60 @@ func genClientWithSTS(tcClient *TencentCloudClient, assumeRoleArn, assumeRoleSes
return nil
}

func genClientWithSamlSTS(tcClient *TencentCloudClient, assumeRoleArn, assumeRoleSessionName string, assumeRoleSessionDuration int, assumeRoleSamlAssertion, assumeRolePrincipalArn string) error {
// applying STS credentials
request := sdksts.NewAssumeRoleWithSAMLRequest()
request.RoleArn = helper.String(assumeRoleArn)
request.RoleSessionName = helper.String(assumeRoleSessionName)
request.DurationSeconds = helper.IntUint64(assumeRoleSessionDuration)
request.SAMLAssertion = helper.String(assumeRoleSamlAssertion)
request.PrincipalArn = helper.String(assumeRolePrincipalArn)

ratelimit.Check(request.GetAction())
var stsExtInfo connectivity.StsExtInfo
stsExtInfo.Authorization = "SKIP"
response, err := tcClient.apiV3Conn.UseStsClient(stsExtInfo).AssumeRoleWithSAML(request)
if err != nil {
return err
}

// using STS credentials
tcClient.apiV3Conn.Credential = sdkcommon.NewTokenCredential(
*response.Response.Credentials.TmpSecretId,
*response.Response.Credentials.TmpSecretKey,
*response.Response.Credentials.Token,
)

return nil
}

func genClientWithOidcSTS(tcClient *TencentCloudClient, assumeRoleArn, assumeRoleSessionName string, assumeRoleSessionDuration int, assumeRolePolicy string) error {
// applying STS credentials
request := sdksts.NewAssumeRoleWithWebIdentityRequest()
request.ProviderId = helper.String("OIDC")
request.RoleArn = helper.String(assumeRoleArn)
request.RoleSessionName = helper.String(assumeRoleSessionName)
request.DurationSeconds = helper.IntInt64(assumeRoleSessionDuration)
request.WebIdentityToken = helper.String(assumeRolePolicy)

ratelimit.Check(request.GetAction())
var stsExtInfo connectivity.StsExtInfo
stsExtInfo.Authorization = "SKIP"
response, err := tcClient.apiV3Conn.UseStsClient(stsExtInfo).AssumeRoleWithWebIdentity(request)
if err != nil {
return err
}

// using STS credentials
tcClient.apiV3Conn.Credential = sdkcommon.NewTokenCredential(
*response.Response.Credentials.TmpSecretId,
*response.Response.Credentials.TmpSecretKey,
*response.Response.Credentials.Token,
)

return nil
}

var providerConfig map[string]interface{}

func getConfigFromProfile(d *schema.ResourceData, ProfileKey string) (interface{}, error) {
Expand Down
Loading
Loading