Skip to content

Commit

Permalink
Support chainable ram role arn
Browse files Browse the repository at this point in the history
  • Loading branch information
JacksonTian committed Aug 16, 2021
1 parent 5fc4c4a commit c962ad3
Show file tree
Hide file tree
Showing 13 changed files with 165 additions and 39 deletions.
35 changes: 30 additions & 5 deletions README-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ Saving profile[akProfile] ...Done.

| 验证方式 | 说明 |
|------------|-------------------------------------|
| AK | 使用AccessKey ID/Secret访问 |
| StsToken | 使用STS Token访问 |
| RamRoleArn | 使用RAM子账号的AssumeRole方式访问 |
| EcsRamRole | 在ECS实例上通过EcsRamRole实现免密验证 |
| AK | 使用AccessKey ID/Secret访问 |
| StsToken | 使用STS Token访问 |
| RamRoleArn | 使用RAM子账号的AssumeRole方式访问 |
| EcsRamRole | 在ECS实例上通过EcsRamRole实现免密验证 |

### 使用外部程序获取凭证

Expand Down Expand Up @@ -144,7 +144,7 @@ Saving profile[akProfile] ...Done.
}
```

#### Example:
#### 示例:
```shell
$ aliyun configure --mode External --profile externalTest
Configuring profile 'externalTest' in 'External' authenticate mode...
Expand All @@ -155,6 +155,31 @@ Default Language [zh|en] en:
Saving profile[externalTest] ...Done.
```

### 使用链式 RamRoleArn

你可以使用 `--mode ChainableRamRoleArn` 来组合源配置和 RamRoleARN 的角色扮演流程。下面的例子从源配置中获取中间凭证,再基于中间凭证完成角色扮演,获取最终的凭证。

```json
{
"profiles": [
{
"name": "chain",
"mode": "ChainableRamRoleArn",
"ram_role_arn": "acs:ram::<Account ID>:role/<Role Name>",
"ram_session_name": "session",
"source_profile": "cli-test"
},
{
"name": "cli-test",
"mode": "AK",
"access_key_id": "<Access Key ID>",
"access_key_secret": "<Access Key Secret>"
}
]
}
```


### 启用zsh/bash自动补全

- 使用`aliyun auto-completion`命令开启自动补全,目前支持zsh/bash
Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,31 @@ Default Language [zh|en] en:
Saving profile[externalTest] ...Done.
```

### Use chainable RamRoleArn

You can use `--mode ChainableRamRoleArn` to combile a source profile and RAM role ARN flow. The following example
get intermediate credentials from source profile `cli-test`, then use it to call AssumeRole for get final credentials.

```json
{
"profiles": [
{
"name": "chain",
"mode": "ChainableRamRoleArn",
"ram_role_arn": "acs:ram::<Account ID>:role/<Role Name>",
"ram_session_name": "session",
"source_profile": "cli-test"
},
{
"name": "cli-test",
"mode": "AK",
"access_key_id": "<Access Key ID>",
"access_key_secret": "<Access Key Secret>"
}
]
}
```

### Enable bash/zsh auto completion

- Use `aliyun auto-completion` command to enable auto completion in zsh/bash
Expand Down
32 changes: 30 additions & 2 deletions config/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,18 @@ var hookSaveConfiguration = func(fn func(config *Configuration) error) func(conf
return fn
}

func loadConfiguration() (*Configuration, error) {
return hookLoadConfiguration(LoadConfiguration)(GetConfigPath() + "/" + configFile)
}

func NewConfigureCommand() *cli.Command {

c := &cli.Command{
Name: "configure",
Short: i18n.T(
"configure credential and settings",
"配置身份认证和其他信息"),
Usage: "configure --mode {AK|StsToken|RamRoleArn|EcsRamRole|RsaKeyPair|RamRoleArnWithRoleName} --profile <profileName>",
Usage: "configure --mode {AK|StsToken|RamRoleArn|EcsRamRole|RsaKeyPair|RamRoleArnWithRoleName|ChainableRamRoleArn} --profile <profileName>",
Run: func(ctx *cli.Context, args []string) error {
if len(args) > 0 {
return cli.NewInvalidCommandError(args[0], ctx)
Expand All @@ -62,7 +66,7 @@ func NewConfigureCommand() *cli.Command {
func doConfigure(ctx *cli.Context, profileName string, mode string) error {
w := ctx.Writer()

conf, err := hookLoadConfiguration(LoadConfiguration)(GetConfigPath() + "/" + configFile)
conf, err := loadConfiguration()
if err != nil {
return err
}
Expand Down Expand Up @@ -107,6 +111,9 @@ func doConfigure(ctx *cli.Context, profileName string, mode string) error {
case RamRoleArnWithEcs:
cp.Mode = RamRoleArnWithEcs
configureRamRoleArnWithEcs(w, &cp)
case ChainableRamRoleArn:
cp.Mode = ChainableRamRoleArn
configureChainableRamRoleArn(w, &cp)
case RsaKeyPair:
cp.Mode = RsaKeyPair
configureRsaKeyPair(w, &cp)
Expand Down Expand Up @@ -144,6 +151,8 @@ func doConfigure(ctx *cli.Context, profileName string, mode string) error {
conf.PutProfile(cp)
conf.CurrentProfile = cp.Name
err = hookSaveConfiguration(SaveConfiguration)(conf)
// cp 要在下文的 DoHello 中使用,所以 需要建立 parent 的关系
cp.parent = conf

if err != nil {
return err
Expand Down Expand Up @@ -200,6 +209,25 @@ func configureEcsRamRole(w io.Writer, cp *Profile) error {
func configureRamRoleArnWithEcs(w io.Writer, cp *Profile) error {
cli.Printf(w, "Ecs Ram Role [%s]: ", cp.RamRoleName)
cp.RamRoleName = ReadInput(cp.RamRoleName)
cli.Printf(w, "Sts Region [%s]: ", cp.StsRegion)
cp.StsRegion = ReadInput(cp.StsRegion)
cli.Printf(w, "Ram Role Arn [%s]: ", cp.RamRoleArn)
cp.RamRoleArn = ReadInput(cp.RamRoleArn)
cli.Printf(w, "Role Session Name [%s]: ", cp.RoleSessionName)
cp.RoleSessionName = ReadInput(cp.RoleSessionName)
if cp.ExpiredSeconds == 0 {
cp.ExpiredSeconds = 900
}
cli.Printf(w, "Expired Seconds [%v]: ", cp.ExpiredSeconds)
cp.ExpiredSeconds, _ = strconv.Atoi(ReadInput(strconv.Itoa(cp.ExpiredSeconds)))
return nil
}

func configureChainableRamRoleArn(w io.Writer, cp *Profile) error {
cli.Printf(w, "Source Profile [%s]: ", cp.SourceProfile)
cp.SourceProfile = ReadInput(cp.SourceProfile)
cli.Printf(w, "Sts Region [%s]: ", cp.StsRegion)
cp.StsRegion = ReadInput(cp.StsRegion)
cli.Printf(w, "Ram Role Arn [%s]: ", cp.RamRoleArn)
cp.RamRoleArn = ReadInput(cp.RamRoleArn)
cli.Printf(w, "Role Session Name [%s]: ", cp.RoleSessionName)
Expand Down
2 changes: 1 addition & 1 deletion config/configure_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func NewConfigureDeleteCommand() *cli.Command {
}

func doConfigureDelete(ctx *cli.Context, profileName string) {
conf, err := hookLoadConfiguration(LoadConfiguration)(GetConfigPath() + "/" + configFile)
conf, err := loadConfiguration()
if err != nil {
cli.Errorf(ctx.Stderr(), "ERROR: load configure failed: %v\n", err)
}
Expand Down
2 changes: 1 addition & 1 deletion config/configure_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func NewConfigureGetCommand() *cli.Command {
}

func doConfigureGet(c *cli.Context, args []string) {
config, err := hookLoadConfiguration(LoadConfiguration)(GetConfigPath() + "/" + configFile)
config, err := loadConfiguration()
if err != nil {
cli.Errorf(c.Stderr(), "load configuration failed %s", err)
cli.Printf(c.Stderr(), "\n")
Expand Down
2 changes: 1 addition & 1 deletion config/configure_get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func TestDoConfigureGet(t *testing.T) {
ctx.Flags().Get("profile").SetAssigned(true)
ctx.Flags().Get("profile").SetValue("default")
doConfigureGet(ctx, []string{})
assert.Equal(t, "{\n\t\"name\": \"default\",\n\t\"mode\": \"AK\",\n\t\"access_key_id\": \"default_aliyun_access_key_id\",\n\t\"access_key_secret\": \"default_aliyun_access_key_secret\",\n\t\"sts_token\": \"\",\n\t\"sts_region\": \"\",\n\t\"ram_role_name\": \"\",\n\t\"ram_role_arn\": \"\",\n\t\"ram_session_name\": \"\",\n\t\"private_key\": \"\",\n\t\"key_pair_name\": \"\",\n\t\"expired_seconds\": 0,\n\t\"verified\": \"\",\n\t\"region_id\": \"\",\n\t\"output_format\": \"json\",\n\t\"language\": \"\",\n\t\"site\": \"\",\n\t\"retry_timeout\": 0,\n\t\"connect_timeout\": 0,\n\t\"retry_count\": 0,\n\t\"process_command\": \"\"\n}\n\n", w.String())
assert.Equal(t, "{\n\t\"name\": \"default\",\n\t\"mode\": \"AK\",\n\t\"access_key_id\": \"default_aliyun_access_key_id\",\n\t\"access_key_secret\": \"default_aliyun_access_key_secret\",\n\t\"sts_token\": \"\",\n\t\"sts_region\": \"\",\n\t\"ram_role_name\": \"\",\n\t\"ram_role_arn\": \"\",\n\t\"ram_session_name\": \"\",\n\t\"source_profile\": \"\",\n\t\"private_key\": \"\",\n\t\"key_pair_name\": \"\",\n\t\"expired_seconds\": 0,\n\t\"verified\": \"\",\n\t\"region_id\": \"\",\n\t\"output_format\": \"json\",\n\t\"language\": \"\",\n\t\"site\": \"\",\n\t\"retry_timeout\": 0,\n\t\"connect_timeout\": 0,\n\t\"retry_count\": 0,\n\t\"process_command\": \"\"\n}\n\n", w.String())

//testcase 5
hookLoadConfiguration = func(fn func(path string) (*Configuration, error)) func(path string) (*Configuration, error) {
Expand Down
4 changes: 3 additions & 1 deletion config/configure_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func NewConfigureListCommand() *cli.Command {
}

func doConfigureList(w io.Writer) {
conf, err := hookLoadConfiguration(LoadConfiguration)(GetConfigPath() + "/" + configFile)
conf, err := loadConfiguration()
if err != nil {
cli.Errorf(w, "ERROR: load configure failed: %v\n", err)
}
Expand Down Expand Up @@ -65,6 +65,8 @@ func doConfigureList(w io.Writer) {
cred = "EcsRamRole:" + pf.RamRoleName
case RamRoleArnWithEcs:
cred = "arn:" + "***" + GetLastChars(pf.AccessKeyId, 3)
case ChainableRamRoleArn:
cred = "ChainableRamRoleArn:" + pf.SourceProfile + ":" + pf.RamRoleArn
case RsaKeyPair:
cred = "RsaKeyPair:" + pf.KeyPairName
case External:
Expand Down
7 changes: 6 additions & 1 deletion config/configure_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func NewConfigureSetCommand() *cli.Command {
}

func doConfigureSet(w io.Writer, flags *cli.FlagSet) {
config, err := hookLoadConfiguration(LoadConfiguration)(GetConfigPath() + "/" + configFile)
config, err := loadConfiguration()
if err != nil {
cli.Errorf(w, "load configuration failed %s", err)
return
Expand Down Expand Up @@ -108,6 +108,11 @@ func doConfigureSet(w io.Writer, flags *cli.FlagSet) {
profile.RamRoleArn = RamRoleArnFlag(flags).GetStringOrDefault(profile.RamRoleArn)
profile.RoleSessionName = RoleSessionNameFlag(flags).GetStringOrDefault(profile.RoleSessionName)
profile.ExpiredSeconds = ExpiredSecondsFlag(flags).GetIntegerOrDefault(profile.ExpiredSeconds)
case ChainableRamRoleArn:
profile.SourceProfile = SourceProfileFlag(flags).GetStringOrDefault(profile.SourceProfile)
profile.RamRoleArn = RamRoleArnFlag(flags).GetStringOrDefault(profile.RamRoleArn)
profile.RoleSessionName = RoleSessionNameFlag(flags).GetStringOrDefault(profile.RoleSessionName)
profile.ExpiredSeconds = ExpiredSecondsFlag(flags).GetIntegerOrDefault(profile.ExpiredSeconds)
case RsaKeyPair:
profile.PrivateKey = PrivateKeyFlag(flags).GetStringOrDefault(profile.PrivateKey)
profile.KeyPairName = KeyPairNameFlag(flags).GetStringOrDefault(profile.KeyPairName)
Expand Down
6 changes: 3 additions & 3 deletions config/configure_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func TestDoConfigureSet(t *testing.T) {
doConfigureSet(w, fs)
assert.Empty(t, w.String())

//RamRoleArnWithEcs
// RamRoleArnWithEcs
hookLoadConfiguration = func(fn func(path string) (*Configuration, error)) func(path string) (*Configuration, error) {
return func(path string) (*Configuration, error) {
return &Configuration{CurrentProfile: "default", Profiles: []Profile{Profile{Name: "default", Mode: RamRoleArnWithEcs, RamRoleName: "RamRoleName", RoleSessionName: "RoleSessionName", RamRoleArn: "RamRoleArn", AccessKeyId: "default_aliyun_access_key_id", AccessKeySecret: "default_aliyun_access_key_secret", OutputFormat: "json", RegionId: "cn-hangzhou"}, Profile{Name: "aaa", Mode: AK, AccessKeyId: "sdf", AccessKeySecret: "ddf", OutputFormat: "json"}}}, nil
Expand All @@ -109,7 +109,7 @@ func TestDoConfigureSet(t *testing.T) {
doConfigureSet(w, fs)
assert.Empty(t, w.String())

//RsaKeyPair
// RsaKeyPair
hookLoadConfiguration = func(fn func(path string) (*Configuration, error)) func(path string) (*Configuration, error) {
return func(path string) (*Configuration, error) {
return &Configuration{CurrentProfile: "default", Profiles: []Profile{Profile{Name: "default", Mode: RsaKeyPair, KeyPairName: "KeyPairName", PrivateKey: "PrivateKey", AccessKeyId: "default_aliyun_access_key_id", AccessKeySecret: "default_aliyun_access_key_secret", OutputFormat: "json", RegionId: "cn-hangzhou"}, Profile{Name: "aaa", Mode: AK, AccessKeyId: "sdf", AccessKeySecret: "ddf", OutputFormat: "json"}}}, nil
Expand All @@ -119,7 +119,7 @@ func TestDoConfigureSet(t *testing.T) {
doConfigureSet(w, fs)
assert.Empty(t, w.String())

//External
// External
hookLoadConfiguration = func(fn func(path string) (*Configuration, error)) func(path string) (*Configuration, error) {
return func(path string) (*Configuration, error) {
return &Configuration{CurrentProfile: "default", Profiles: []Profile{Profile{Name: "default", Mode: External, ProcessCommand: "process command", OutputFormat: "json", RegionId: "cn-hangzhou"}, Profile{Name: "aaa", Mode: AK, AccessKeyId: "sdf", AccessKeySecret: "ddf", OutputFormat: "json"}}}, nil
Expand Down
5 changes: 5 additions & 0 deletions config/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const (
RamRoleNameFlagName = "ram-role-name"
RamRoleArnFlagName = "ram-role-arn"
RoleSessionNameFlagName = "role-session-name"
SourceProfileFlagName = "source-profile"
PrivateKeyFlagName = "private-key"
KeyPairNameFlagName = "key-pair-name"
RegionFlagName = "region"
Expand Down Expand Up @@ -100,6 +101,10 @@ func RamRoleArnFlag(fs *cli.FlagSet) *cli.Flag {
return fs.Get(RamRoleArnFlagName)
}

func SourceProfileFlag(fs *cli.FlagSet) *cli.Flag {
return fs.Get(SourceProfileFlagName)
}

func RoleSessionNameFlag(fs *cli.FlagSet) *cli.Flag {
return fs.Get(RoleSessionNameFlagName)
}
Expand Down
Loading

0 comments on commit c962ad3

Please sign in to comment.