AWS CLI ECR login 分析

現在如果使用 brew 更新 AWS CLI 會更新到 version 2,會發現原本使用的 ECR login 指令 $(aws ecr get-login --no-include-email --region us-west-2) 不能用會出現錯誤

usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
To see help text, you can run:

  aws help
  aws <command> help
  aws <command> <subcommand> help
aws: error: argument operation: Invalid choice, valid choices are:

batch-check-layer-availability           | batch-delete-image
batch-get-image                          | complete-layer-upload
create-repository                        | delete-lifecycle-policy
delete-repository                        | delete-repository-policy
describe-image-scan-findings             | describe-images
describe-repositories                    | get-authorization-token
get-download-url-for-layer               | get-lifecycle-policy
get-lifecycle-policy-preview             | get-repository-policy
initiate-layer-upload                    | list-images
list-tags-for-resource                   | put-image
put-image-scanning-configuration         | put-image-tag-mutability
put-lifecycle-policy                     | set-repository-policy
start-image-scan                         | start-lifecycle-policy-preview
tag-resource                             | untag-resource
upload-layer-part                        | get-login-password
wait                                     | help

AWS CLI v2 使用方法

因此就一定要使用新的指令來做 ECR Login,使用方法

aws ecr get-login-password | docker login --username AWS --password-stdin https://8888888888888.dkr.ecr.us-west-2.amazonaws.com

與 v1 版本指令不同需要自行輸入 username 為 AWS 與自己 ECR 的 Proxy Endpoint 位置來登入。

安全性分析

v1 版本

使用指令 aws ecr get-login --no-include-email --region us-west-2 會直接輸出

docker login -u AWS -p eyJwYXlsb2FkIjoiS3V3UXZJQ2FWWXBsNlBreHVzeGREekNIOHBaWkphNTFsS0dCelVS
...
REFUQV9LRVkiLCJleHBpcmF0aW9uIjoxNTgyNTAwMTM2fQ== https://8888888888888.dkr.ecr.us-west-2.amazonaws.com

所以使用方法是 $(aws ecr get-login --no-include-email --region us-west-2) 把指令帶入,但是這樣可能會發生 shell 紀錄或是日誌紀錄密碼的風險,所以 v2 版本做了改進。

v2 版本

使用指令 aws ecr get-login-password 改為只有輸出密碼

eyJwYXlsb2FkIjoiTXFWWGR4d3BzNVZHbUNacThheDdhZ2Z6TXBwUjJJMVBDU1FETEIy
...
REFUQV9LRVkiLCJleHBpcmF0aW9uIjoxNTgyNTAxMDE1fQ==

因此使用方法改為 aws ecr get-login-password | docker login --username AWS --password-stdin https://8888888888888.dkr.ecr.us-west-2.amazonaws.com,使用 pipe 把密碼送入 docker login 指令,減少將密碼暴露給 shell 紀錄或是日誌紀錄的風險

Source Code 分析

看了上面的指令使用方法說明我們來看一下指令實作方法哪裡不同。

v1 版本

我們主要關注的實作在於 https://github.com/aws/aws-cli/blob/1.18.5/awscli/customizations/ecr.py#L73https://github.com/aws/aws-cli/blob/1.18.5/awscli/customizations/ecr.py#L77-L85

result = ecr_client.get_authorization_token()
        for auth in result['authorizationData']:
            auth_token = b64decode(auth['authorizationToken']).decode()
            username, password = auth_token.split(':')
            command = ['docker', 'login', '-u', username, '-p', password]
            if parsed_args.include_email:
                command.extend(['-e', 'none'])
            command.append(auth['proxyEndpoint'])
            sys.stdout.write(' '.join(command))
            sys.stdout.write('\n')

我們可以在文件看到 get_authorization_token() 回傳的資料

{
    'authorizationData': [
        {
            'authorizationToken': 'string',
            'expiresAt': datetime(2015, 1, 1),
            'proxyEndpoint': 'string'
        },
    ]
}

所以可以看到 L80 把 usernamepassword 的變數塞入製作指令,L83 把 proxyEndpoint 塞入指令,在 L84 使用 stdout 輸出來完成指令的實作。

v2 版本

我們在 v2 版本關注的實作在於 https://github.com/aws/aws-cli/blob/1.18.5/awscli/customizations/ecr.py#L101-L106

        result = ecr_client.get_authorization_token()
        auth = result['authorizationData'][0]
        auth_token = b64decode(auth['authorizationToken']).decode()
        _, password = auth_token.split(':')
        sys.stdout.write(password)
        sys.stdout.write('\n')

基本上可以看出與 v1 大致上相同,不一樣的地方在於 L104 只有把 password 放入變數,在 L105 輸出實作這個指令。

最後

如果 AWS CLI 版本是 1.17.10 ~ 1.18.5 新舊指令都可以使用,但是如果使用的版本是 2.0.0 就沒有辦法使用舊指令了,不過為了安全問題如果使用的是 1.17.10 以上的版本就建議使用新版指令!

參考資料