awsgetsts 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- awsgetsts-0.1.0/LICENSE +21 -0
- awsgetsts-0.1.0/MANIFEST.in +7 -0
- awsgetsts-0.1.0/PKG-INFO +203 -0
- awsgetsts-0.1.0/README.md +168 -0
- awsgetsts-0.1.0/aws_user_config_sample.json +8 -0
- awsgetsts-0.1.0/awsgetsts/__init__.py +11 -0
- awsgetsts-0.1.0/awsgetsts/__main__.py +10 -0
- awsgetsts-0.1.0/awsgetsts/aws_config.py +224 -0
- awsgetsts-0.1.0/awsgetsts/cli.py +608 -0
- awsgetsts-0.1.0/awsgetsts/config.py +126 -0
- awsgetsts-0.1.0/awsgetsts/lang.py +95 -0
- awsgetsts-0.1.0/awsgetsts/logger.py +50 -0
- awsgetsts-0.1.0/awsgetsts/mfa.py +10 -0
- awsgetsts-0.1.0/awsgetsts/setup.py +491 -0
- awsgetsts-0.1.0/awsgetsts/sts.py +79 -0
- awsgetsts-0.1.0/awsgetsts.egg-info/PKG-INFO +203 -0
- awsgetsts-0.1.0/awsgetsts.egg-info/SOURCES.txt +22 -0
- awsgetsts-0.1.0/awsgetsts.egg-info/dependency_links.txt +1 -0
- awsgetsts-0.1.0/awsgetsts.egg-info/entry_points.txt +2 -0
- awsgetsts-0.1.0/awsgetsts.egg-info/requires.txt +5 -0
- awsgetsts-0.1.0/awsgetsts.egg-info/top_level.txt +1 -0
- awsgetsts-0.1.0/pyproject.toml +51 -0
- awsgetsts-0.1.0/requirements.txt +5 -0
- awsgetsts-0.1.0/setup.cfg +4 -0
awsgetsts-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 louky0714
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
awsgetsts-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: awsgetsts
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Issue AWS STS session tokens (MFA/TOTP) and merge them into ~/.aws/config
|
|
5
|
+
Author-email: louky0714 <louky0714@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/HonamSong/aws-getsts
|
|
8
|
+
Project-URL: Repository, https://github.com/HonamSong/aws-getsts
|
|
9
|
+
Project-URL: Issues, https://github.com/HonamSong/aws-getsts/issues
|
|
10
|
+
Keywords: aws,sts,mfa,totp,cli,credentials
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: System Administrators
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Topic :: Security
|
|
25
|
+
Classifier: Topic :: System :: Systems Administration :: Authentication/Directory
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: boto3<2,>=1.43
|
|
30
|
+
Requires-Dist: pyotp<3,>=2.9
|
|
31
|
+
Requires-Dist: python-dateutil<3,>=2.9
|
|
32
|
+
Requires-Dist: cplogger<1,>=0.1
|
|
33
|
+
Requires-Dist: termcolor<4,>=2
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# awsgetsts
|
|
37
|
+
|
|
38
|
+
AWS STS 임시 자격증명(session token)을 MFA/TOTP 와 함께 자동으로 발급·갱신하여
|
|
39
|
+
`~/.aws/config` 에 `[profile sts-<name>]` 섹션으로 저장하는 CLI 유틸리티.
|
|
40
|
+
|
|
41
|
+
Small CLI that issues AWS STS session tokens (with MFA/TOTP) and merges
|
|
42
|
+
them into `~/.aws/config` as `[profile sts-<name>]` sections.
|
|
43
|
+
|
|
44
|
+
## 특징 / Features
|
|
45
|
+
|
|
46
|
+
- MFA 디바이스 ARN + TOTP shared secret 으로 `sts:GetSessionToken` 호출
|
|
47
|
+
- 기존 토큰 유효기간 확인 후 만료 임박 시에만 갱신 (`--force` 로 강제)
|
|
48
|
+
- 다중 프로필 지원, `--profile` 반복 지정 가능
|
|
49
|
+
- 대화형 마법사(`--setup`)로 최초 설정 파일 생성
|
|
50
|
+
- 종합 진단(`--check`): 설정 파일 / AWS 프로필 / 토큰 유효성 점검
|
|
51
|
+
- 한/영 자동 전환 (터미널 UTF-8 지원 여부 기준, `AWSGETSTS_LANG` 로 강제)
|
|
52
|
+
- 컬러 상태 표시 (TTY 자동 감지, `FORCE_COLOR` / `NO_COLOR` 존중)
|
|
53
|
+
|
|
54
|
+
## 요구사항 / Requirements
|
|
55
|
+
|
|
56
|
+
- Python 3.9+
|
|
57
|
+
- 의존성 / dependencies: `boto3`, `pyotp`, `python-dateutil`, `cplogger`, `termcolor`
|
|
58
|
+
|
|
59
|
+
설치 / install:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
pip install boto3 pyotp python-dateutil cplogger termcolor
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 빠른 시작 / Quick start
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
# 최초 설정 (대화형 마법사) / first-time setup
|
|
69
|
+
./awsgetsts.py --setup
|
|
70
|
+
|
|
71
|
+
# 상태 진단 / diagnose
|
|
72
|
+
./awsgetsts.py --check
|
|
73
|
+
|
|
74
|
+
# 모든 프로필 갱신 / refresh all profiles
|
|
75
|
+
./awsgetsts.py
|
|
76
|
+
|
|
77
|
+
# 특정 프로필만 / one profile
|
|
78
|
+
./awsgetsts.py -p dev
|
|
79
|
+
|
|
80
|
+
# 강제 재발급 / force refresh
|
|
81
|
+
./awsgetsts.py --force
|
|
82
|
+
|
|
83
|
+
# 파일에 쓰지 않고 미리보기 / dry-run
|
|
84
|
+
./awsgetsts.py --dry-run
|
|
85
|
+
|
|
86
|
+
# 특정 프로필만 수정 / edit one profile
|
|
87
|
+
./awsgetsts.py --edit -p dev
|
|
88
|
+
|
|
89
|
+
# 특정 프로필만 삭제 / delete one profile
|
|
90
|
+
./awsgetsts.py --clean -p dev
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## CLI 옵션 / Options
|
|
94
|
+
|
|
95
|
+
| 옵션 | 설명 |
|
|
96
|
+
| --- | --- |
|
|
97
|
+
| -p, --profile NAME | 대상 프로필 (반복 가능). 생략 시 설정된 모든 프로필. |
|
|
98
|
+
| -c, --config PATH | 사용자 설정 파일 경로 |
|
|
99
|
+
| -d, --duration SEC | 세션 지속시간(초). 기본값은 sts 모듈의 DEFAULT_DURATION |
|
|
100
|
+
| --force | 기존 토큰이 유효해도 재발급 |
|
|
101
|
+
| --dry-run | ~/.aws/config 에 쓰지 않고 표준출력에 INI 출력 |
|
|
102
|
+
| --cli-pager | 각 sts 섹션에 `cli_pager =` 추가 (프롬프트 스킵) |
|
|
103
|
+
| --no-cli-pager | `cli_pager =` 추가 안 함 (프롬프트 스킵) |
|
|
104
|
+
| --setup | 대화형 설정 마법사 실행 |
|
|
105
|
+
| --edit | 선택한 프로필만 대화형으로 수정 (`-p` 필수, 필드별 Enter로 기존값 유지) |
|
|
106
|
+
| --list | 사용자 설정 파일의 프로필 이름 목록 |
|
|
107
|
+
| --list-aws | ~/.aws/config 의 프로필 이름 목록 |
|
|
108
|
+
| --check | 설정 / AWS 프로필 / 토큰 유효성 진단 (STS 미호출) |
|
|
109
|
+
| --clean | 도구가 만든 파일 삭제 (사용자 JSON + `awsUserConfig.json` 프로필과 매칭되는 sts-* 섹션만). `-p` 지정 시 해당 프로필만 부분 삭제 (파일 유지). `--dry-run` 병용 시 미리보기 |
|
|
110
|
+
| -v, --verbose | DEBUG 로그까지 출력 |
|
|
111
|
+
| -q, --quiet | 경고/오류만 출력 |
|
|
112
|
+
| -V, --version | 버전 표시 |
|
|
113
|
+
|
|
114
|
+
## 설정 파일 / Config file
|
|
115
|
+
|
|
116
|
+
기본 위치 / default location:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
~/.config/getAwsSTS/awsUserConfig.json
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
탐색 순서 / lookup order:
|
|
123
|
+
|
|
124
|
+
1. `--config` 로 명시한 경로
|
|
125
|
+
2. `AWSGETSTS_CONFIG` 환경변수
|
|
126
|
+
3. `~/.config/getAwsSTS/awsUserConfig.json` (정규 기본값)
|
|
127
|
+
4. (legacy) 엔트리 스크립트 옆 `aws_user_config.json`
|
|
128
|
+
5. (legacy) 현재 디렉토리 `./aws_user_config.json`
|
|
129
|
+
|
|
130
|
+
포맷 / format:
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
{
|
|
134
|
+
"dev": {
|
|
135
|
+
"account_id": "123456789012",
|
|
136
|
+
"profile": "dev",
|
|
137
|
+
"mfa_serial": "arn:aws:iam::123456789012:mfa/your-username",
|
|
138
|
+
"totp_secret": "BASE32SECRET"
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
- `account_id`: 12자리 AWS 계정 ID
|
|
144
|
+
- `profile`: `~/.aws/config` 의 base 프로필 이름 (자격증명 조회 소스)
|
|
145
|
+
- `mfa_serial`: MFA 디바이스 ARN
|
|
146
|
+
- `totp_secret`: MFA 등록 시 발급되는 base32 shared secret
|
|
147
|
+
|
|
148
|
+
파일은 wizard 가 자동으로 `chmod 600` 처리.
|
|
149
|
+
|
|
150
|
+
## 동작 흐름 / How it works
|
|
151
|
+
|
|
152
|
+
1. `~/.aws/config` 에 base profile (`profile <name>`) 존재 확인
|
|
153
|
+
2. 기존 `profile sts-<name>` 섹션의 만료시각 확인 → 유효하면 스킵
|
|
154
|
+
3. `sts:GetSessionToken` 호출 (TOTP 코드로 MFA 인증)
|
|
155
|
+
4. 발급된 자격증명을 `[profile sts-<name>]` 로 병합 저장
|
|
156
|
+
5. 옵션에 따라 `cli_pager =` 추가
|
|
157
|
+
|
|
158
|
+
## 환경변수 / Environment variables
|
|
159
|
+
|
|
160
|
+
| 변수 | 용도 |
|
|
161
|
+
| --- | --- |
|
|
162
|
+
| AWSGETSTS_CONFIG | 사용자 설정 파일 경로 override |
|
|
163
|
+
| AWSGETSTS_LANG | `ko` / `en` — 출력 언어 강제 (기본: 터미널 인코딩) |
|
|
164
|
+
| FORCE_COLOR | 비-TTY 에서도 컬러 강제 |
|
|
165
|
+
| NO_COLOR | 컬러 비활성화 |
|
|
166
|
+
|
|
167
|
+
## --check 출력 예 / Sample --check output
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
[awsgetsts --check] 프로필 진단
|
|
171
|
+
────────────────────────────────────────────────────────────
|
|
172
|
+
Files
|
|
173
|
+
user_config : FOUND ~/.config/getAwsSTS/awsUserConfig.json
|
|
174
|
+
aws_config : FOUND ~/.aws/config
|
|
175
|
+
aws_credentials : FOUND ~/.aws/credentials
|
|
176
|
+
────────────────────────────────────────────────────────────
|
|
177
|
+
[dev] user_config=OK | aws_profile=OK | token=VALID (until 2026-09-02 03:15:00 KST)
|
|
178
|
+
[prod] user_config=OK | aws_profile=OK | token=EXPIRED (2026-09-01 08:00:00 KST)
|
|
179
|
+
────────────────────────────────────────────────────────────
|
|
180
|
+
[awsgetsts --check] Summary
|
|
181
|
+
total : 2
|
|
182
|
+
valid : 1
|
|
183
|
+
expired : 1
|
|
184
|
+
no_token : 0
|
|
185
|
+
missing_user_cfg : 0
|
|
186
|
+
missing_aws_profile : 0
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## 종료 코드 / Exit codes
|
|
190
|
+
|
|
191
|
+
| 코드 | 의미 |
|
|
192
|
+
| --- | --- |
|
|
193
|
+
| 0 | 정상 종료 |
|
|
194
|
+
| 1 | STS 호출 실패 등 발급 실패 존재 |
|
|
195
|
+
| 2 | 잘못된 인자 / 설정 로드 실패 / 알 수 없는 프로필 |
|
|
196
|
+
| 130 | 사용자 취소 (Ctrl+C / EOF) |
|
|
197
|
+
|
|
198
|
+
## 보안 / Security
|
|
199
|
+
|
|
200
|
+
- `awsUserConfig.json` 은 TOTP shared secret 을 포함 → 반드시 `chmod 600`, 유출 금지
|
|
201
|
+
- 노출된 secret 은 즉시 MFA 재등록으로 폐기
|
|
202
|
+
- `.aws/credentials` 의 Access Key 도 동일하게 취급
|
|
203
|
+
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# awsgetsts
|
|
2
|
+
|
|
3
|
+
AWS STS 임시 자격증명(session token)을 MFA/TOTP 와 함께 자동으로 발급·갱신하여
|
|
4
|
+
`~/.aws/config` 에 `[profile sts-<name>]` 섹션으로 저장하는 CLI 유틸리티.
|
|
5
|
+
|
|
6
|
+
Small CLI that issues AWS STS session tokens (with MFA/TOTP) and merges
|
|
7
|
+
them into `~/.aws/config` as `[profile sts-<name>]` sections.
|
|
8
|
+
|
|
9
|
+
## 특징 / Features
|
|
10
|
+
|
|
11
|
+
- MFA 디바이스 ARN + TOTP shared secret 으로 `sts:GetSessionToken` 호출
|
|
12
|
+
- 기존 토큰 유효기간 확인 후 만료 임박 시에만 갱신 (`--force` 로 강제)
|
|
13
|
+
- 다중 프로필 지원, `--profile` 반복 지정 가능
|
|
14
|
+
- 대화형 마법사(`--setup`)로 최초 설정 파일 생성
|
|
15
|
+
- 종합 진단(`--check`): 설정 파일 / AWS 프로필 / 토큰 유효성 점검
|
|
16
|
+
- 한/영 자동 전환 (터미널 UTF-8 지원 여부 기준, `AWSGETSTS_LANG` 로 강제)
|
|
17
|
+
- 컬러 상태 표시 (TTY 자동 감지, `FORCE_COLOR` / `NO_COLOR` 존중)
|
|
18
|
+
|
|
19
|
+
## 요구사항 / Requirements
|
|
20
|
+
|
|
21
|
+
- Python 3.9+
|
|
22
|
+
- 의존성 / dependencies: `boto3`, `pyotp`, `python-dateutil`, `cplogger`, `termcolor`
|
|
23
|
+
|
|
24
|
+
설치 / install:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
pip install boto3 pyotp python-dateutil cplogger termcolor
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 빠른 시작 / Quick start
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
# 최초 설정 (대화형 마법사) / first-time setup
|
|
34
|
+
./awsgetsts.py --setup
|
|
35
|
+
|
|
36
|
+
# 상태 진단 / diagnose
|
|
37
|
+
./awsgetsts.py --check
|
|
38
|
+
|
|
39
|
+
# 모든 프로필 갱신 / refresh all profiles
|
|
40
|
+
./awsgetsts.py
|
|
41
|
+
|
|
42
|
+
# 특정 프로필만 / one profile
|
|
43
|
+
./awsgetsts.py -p dev
|
|
44
|
+
|
|
45
|
+
# 강제 재발급 / force refresh
|
|
46
|
+
./awsgetsts.py --force
|
|
47
|
+
|
|
48
|
+
# 파일에 쓰지 않고 미리보기 / dry-run
|
|
49
|
+
./awsgetsts.py --dry-run
|
|
50
|
+
|
|
51
|
+
# 특정 프로필만 수정 / edit one profile
|
|
52
|
+
./awsgetsts.py --edit -p dev
|
|
53
|
+
|
|
54
|
+
# 특정 프로필만 삭제 / delete one profile
|
|
55
|
+
./awsgetsts.py --clean -p dev
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## CLI 옵션 / Options
|
|
59
|
+
|
|
60
|
+
| 옵션 | 설명 |
|
|
61
|
+
| --- | --- |
|
|
62
|
+
| -p, --profile NAME | 대상 프로필 (반복 가능). 생략 시 설정된 모든 프로필. |
|
|
63
|
+
| -c, --config PATH | 사용자 설정 파일 경로 |
|
|
64
|
+
| -d, --duration SEC | 세션 지속시간(초). 기본값은 sts 모듈의 DEFAULT_DURATION |
|
|
65
|
+
| --force | 기존 토큰이 유효해도 재발급 |
|
|
66
|
+
| --dry-run | ~/.aws/config 에 쓰지 않고 표준출력에 INI 출력 |
|
|
67
|
+
| --cli-pager | 각 sts 섹션에 `cli_pager =` 추가 (프롬프트 스킵) |
|
|
68
|
+
| --no-cli-pager | `cli_pager =` 추가 안 함 (프롬프트 스킵) |
|
|
69
|
+
| --setup | 대화형 설정 마법사 실행 |
|
|
70
|
+
| --edit | 선택한 프로필만 대화형으로 수정 (`-p` 필수, 필드별 Enter로 기존값 유지) |
|
|
71
|
+
| --list | 사용자 설정 파일의 프로필 이름 목록 |
|
|
72
|
+
| --list-aws | ~/.aws/config 의 프로필 이름 목록 |
|
|
73
|
+
| --check | 설정 / AWS 프로필 / 토큰 유효성 진단 (STS 미호출) |
|
|
74
|
+
| --clean | 도구가 만든 파일 삭제 (사용자 JSON + `awsUserConfig.json` 프로필과 매칭되는 sts-* 섹션만). `-p` 지정 시 해당 프로필만 부분 삭제 (파일 유지). `--dry-run` 병용 시 미리보기 |
|
|
75
|
+
| -v, --verbose | DEBUG 로그까지 출력 |
|
|
76
|
+
| -q, --quiet | 경고/오류만 출력 |
|
|
77
|
+
| -V, --version | 버전 표시 |
|
|
78
|
+
|
|
79
|
+
## 설정 파일 / Config file
|
|
80
|
+
|
|
81
|
+
기본 위치 / default location:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
~/.config/getAwsSTS/awsUserConfig.json
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
탐색 순서 / lookup order:
|
|
88
|
+
|
|
89
|
+
1. `--config` 로 명시한 경로
|
|
90
|
+
2. `AWSGETSTS_CONFIG` 환경변수
|
|
91
|
+
3. `~/.config/getAwsSTS/awsUserConfig.json` (정규 기본값)
|
|
92
|
+
4. (legacy) 엔트리 스크립트 옆 `aws_user_config.json`
|
|
93
|
+
5. (legacy) 현재 디렉토리 `./aws_user_config.json`
|
|
94
|
+
|
|
95
|
+
포맷 / format:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
{
|
|
99
|
+
"dev": {
|
|
100
|
+
"account_id": "123456789012",
|
|
101
|
+
"profile": "dev",
|
|
102
|
+
"mfa_serial": "arn:aws:iam::123456789012:mfa/your-username",
|
|
103
|
+
"totp_secret": "BASE32SECRET"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
- `account_id`: 12자리 AWS 계정 ID
|
|
109
|
+
- `profile`: `~/.aws/config` 의 base 프로필 이름 (자격증명 조회 소스)
|
|
110
|
+
- `mfa_serial`: MFA 디바이스 ARN
|
|
111
|
+
- `totp_secret`: MFA 등록 시 발급되는 base32 shared secret
|
|
112
|
+
|
|
113
|
+
파일은 wizard 가 자동으로 `chmod 600` 처리.
|
|
114
|
+
|
|
115
|
+
## 동작 흐름 / How it works
|
|
116
|
+
|
|
117
|
+
1. `~/.aws/config` 에 base profile (`profile <name>`) 존재 확인
|
|
118
|
+
2. 기존 `profile sts-<name>` 섹션의 만료시각 확인 → 유효하면 스킵
|
|
119
|
+
3. `sts:GetSessionToken` 호출 (TOTP 코드로 MFA 인증)
|
|
120
|
+
4. 발급된 자격증명을 `[profile sts-<name>]` 로 병합 저장
|
|
121
|
+
5. 옵션에 따라 `cli_pager =` 추가
|
|
122
|
+
|
|
123
|
+
## 환경변수 / Environment variables
|
|
124
|
+
|
|
125
|
+
| 변수 | 용도 |
|
|
126
|
+
| --- | --- |
|
|
127
|
+
| AWSGETSTS_CONFIG | 사용자 설정 파일 경로 override |
|
|
128
|
+
| AWSGETSTS_LANG | `ko` / `en` — 출력 언어 강제 (기본: 터미널 인코딩) |
|
|
129
|
+
| FORCE_COLOR | 비-TTY 에서도 컬러 강제 |
|
|
130
|
+
| NO_COLOR | 컬러 비활성화 |
|
|
131
|
+
|
|
132
|
+
## --check 출력 예 / Sample --check output
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
[awsgetsts --check] 프로필 진단
|
|
136
|
+
────────────────────────────────────────────────────────────
|
|
137
|
+
Files
|
|
138
|
+
user_config : FOUND ~/.config/getAwsSTS/awsUserConfig.json
|
|
139
|
+
aws_config : FOUND ~/.aws/config
|
|
140
|
+
aws_credentials : FOUND ~/.aws/credentials
|
|
141
|
+
────────────────────────────────────────────────────────────
|
|
142
|
+
[dev] user_config=OK | aws_profile=OK | token=VALID (until 2026-09-02 03:15:00 KST)
|
|
143
|
+
[prod] user_config=OK | aws_profile=OK | token=EXPIRED (2026-09-01 08:00:00 KST)
|
|
144
|
+
────────────────────────────────────────────────────────────
|
|
145
|
+
[awsgetsts --check] Summary
|
|
146
|
+
total : 2
|
|
147
|
+
valid : 1
|
|
148
|
+
expired : 1
|
|
149
|
+
no_token : 0
|
|
150
|
+
missing_user_cfg : 0
|
|
151
|
+
missing_aws_profile : 0
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## 종료 코드 / Exit codes
|
|
155
|
+
|
|
156
|
+
| 코드 | 의미 |
|
|
157
|
+
| --- | --- |
|
|
158
|
+
| 0 | 정상 종료 |
|
|
159
|
+
| 1 | STS 호출 실패 등 발급 실패 존재 |
|
|
160
|
+
| 2 | 잘못된 인자 / 설정 로드 실패 / 알 수 없는 프로필 |
|
|
161
|
+
| 130 | 사용자 취소 (Ctrl+C / EOF) |
|
|
162
|
+
|
|
163
|
+
## 보안 / Security
|
|
164
|
+
|
|
165
|
+
- `awsUserConfig.json` 은 TOTP shared secret 을 포함 → 반드시 `chmod 600`, 유출 금지
|
|
166
|
+
- 노출된 secret 은 즉시 MFA 재등록으로 폐기
|
|
167
|
+
- `.aws/credentials` 의 Access Key 도 동일하게 취급
|
|
168
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""awsgetsts — AWS STS 임시 자격증명 발급 유틸리티.
|
|
2
|
+
|
|
3
|
+
awsgetsts is a small utility that fetches AWS STS session tokens
|
|
4
|
+
(optionally with MFA/TOTP) and writes them to ``~/.aws/config``
|
|
5
|
+
as ``profile sts-<name>`` entries.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
__version__ = "0.1.0"
|
|
11
|
+
__all__ = ["__version__"]
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""~/.aws/config 관련 유틸 / Helpers for ~/.aws/config.
|
|
2
|
+
|
|
3
|
+
- 프로필 존재 여부 확인 / profile existence checks
|
|
4
|
+
- 토큰 만료 검사 / expiration check for existing sts-<name> profiles
|
|
5
|
+
- INI 파일 병합 저장 / merge-write INI sections
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import configparser
|
|
11
|
+
import datetime
|
|
12
|
+
from datetime import timedelta, timezone
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Iterable, List, Optional
|
|
15
|
+
|
|
16
|
+
from dateutil.tz import tzutc
|
|
17
|
+
|
|
18
|
+
from .logger import log
|
|
19
|
+
|
|
20
|
+
AWS_CONFIG_PATH = Path.home() / ".aws" / "config"
|
|
21
|
+
AWS_CREDENTIALS_PATH = Path.home() / ".aws" / "credentials"
|
|
22
|
+
KST = timezone(timedelta(hours=9), name="KST")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def convert_to_kst(dt: datetime.datetime) -> datetime.datetime:
|
|
26
|
+
"""UTC → KST 변환 / Convert to Korea Standard Time."""
|
|
27
|
+
if dt.tzinfo is None:
|
|
28
|
+
dt = dt.replace(tzinfo=tzutc())
|
|
29
|
+
return dt.astimezone(KST)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _read_ini(path: Path) -> configparser.ConfigParser:
|
|
33
|
+
parser = configparser.ConfigParser()
|
|
34
|
+
if path.exists():
|
|
35
|
+
parser.read(path)
|
|
36
|
+
return parser
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def list_aws_profiles(
|
|
40
|
+
config_path: Path = AWS_CONFIG_PATH,
|
|
41
|
+
credentials_path: Path = AWS_CREDENTIALS_PATH,
|
|
42
|
+
) -> List[str]:
|
|
43
|
+
"""~/.aws 에 설정된 모든 프로필 이름 반환 / List AWS profiles."""
|
|
44
|
+
profiles: set[str] = set()
|
|
45
|
+
|
|
46
|
+
cfg = _read_ini(config_path)
|
|
47
|
+
for section in cfg.sections():
|
|
48
|
+
name = section.replace("profile ", "") if section != "default" else "default"
|
|
49
|
+
profiles.add(name)
|
|
50
|
+
|
|
51
|
+
creds = _read_ini(credentials_path)
|
|
52
|
+
for section in creds.sections():
|
|
53
|
+
profiles.add(section)
|
|
54
|
+
|
|
55
|
+
return sorted(profiles)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def profile_exists(
|
|
59
|
+
profile_name: str,
|
|
60
|
+
config_path: Path = AWS_CONFIG_PATH,
|
|
61
|
+
credentials_path: Path = AWS_CREDENTIALS_PATH,
|
|
62
|
+
) -> bool:
|
|
63
|
+
"""지정 프로필이 존재하는지 확인 / Check whether a profile is configured."""
|
|
64
|
+
cfg = _read_ini(config_path)
|
|
65
|
+
section = "default" if profile_name == "default" else f"profile {profile_name}"
|
|
66
|
+
if cfg.has_section(section):
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
creds = _read_ini(credentials_path)
|
|
70
|
+
return creds.has_section(profile_name)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def existing_token_valid(
|
|
74
|
+
profile_name: str,
|
|
75
|
+
config_path: Path = AWS_CONFIG_PATH,
|
|
76
|
+
now: Optional[datetime.datetime] = None,
|
|
77
|
+
) -> bool:
|
|
78
|
+
"""기존 sts-<name> 프로필이 아직 유효한지 검사 / Is the stored token still valid?"""
|
|
79
|
+
section = f"profile sts-{profile_name}"
|
|
80
|
+
|
|
81
|
+
cfg = _read_ini(config_path)
|
|
82
|
+
if not cfg.has_section(section):
|
|
83
|
+
log.debug(f"section {section} not found")
|
|
84
|
+
return False
|
|
85
|
+
if not cfg.has_option(section, "expiration"):
|
|
86
|
+
log.debug(f"expiration missing in {section}")
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
expiration_str = cfg.get(section, "expiration")
|
|
90
|
+
try:
|
|
91
|
+
expiration = datetime.datetime.fromisoformat(expiration_str)
|
|
92
|
+
except ValueError as e:
|
|
93
|
+
log.warn(f"invalid expiration in {section}: {e}")
|
|
94
|
+
return False
|
|
95
|
+
|
|
96
|
+
current = now or datetime.datetime.now(tz=expiration.tzinfo)
|
|
97
|
+
kst = convert_to_kst(expiration)
|
|
98
|
+
if current >= expiration:
|
|
99
|
+
# 타임스탬프 없이 강조 / plain print for visibility
|
|
100
|
+
print(f"[{section}] 토큰 만료됨 (만료: {kst}) - 새 토큰 발급 필요")
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
print(f"[{section}] 토큰 유효함 (만료 예정일: {kst})")
|
|
104
|
+
return True
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def token_info(
|
|
108
|
+
profile_name: str,
|
|
109
|
+
config_path: Path = AWS_CONFIG_PATH,
|
|
110
|
+
now: Optional[datetime.datetime] = None,
|
|
111
|
+
) -> tuple[str, Optional[datetime.datetime]]:
|
|
112
|
+
"""sts-<name> 토큰 상태 조회 / Inspect stored STS token.
|
|
113
|
+
|
|
114
|
+
Returns ``(state, expiration)`` where state is one of
|
|
115
|
+
``"none"``, ``"invalid"``, ``"expired"``, ``"valid"``.
|
|
116
|
+
"""
|
|
117
|
+
section = f"profile sts-{profile_name}"
|
|
118
|
+
cfg = _read_ini(config_path)
|
|
119
|
+
if not cfg.has_section(section) or not cfg.has_option(section, "expiration"):
|
|
120
|
+
return ("none", None)
|
|
121
|
+
try:
|
|
122
|
+
expiration = datetime.datetime.fromisoformat(cfg.get(section, "expiration"))
|
|
123
|
+
except ValueError:
|
|
124
|
+
return ("invalid", None)
|
|
125
|
+
current = now or datetime.datetime.now(tz=expiration.tzinfo)
|
|
126
|
+
return ("valid" if current < expiration else "expired", expiration)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def write_sts_sections(
|
|
130
|
+
sections: Iterable[tuple[str, dict]],
|
|
131
|
+
config_path: Path = AWS_CONFIG_PATH,
|
|
132
|
+
add_cli_pager: bool = True,
|
|
133
|
+
) -> None:
|
|
134
|
+
"""STS 세션을 ~/.aws/config 에 병합 저장 / Merge STS sections into ~/.aws/config.
|
|
135
|
+
|
|
136
|
+
``sections`` yields ``(section_name, key_value_dict)``. The section
|
|
137
|
+
name is written verbatim (e.g. ``"profile sts-dev"``). When
|
|
138
|
+
``add_cli_pager`` is True, ``cli_pager = `` is appended to each
|
|
139
|
+
written section (disables the AWS CLI pager).
|
|
140
|
+
"""
|
|
141
|
+
existing = _read_ini(config_path)
|
|
142
|
+
|
|
143
|
+
for section_name, kv in sections:
|
|
144
|
+
if not existing.has_section(section_name):
|
|
145
|
+
existing.add_section(section_name)
|
|
146
|
+
for key, value in kv.items():
|
|
147
|
+
existing.set(section_name, key, value)
|
|
148
|
+
if add_cli_pager:
|
|
149
|
+
existing.set(section_name, "cli_pager", "")
|
|
150
|
+
|
|
151
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
152
|
+
with config_path.open("w", encoding="utf-8") as f:
|
|
153
|
+
existing.write(f)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def list_sts_sections(config_path: Path = AWS_CONFIG_PATH) -> List[str]:
|
|
157
|
+
"""~/.aws/config 의 'profile sts-*' 섹션 이름 목록 / List sts-* sections."""
|
|
158
|
+
parser = _read_ini(config_path)
|
|
159
|
+
return [s for s in parser.sections() if s.startswith("profile sts-")]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def remove_sts_sections(
|
|
163
|
+
section_names: Iterable[str],
|
|
164
|
+
config_path: Path = AWS_CONFIG_PATH,
|
|
165
|
+
) -> int:
|
|
166
|
+
"""지정된 섹션들을 ~/.aws/config 에서 제거 / Remove sections; return count."""
|
|
167
|
+
if not config_path.exists():
|
|
168
|
+
return 0
|
|
169
|
+
parser = _read_ini(config_path)
|
|
170
|
+
removed = 0
|
|
171
|
+
for name in section_names:
|
|
172
|
+
if parser.has_section(name):
|
|
173
|
+
parser.remove_section(name)
|
|
174
|
+
removed += 1
|
|
175
|
+
if removed:
|
|
176
|
+
with config_path.open("w", encoding="utf-8") as f:
|
|
177
|
+
parser.write(f)
|
|
178
|
+
return removed
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def upsert_base_profile(
|
|
182
|
+
profile_name: str,
|
|
183
|
+
access_key_id: str,
|
|
184
|
+
secret_access_key: str,
|
|
185
|
+
region: str = "ap-northeast-2",
|
|
186
|
+
output: str = "json",
|
|
187
|
+
config_path: Path = AWS_CONFIG_PATH,
|
|
188
|
+
credentials_path: Path = AWS_CREDENTIALS_PATH,
|
|
189
|
+
) -> None:
|
|
190
|
+
"""base AWS 프로필 신규/갱신 / Create or update a base AWS profile.
|
|
191
|
+
|
|
192
|
+
~/.aws/credentials 에는 access_key / secret_key,
|
|
193
|
+
~/.aws/config 에는 region / output 을 저장합니다.
|
|
194
|
+
Writes access keys to ~/.aws/credentials and region/output to ~/.aws/config.
|
|
195
|
+
Both files are chmod'd to 0600.
|
|
196
|
+
"""
|
|
197
|
+
creds = _read_ini(credentials_path)
|
|
198
|
+
if not creds.has_section(profile_name):
|
|
199
|
+
creds.add_section(profile_name)
|
|
200
|
+
creds.set(profile_name, "aws_access_key_id", access_key_id)
|
|
201
|
+
creds.set(profile_name, "aws_secret_access_key", secret_access_key)
|
|
202
|
+
|
|
203
|
+
credentials_path.parent.mkdir(parents=True, exist_ok=True)
|
|
204
|
+
with credentials_path.open("w", encoding="utf-8") as f:
|
|
205
|
+
creds.write(f)
|
|
206
|
+
try:
|
|
207
|
+
credentials_path.chmod(0o600)
|
|
208
|
+
except OSError as e:
|
|
209
|
+
log.warn(f"chmod 0600 failed for {credentials_path}: {e}")
|
|
210
|
+
|
|
211
|
+
cfg = _read_ini(config_path)
|
|
212
|
+
section = "default" if profile_name == "default" else f"profile {profile_name}"
|
|
213
|
+
if not cfg.has_section(section):
|
|
214
|
+
cfg.add_section(section)
|
|
215
|
+
cfg.set(section, "region", region)
|
|
216
|
+
cfg.set(section, "output", output)
|
|
217
|
+
|
|
218
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
219
|
+
with config_path.open("w", encoding="utf-8") as f:
|
|
220
|
+
cfg.write(f)
|
|
221
|
+
try:
|
|
222
|
+
config_path.chmod(0o600)
|
|
223
|
+
except OSError as e:
|
|
224
|
+
log.warn(f"chmod 0600 failed for {config_path}: {e}")
|