Skip to content

Commit

Permalink
Remove Tencent Cloud support
Browse files Browse the repository at this point in the history
We haven't re-implemented this yet; there's no point keeping this code
around, especially since the user will just get panics if they try to
use it
  • Loading branch information
punmechanic committed Mar 18, 2024
1 parent da02549 commit e3570b9
Show file tree
Hide file tree
Showing 16 changed files with 62 additions and 566 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ KeyConjurer is made of three parts:
- [cli](./cli/) - The CLI interface.
- [frontend](./frontend/) - A static webpage which informs users on how to download and use KeyConjurer.

KeyConjurer is designed to work with Okta as an IdP, supports AWS and Tencent Cloud applications, and is inspired in part by [okta-aws-cli](https://github.com/okta/okta-aws-cli). The main difference from okta-aws-cli is that KeyConjurer does not require all users to have access to the Okta administration API - Instead, we use a Lambda function to access the protected resources required.
KeyConjurer is designed to work with Okta as an IdP, supports AWS applications, and is inspired in part by [okta-aws-cli](https://github.com/okta/okta-aws-cli). The main difference from okta-aws-cli is that KeyConjurer does not require all users to have access to the Okta administration API - Instead, we use a Lambda function to access the protected resources required.

We use KeyConjurer a lot at Riot, but we can't guarantee any external support for this project. It's use at your own risk. If you encounter a bug or have a feature request, please feel free to raise a pull request or an issue against this repository. You're also welcome to fork the code and modify it as you see fit.

Expand Down Expand Up @@ -48,7 +48,7 @@ In order to use KeyConjurer, an Okta administrator must configure their tenant a
* Authorization Types: Hybrid Flow, Authorization Code, Token Exchange
* Redirection URI: http://localhost:57468
* We recommend you enable Federated Mode on this native application so that users don't need to be explicitly assigned to it.
* All AWS and tencent applications must have their Allowed Web SSO Client set to the _Client ID_ of the native OIDC application that was created. This can be configured by going to the Sign On tab for each individual Okta application or managing the application configuration in an IAC provider, like Terraform.
* All AWS applications must have their Allowed Web SSO Client set to the _Client ID_ of the native OIDC application that was created. This can be configured by going to the Sign On tab for each individual Okta application or managing the application configuration in an IAC provider, like Terraform.

Okta configuration should be configured _out of band_ and is not provided in this repository.

Expand Down
22 changes: 5 additions & 17 deletions cli/awsconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main
import (
"os"
"path/filepath"
"strings"

"github.com/go-ini/ini"
homedir "github.com/mitchellh/go-homedir"
Expand Down Expand Up @@ -53,17 +52,11 @@ func ResolveAWSCredentialsPath(rootPath string) string {
return rootPath
}

func saveCredentialEntry(file *ini.File, entry CloudCliEntry, cloud string) error {
func saveCredentialEntry(file *ini.File, entry CloudCliEntry) error {
section := file.Section(entry.profileName)
if cloud == cloudAws {
section.Key("aws_access_key_id").SetValue(entry.keyID)
section.Key("aws_secret_access_key").SetValue(entry.key)
section.Key("aws_session_token").SetValue(entry.token)
} else if cloud == cloudTencent {
section.Key("tencent_access_key_id").SetValue(entry.keyID)
section.Key("tencent_secret_access_key").SetValue(entry.key)
section.Key("tencent_session_token").SetValue(entry.token)
}
section.Key("aws_access_key_id").SetValue(entry.keyID)
section.Key("aws_secret_access_key").SetValue(entry.key)
section.Key("aws_session_token").SetValue(entry.token)
return nil
}

Expand All @@ -74,12 +67,7 @@ func SaveCloudCredentialInCLI(cloudCliPath string, entry CloudCliEntry) error {
return err
}

cloud := cloudAws
if strings.Contains(strings.ToLower(path), cloudTencent) {
cloud = cloudTencent
}

if err := saveCredentialEntry(file, entry, cloud); err != nil {
if err := saveCredentialEntry(file, entry); err != nil {
return err
}

Expand Down
2 changes: 1 addition & 1 deletion cli/awsconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestAddAWSCliEntry(t *testing.T) {
token: "notatoken",
}

require.NoError(t, saveCredentialEntry(file, entry, cloudAws))
require.NoError(t, saveCredentialEntry(file, entry))

sec := file.Section("test-profile")
require.NotNil(t, sec, "section should have been added above")
Expand Down
5 changes: 2 additions & 3 deletions cli/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type Account struct {
}

func (a *Account) NormalizeName() string {
magicPrefixes := []string{"AWS - ", "Tencent - "}
magicPrefixes := []string{"AWS - "}
name := a.Name
for _, prefix := range magicPrefixes {
name = strings.TrimPrefix(name, prefix)
Expand Down Expand Up @@ -62,9 +62,8 @@ type accountSet struct {
accounts map[string]*Account
}

// need support Aws and Tencent
func generateDefaultAlias(name string) string {
magicPrefixes := []string{"AWS -", "Tencent -", "Tencent Cloud -"}
magicPrefixes := []string{"AWS -"}
for _, prefix := range magicPrefixes {
name = strings.TrimPrefix(name, prefix)
name = strings.TrimSpace(name)
Expand Down
2 changes: 0 additions & 2 deletions cli/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,6 @@ func TestAliasesPreservedAfterReplaceWith(t *testing.T) {

func TestGeneratesGoodAliases(t *testing.T) {
pairs := [][2]string{
{"Tencent Cloud - Foo Bar", "foo-bar"},
{"Tencent Cloud - Foobar", "foobar"},
{"AWS - Foo Bar", "foo-bar"},
{"AWS - Foobar", "foobar"},
}
Expand Down
52 changes: 0 additions & 52 deletions cli/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,6 @@ type CloudCredentials struct {
SecretAccessKey string `json:"SecretAccessKey"`
SessionToken string `json:"SessionToken"`
Expiration string `json:"Expiration"`

credentialsType string
}

func LoadTencentCredentialsFromEnvironment() CloudCredentials {
return CloudCredentials{
AccessKeyID: os.Getenv("TENCENTCLOUD_SECRET_ID"),
SecretAccessKey: os.Getenv("TENCENTCLOUD_SECRET_KEY"),
SessionToken: os.Getenv("TENCENTCLOUD_TOKEN"),
AccountID: os.Getenv("TENCENTKEY_ACCOUNT"),
Expiration: os.Getenv("TENCENTKEY_EXPIRATION"),
credentialsType: cloudTencent,
}
}

func LoadAWSCredentialsFromEnvironment() CloudCredentials {
Expand All @@ -68,7 +55,6 @@ func LoadAWSCredentialsFromEnvironment() CloudCredentials {
SessionToken: os.Getenv("AWS_SESSION_TOKEN"),
AccountID: os.Getenv("AWSKEY_ACCOUNT"),
Expiration: os.Getenv("AWSKEY_EXPIRATION"),
credentialsType: cloudAws,
}
}

Expand Down Expand Up @@ -99,16 +85,6 @@ $Env:TF_VAR_secret_key = $Env:AWS_SECRET_ACCESS_KEY
$Env:TF_VAR_token = $Env:AWS_SESSION_TOKEN
$Env:AWSKEY_EXPIRATION = "%v"
$Env:AWSKEY_ACCOUNT = "%v"
`
tencentShellTypePowershell = `$Env:TENCENTCLOUD_SECRET_ID = "%v"
$Env:TENCENTCLOUD_SECRET_KEY = "%v"
$Env:TENCENTCLOUD_TOKEN = "%v"
$Env:TENCENTCLOUD_SECURITY_TOKEN = "%v"
$Env:TF_VAR_access_key = $Env:TENCENTCLOUD_SECRET_ID
$Env:TF_VAR_secret_key = $Env:TENCENTCLOUD_SECRET_KEY
$Env:TF_VAR_token = $Env:TENCENTCLOUD_TOKEN
$Env:TENCENT_KEY_EXPIRATION = "%v"
$Env:TENCENT_KEY_ACCOUNT = "%v"
`
awsShellTypeBasic = `SET AWS_ACCESS_KEY_ID=%v
SET AWS_SECRET_ACCESS_KEY=%v
Expand All @@ -120,15 +96,6 @@ SET TF_VAR_token=%%AWS_SESSION_TOKEN%%
SET AWSKEY_EXPIRATION=%v
SET AWSKEY_ACCOUNT=%v
`
tencentShellTypeBasic = `SET TENCENTCLOUD_SECRET_ID=%v
SET TENCENTCLOUD_SECRET_KEY=%v
SET TENCENTCLOUD_TOKEN=%v
SET TENCENTCLOUD_SECURITY_TOKEN=%v
SET TF_VAR_access_key=%%TENCENTCLOUD_SECRET_ID%%
SET TF_VAR_secret_key=%%TENCENTCLOUD_SECRET_KEY%%
SET TF_VAR_token=%%TENCENTCLOUD_TOKEN%%
SET TENCENTKEY_EXPIRATION=%v
SET TENCENTKEY_ACCOUNT=%v`
awsShellTypeBash = `export AWS_ACCESS_KEY_ID=%v
export AWS_SECRET_ACCESS_KEY=%v
export AWS_SESSION_TOKEN=%v
Expand All @@ -138,16 +105,6 @@ export TF_VAR_secret_key=$AWS_SECRET_ACCESS_KEY
export TF_VAR_token=$AWS_SESSION_TOKEN
export AWSKEY_EXPIRATION=%v
export AWSKEY_ACCOUNT=%v
`
tencentShellTypeBash = `export TENCENTCLOUD_SECRET_ID=%v
export TENCENTCLOUD_SECRET_KEY=%v
export TENCENTCLOUD_TOKEN=%v
export TENCENT_SECURITY_TOKEN=%v
export TF_VAR_access_key=$TENCENTCLOUD_SECRET_ID
export TF_VAR_secret_key=$TENCENTCLOUD_SECRET_KEY
export TF_VAR_token=$TENCENTCLOUD_TOKEN
export TENCENTKEY_EXPIRATION=%v
export TENCENTKEY_ACCOUNT=%v
`
)

Expand All @@ -160,19 +117,10 @@ func (c CloudCredentials) WriteFormat(w io.Writer, format ShellType) (int, error
switch format {
case shellTypePowershell:
str = awsShellTypePowershell
if c.credentialsType == cloudTencent {
str = tencentShellTypePowershell
}
case shellTypeBasic:
str = awsShellTypeBasic
if c.credentialsType == cloudTencent {
str = tencentShellTypeBasic
}
case shellTypeBash:
str = awsShellTypeBash
if c.credentialsType == cloudTencent {
str = tencentShellTypeBash
}
}

return fmt.Fprintf(w, str, c.AccessKeyID, c.SecretAccessKey, c.SessionToken, c.SessionToken, c.Expiration, c.AccountID)
Expand Down
105 changes: 44 additions & 61 deletions cli/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,25 @@ import (
)

var (
FlagRegion = "region"
FlagRoleName = "role"
FlagTimeRemaining = "time-remaining"
FlagTimeToLive = "ttl"
FlagBypassCache = "bypass-cache"
FlagLogin = "login"
FlagRegion = "region"
FlagRoleName = "role"
FlagTimeRemaining = "time-remaining"
FlagTimeToLive = "ttl"
FlagBypassCache = "bypass-cache"
FlagLogin = "login"
FlagShellType = "shell"
FlagOutputType = "out"
FlagRoleSessionName = "role-session-name"
FlagAWSCLIPath = "awscli"
)

var (
// outputTypeEnvironmentVariable indicates that keyconjurer will dump the credentials to stdout in Bash environment variable format
outputTypeEnvironmentVariable = "env"
// outputTypeAWSCredentialsFile indicates that keyconjurer will dump the credentials into the ~/.aws/credentials file.
outputTypeAWSCredentialsFile = "awscli"
// outputTypeTencentCredentialsFile indicates that keyconjurer will dump the credentials into the ~/.tencent/credentials file.
outputTypeTencentCredentialsFile = "tencentcli"
permittedOutputTypes = []string{outputTypeAWSCredentialsFile, outputTypeEnvironmentVariable, outputTypeTencentCredentialsFile}
permittedShellTypes = []string{shellTypePowershell, shellTypeBash, shellTypeBasic, shellTypeInfer}
permittedOutputTypes = []string{outputTypeAWSCredentialsFile, outputTypeEnvironmentVariable}
permittedShellTypes = []string{shellTypePowershell, shellTypeBash, shellTypeBasic, shellTypeInfer}
)

func init() {
Expand All @@ -37,13 +39,11 @@ func init() {
getCmd.Flags().UintP(FlagTimeRemaining, "t", DefaultTimeRemaining, "Request new keys if there are no keys in the environment or the current keys expire within <time-remaining> minutes. Defaults to 60.")
getCmd.Flags().StringP(FlagRoleName, "r", "", "The name of the role to assume.")
getCmd.Flags().String(FlagRoleSessionName, "KeyConjurer-AssumeRole", "the name of the role session name that will show up in CloudTrail logs")
getCmd.Flags().StringP(FlagOutputType, "o", outputTypeEnvironmentVariable, "Format to save new credentials in. Supported outputs: env, awscli,tencentcli")
getCmd.Flags().StringP(FlagOutputType, "o", outputTypeEnvironmentVariable, "Format to save new credentials in. Supported outputs: env, awscli")
getCmd.Flags().String(FlagShellType, shellTypeInfer, "If output type is env, determines which format to output credentials in - by default, the format is inferred based on the execution environment. WSL users may wish to overwrite this to `bash`")
getCmd.Flags().String(FlagAWSCLIPath, "~/.aws/", "Path for directory used by the aws-cli tool. Default is \"~/.aws\".")
getCmd.Flags().String(FlagTencentCLIPath, "~/.tencent/", "Path for directory used by the tencent-cli tool. Default is \"~/.tencent\".")
getCmd.Flags().String(FlagCloudType, "aws", "Choose a cloud vendor. Default is aws. Can choose aws or tencent")
getCmd.Flags().Bool(FlagBypassCache, false, "Do not check the cache for accounts and send the application ID as-is to Okta. This is useful if you have an ID you know is an Okta application ID and it is not stored in your local account cache.")
getCmd.Flags().Bool(FlagLogin, false, "Login to Okta before running the command")
getCmd.Flags().String(FlagAWSCLIPath, "~/.aws/", "Path for directory used by the aws CLI")
}

func isMemberOfSlice(slice []string, val string) bool {
Expand Down Expand Up @@ -95,9 +95,7 @@ A role must be specified when using this command through the --role flag. You ma
outputType, _ := cmd.Flags().GetString(FlagOutputType)
shellType, _ := cmd.Flags().GetString(FlagShellType)
roleName, _ := cmd.Flags().GetString(FlagRoleName)
cloudType, _ := cmd.Flags().GetString(FlagCloudType)
awsCliPath, _ := cmd.Flags().GetString(FlagAWSCLIPath)
tencentCliPath, _ := cmd.Flags().GetString(FlagTencentCLIPath)

if !isMemberOfSlice(permittedOutputTypes, outputType) {
return ValueError{Value: outputType, ValidValues: permittedOutputTypes}
Expand Down Expand Up @@ -135,15 +133,9 @@ A role must be specified when using this command through the --role flag. You ma
timeRemaining = config.TimeRemaining
}

var credentials CloudCredentials
if cloudType == cloudAws {
credentials = LoadAWSCredentialsFromEnvironment()
} else if cloudType == cloudTencent {
credentials = LoadTencentCredentialsFromEnvironment()
}

credentials := LoadAWSCredentialsFromEnvironment()
if credentials.ValidUntil(account, time.Duration(timeRemaining)*time.Minute) {
return echoCredentials(accountID, accountID, credentials, outputType, shellType, awsCliPath, tencentCliPath)
return echoCredentials(accountID, accountID, credentials, outputType, shellType, awsCliPath)
}

samlResponse, assertionStr, err := DiscoverConfigAndExchangeTokenForAssertion(cmd.Context(), NewHTTPClient(), config.Tokens, oidcDomain, clientID, account.ID)
Expand All @@ -160,61 +152,52 @@ A role must be specified when using this command through the --role flag. You ma
ttl = config.TTL
}

if cloudType == cloudAws {
region, _ := cmd.Flags().GetString(FlagRegion)
session, _ := session.NewSession(&aws.Config{Region: aws.String(region)})
stsClient := sts.New(session)
timeoutInSeconds := int64(3600 * ttl)
resp, err := stsClient.AssumeRoleWithSAMLWithContext(ctx, &sts.AssumeRoleWithSAMLInput{
DurationSeconds: &timeoutInSeconds,
PrincipalArn: &pair.ProviderARN,
RoleArn: &pair.RoleARN,
SAMLAssertion: &assertionStr,
})

if err, ok := tryParseTimeToLiveError(err); ok {
return err
}
region, _ := cmd.Flags().GetString(FlagRegion)
session, _ := session.NewSession(&aws.Config{Region: aws.String(region)})
stsClient := sts.New(session)
timeoutInSeconds := int64(3600 * ttl)
resp, err := stsClient.AssumeRoleWithSAMLWithContext(ctx, &sts.AssumeRoleWithSAMLInput{
DurationSeconds: &timeoutInSeconds,
PrincipalArn: &pair.ProviderARN,
RoleArn: &pair.RoleARN,
SAMLAssertion: &assertionStr,
})

if err != nil {
return AWSError{
InnerError: err,
Message: "failed to exchange credentials",
}
}
if err, ok := tryParseTimeToLiveError(err); ok {
return err
}

credentials = CloudCredentials{
AccessKeyID: *resp.Credentials.AccessKeyId,
Expiration: resp.Credentials.Expiration.Format(time.RFC3339),
SecretAccessKey: *resp.Credentials.SecretAccessKey,
SessionToken: *resp.Credentials.SessionToken,
credentialsType: cloudType,
if err != nil {
return AWSError{
InnerError: err,
Message: "failed to exchange credentials",
}
} else {
panic("not yet implemented")
}

credentials = CloudCredentials{
AccessKeyID: *resp.Credentials.AccessKeyId,
Expiration: resp.Credentials.Expiration.Format(time.RFC3339),
SecretAccessKey: *resp.Credentials.SecretAccessKey,
SessionToken: *resp.Credentials.SessionToken,
}

if account != nil {
account.MostRecentRole = roleName
}
config.LastUsedAccount = &accountID

return echoCredentials(accountID, accountID, credentials, outputType, shellType, awsCliPath, tencentCliPath)
return echoCredentials(accountID, accountID, credentials, outputType, shellType, awsCliPath)
}}

func echoCredentials(id, name string, credentials CloudCredentials, outputType, shellType, awsCliPath, tencentCliPath string) error {
func echoCredentials(id, name string, credentials CloudCredentials, outputType, shellType, awsCliPath string) error {
switch outputType {
case outputTypeEnvironmentVariable:
credentials.WriteFormat(os.Stdout, shellType)
return nil
case outputTypeAWSCredentialsFile, outputTypeTencentCredentialsFile:
case outputTypeAWSCredentialsFile:
acc := Account{ID: id, Name: name}
newCliEntry := NewCloudCliEntry(credentials, &acc)
cliPath := awsCliPath
if outputType == outputTypeTencentCredentialsFile {
cliPath = tencentCliPath
}
return SaveCloudCredentialInCLI(cliPath, newCliEntry)
return SaveCloudCredentialInCLI(awsCliPath, newCliEntry)
default:
return fmt.Errorf("%s is an invalid output type", outputType)
}
Expand Down
3 changes: 0 additions & 3 deletions cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ var (
FlagConfigPath = "config"
FlagQuiet = "quiet"
FlagTimeout = "timeout"
cloudAws = "aws"
cloudTencent = "tencent"
)

func init() {
Expand All @@ -33,7 +31,6 @@ func init() {
rootCmd.AddCommand(getCmd)
rootCmd.AddCommand(setCmd)
rootCmd.AddCommand(upgradeCmd)
rootCmd.AddCommand(&switchCmd)
rootCmd.AddCommand(&aliasCmd)
rootCmd.AddCommand(&unaliasCmd)
rootCmd.AddCommand(&rolesCmd)
Expand Down
Loading

0 comments on commit e3570b9

Please sign in to comment.