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.
@@ -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.
@@ -0,0 +1,7 @@
1
+ include README.md
2
+ include LICENSE
3
+ include requirements.txt
4
+ include aws_user_config_sample.json
5
+ recursive-exclude tests *
6
+ global-exclude __pycache__
7
+ global-exclude *.py[cod]
@@ -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,8 @@
1
+ {
2
+ "dev": {
3
+ "account_id": "",
4
+ "profile": "dev",
5
+ "mfa_serial": "",
6
+ "totp_secret": ""
7
+ },
8
+ }
@@ -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,10 @@
1
+ """``python -m awsgetsts`` 엔트리 / Entry point for module invocation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+ from .cli import main
8
+
9
+ if __name__ == "__main__":
10
+ sys.exit(main())
@@ -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}")