๊ฐ์, ์ธ๊ฐ๋ ๋ฌด์์ธ๊ฐ?
๋ค์ด๊ฐ๊ธฐ ์ ์ ์ธ๊ฐ(Authorization)์ด ๋ฌด์์ธ์ง ํ๋ฒ ๋ ๊ฐ๋
์ ์ ๋ฆฌํด๋ณด์.
์ธ๊ฐ๋ "์ด ์ฌ์ฉ์๊ฐ ํน์ ๋ฆฌ์์ค๋ ๊ธฐ๋ฅ์ ์ ๊ทผํ ๊ถํ์ด ์๋์ง ํ์ธํ๋ ๊ณผ์ "์ด๋ค.
์๋ฅผ๋ค์๋ฉด ์ด๋ค ์ฌ์ฉ์๊ฐ ID/PW๋ฅผ ์
๋ ฅํ๊ณ ์ฌ์ง์ฒฉ ์ดํ๋ฆฌ์ผ์ด์
์ ๋ก๊ทธ์ธํ๋ค๋ฉด,
์ฌ์ฉ์ ๋ณธ์ธ์ ์ฌ์ง์ฒฉ์๋ง ์ ๊ทผ ๊ฐ๋ฅํด์ผํ๋ฉฐ ๋ค๋ฅธ ์ฌ์ฉ์์ ์ฌ์ง์ฒฉ์๋ ์ ๊ทผํด์๋ ์๋๋ค.
๊ตฌ๋ถ | ๊ฐ๋ | ์์ |
์ธ์ฆ Authentication |
๋๊ตฌ์ธ์ง ํ์ธํ๋ ๊ณผ์ | ๋ก๊ทธ์ธ |
์ธ๊ฐ Authorization |
๋ฌด์์ ํ ์ ์๋์ง ํ์ธํ๋ ๊ณผ์ | ๋ด ์ฌ์ง์ฒฉ์๋ง ์ ๊ทผ |
์ฆ, ์ธ๊ฐ๋ ์ฌ์ฉ์๊ฐ ํ์ฉ๋ ๋ฆฌ์์ค์๋ง ์ ๊ทผํ๋๋ก ํ๋๊ฒ์ด ์ธ๊ฐ์ ๊ฐ๋ ์ด๋ค.
์ธ๊ฐ๋ฅผ ์ํด ๊ณ ๋ คํด์ผํ๋ ์์๋ ์ ๋ง ๋ง๋ค.
- ์ฌ์ฉ์ ์ปจํ ์คํธ (Who): ์ฌ์ฉ์ ์ ์, ์ญํ , ๊ทธ๋ฃน, ์์ฑ, ์กฐ์ง ๊ตฌ์กฐ ๋ด ์์น
- ๋ฆฌ์์ค ์ปจํ ์คํธ (What): ๋ฆฌ์์ค ์ ํ, ์์ ๊ถ, ๋ฏผ๊ฐ๋ ์์ค, ๊ณ์ธต ๊ตฌ์กฐ, ๋ฉํ๋ฐ์ดํฐ
- ์ ๊ทผ ์กฐ๊ฑด (When/How): ์๊ฐ/์์น ๊ธฐ๋ฐ ์ ์ฝ, ๋๋ฐ์ด์ค/๋คํธ์ํฌ ์กฐ๊ฑด, ๋ค์ค ์ธ์ฆ, ์ ๊ทผ ๋น๋
- ์ด์ ๊ด์ (Operation): ๊ถํ ์์/์์, ๊ธด๊ธ ์ ๊ทผ, ์์ ๊ถํ, ๊ถํ ์ทจ์
- ๊ฐ์ฌ ๋ฐ ๋ชจ๋ํฐ๋ง (Audit): ์ ๊ทผ ๋ก๊ทธ, ๊ถํ ๋ณ๊ฒฝ ์ด๋ ฅ, ๋น์ ์ ํ์ง, ์ปดํ๋ผ์ด์ธ์ค, ์ฌ์ฉ ํจํด
- ์ ์ฑ ๊ด๋ฆฌ (Policy): ๋ฒ์ ๊ด๋ฆฌ, ์ถฉ๋ ํด๊ฒฐ, ํ ์คํธ, ๋ฐฐํฌ ์ ๋ต, ์์ธ ์ฒ๋ฆฌ
- ๋ณด์ ๊ณ ๋ ค์ฌํญ: ์ต์ ๊ถํ ์์น, ์ง๋ฌด ๋ถ๋ฆฌ, ๊ถํ ์์ค์ปฌ๋ ์ด์ ๋ฐฉ์ง, ์ธ์ /ํ ํฐ ๊ด๋ฆฌ
- ํ์ฅ์ฑ ๋ฐ ์ฑ๋ฅ: ์บ์ฑ, ๋ถ์ฐ ์์คํ , ์ ์ฑ ์ฒ๋ฆฌ, ์๋ต ์๊ฐ, ์ฅ์ ๋ณต๊ตฌ
- ํตํฉ ๊ณ ๋ ค์ฌํญ: ๋ ๊ฑฐ์ ํตํฉ, SSO, ์ธ๋ถ ์์คํ ์ฐ๋, API ๋ณด์, ํ๋กํ ์ฝ
- ๊ท์ ์ค์: ์ฐ์ ํ์ค, ๋ฐ์ดํฐ ๋ณดํธ, ๊ฐ์ฌ ์๊ตฌ์ฌํญ, ๋ฒ๊ท, ๊ฐ์ธ์ ๋ณด ๋ณดํธ
๊ทธ๋ ๋ค๋ฉด, Keycloak์์๋ ์ธ๊ฐ๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ๊ฒ์ธ๊ฐ?
๊ณต์๋ฌธ์ Authorization Services Guide ์์๋ ์ฃผ์ ์ ๊ทผ์ ์ด๋ชจ๋ธ๊ณผ ์ ์ฑ
์งํ ํฌ์ธํธ์ ๋ํด ์ ๊ณตํ๊ณ ์๊ณ ,
์ด์ ์ค์ต์์๋ ์์ฃผ ์ ๊น ๋ค๋ฃฌ์ ์ด ์๋ค. (๐์ค์ต)
์ ๊ทผ์ ์ด๋ชจ๋ธ | ์ค๋ช |
Attribute-based access control (ABAC) |
์ฌ์ฉ์, ๋ฆฌ์์ค, ํ๊ฒฝ์ ์์ฑ์ ๊ธฐ๋ฐ์ผ๋ก ์ ๊ทผ์ ์ ์ด ์: ์ฌ์ฉ์์ ๋ถ์, ์ง๊ธ, ์์น ๋ฑ์ ์์ฑ์ ๊ธฐ๋ฐ์ผ๋ก ์ ๊ทผ ๊ถํ ํ์ฉ |
Role-based access control (RBAC) |
์ฌ์ฉ์์๊ฒ ํ ๋น๋ ์ญํ ์ ๊ธฐ๋ฐ์ผ๋ก ์ ๊ทผ์ ์ ์ด ์: Realm Role์ ๋งคํ๋ ์ฌ์ฉ์๋ง ์ ๊ทผ ๊ถํ ํ์ฉ |
User-based access control (UBAC) |
ํน์ ์ฌ์ฉ์๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ ๊ทผ์ ์ ์ด (๊ฐ๋ณ ์ฌ์ฉ์ ๋จ์์ ์ธ๋ฐํ ๊ถํ ์ ์ด ๊ฐ๋ฅ) ์, user01์๊ฒ๋ง ์ ๊ทผ ๊ถํ ํ์ฉ |
Context-based access control (CBAC) |
์์ฒญ์ ์ปจํ
์คํธ(์๊ฐ, ์์น, ๋๋ฐ์ด์ค ๋ฑ)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ ๊ทผ์ ์ ์ด ์: ํน์ IP ๋ฒ์์์๋ง ์ ๊ทผ ํ์ฉ |
Rule-based access control: | JavaScript๋ฅผ ์ฌ์ฉํ ์ปค์คํ ๊ท์น ์ ์ (๋ณต์กํ ๋น์ฆ๋์ค ๋ก์ง์ ๊ตฌํํ ์ ์์) |
Time-based access control: | ์๊ฐ ๊ธฐ๋ฐ์ ์ ๊ทผ ์ ์ด (ํน์ ์๊ฐ๋๋ ๊ธฐ๊ฐ ๋์๋ง ์ ๊ทผ ํ์ฉ) |
Custom ACMs through SPI: | Service Provider Interface๋ฅผ ํตํ ์ปค์คํ
์ ๊ทผ ์ ์ด ๋ฉ์ปค๋์ฆ ๊ตฌํ ๊ฐ๋ฅ (ํ์ํ ๊ฒฝ์ฐ ์์ฒด ์ ์ฑ ํ์ ์ ๊ฐ๋ฐํ์ฌ ํ์ฅ ๊ฐ๋ฅ) |
์ ์ฑ ์งํ ํฌ์ธํธ | ์ค๋ช |
Policy Information Point (PIP/์ ์ฑ ์ ๋ณด) |
์ ์ฑ
ํ๊ฐ์ ํ์ํ ์ ๋ณด ์ ๊ณต ์ฌ์ฉ์ ์์ฑ, ํ๊ฒฝ ์ ๋ณด ๋ฑ ์์ง |
Policy Administration Point (PAP/์ ์ฑ ๊ด๋ฆฌ) |
์ ์ฑ
์ ์์ฑ, ๊ด๋ฆฌ, ์ ์ฅํ๋ ๊ณณ Keycloak ๊ด๋ฆฌ ์ฝ์์์ ์ ์ฑ ์ค์ |
Policy Decision Point (PDP/์ ์ฑ ๊ฒฐ์ ) |
์ ๊ทผ ํ์ฉ/๊ฑฐ๋ถ ๊ฒฐ์ ์ ์ฑ ๊ณผ ์์ฑ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋จ |
Policy Enforcement Point (PEP/์ ์ฑ ์งํ) |
์ค์ ์ ๊ทผ ์ ์ด ์คํ PDP์ ๊ฒฐ์ ์ ๊ฐ์ |
1. PAP (์ ์ฑ
๊ด๋ฆฌ) ์๋ํฌ์ธํธ
# ๊ธฐ๋ณธ URL
${keycloak_url}/admin/realms/${realm_name}/clients/${client_id}/authz/resource-server
# ์ ์ฑ
๊ด๋ฆฌ
GET /policies # ์ ์ฑ
๋ชฉ๋ก ์กฐํ
POST /policies # ์ ์ฑ
์์ฑ
GET /policies/{id} # ํน์ ์ ์ฑ
์กฐํ
PUT /policies/{id} # ์ ์ฑ
์
๋ฐ์ดํธ
DELETE /policies/{id} # ์ ์ฑ
์ญ์
2. PIP (์ ๋ณด ์์ง) ์๋ํฌ์ธํธ
# ์ฌ์ฉ์ ์ ๋ณด
GET /admin/realms/${realm_name}/users/{id}
GET /admin/realms/${realm_name}/users/{id}/groups
GET /admin/realms/${realm_name}/users/{id}/role-mappings
# ๋ฆฌ์์ค ์ ๋ณด
GET /authz/protection/resource_set
GET /authz/protection/resource_set/{id}
3. PDP (๊ฒฐ์ ) ์๋ํฌ์ธํธ
# ๊ถํ ํ๊ฐ
POST /realms/${realm_name}/protocol/openid-connect/token
{
"grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
"audience": "${client_id}",
"permission": "${resource}#${scope}"
}
# ์ ์ฑ
ํ๊ฐ
POST /realms/${realm_name}/clients/${client_id}/authz/resource-server/policy/evaluate
4. PEP (์คํ) ์๋ํฌ์ธํธ
# ํ ํฐ ๊ฒ์ฆ
GET /realms/${realm_name}/protocol/openid-connect/userinfo
GET /realms/${realm_name}/protocol/openid-connect/token/introspect
# ๊ถํ ํ์ธ
POST /realms/${realm_name}/clients/${client_id}/authz/resource-server/permission/ticket
์ค๋์ KeyCloak์์์ ์ ๊ทผ์ ์ด ๋ฉ์ปค๋์ฆ ์ค RBAC, ABAC์ ๋ํด ํ์ธํด๋ณด์.
RBAC (Role-Based Access Control)
๊ฐ๋ ์ดํดํ๊ธฐ
Keycloak์์๋ Role์ ์ฌ์ฉ์ ํน์ ๊ทธ๋ฃน์ ๋งคํ๋์ด ์ฌ์ฉ๋๋ค. (๊ณต์๋ฌธ์ ๋งํฌ)
Role์ด๋, ์ผ๋ฐ์ ์ผ๋ก ์กฐ์ง ๋๋ ์ ํ๋ฆฌ์ผ์ด์
์ปจํ
์คํธ์์ ์ฌ์ฉ์๊ฐ ๊ฐ์ง๋ ์ญํ ์ ๋งํ๋ค.
(e.g. administrator, system-manager, people-manager, audit-user, readonly-user ..)
Keycloak์ RBAC์ ์ดํดํ๊ธฐ ์ํด์๋ user, group์ ๊ฐ๋ ๋ ํจ๊ป ์ดํดํด์ผ ํ๋ค.
๐ User
- Keycloak์์ ์ธ์ฆ๊ณผ ์ธ๊ฐ์ ์ฃผ์ฒด์ด๋ค.
- ์ง์ ์ ์ผ๋ก Role์ ํ ๋น๋ฐ์ ์ ์์ผ๋ฉฐ(UBAC), Group์ ๋ฉค๋ฒ๊ฐ ๋ ์ ์๋ค.
- e.g. ์ฌ์ฉ์1(user01@test.com), ์ฌ์ฉ์2(user02@test.com)
๐ Group
- ์ฌ๋ฌ User๋ฅผ ๋ฌถ์ด์ ๊ด๋ฆฌํ๋ ๋จ์์ด๋ฉฐ, Role์ ํ ๋น๋ฐ์ ์ ์๋ค. (GBAC)
- ๊ณ์ธต์ ๊ตฌ์กฐ๊ฐ ๊ฐ๋ฅ(ํ์ ๊ทธ๋ฃน ์์ฑ ๊ฐ๋ฅ)ํ์ฌ ์ฌ๋ฌ User๋ฅผ ํ๋ฒ์ ๊ด๋ฆฌ ๊ฐ๋ฅํ๋ค.
- Role ๊ด๋ฆฌ ํจ์จ์ฑ ์ฆ๊ฐ๋๋ค.
๐ Role
keycloak์๋ ์ธ ์ข ๋ฅ์ ์ญํ ์ด ์๋ค.
๊ตฌ๋ถ | ์ค๋ช |
Realm Role [๋งํฌ] |
- ์ ์ญ์ (Global) ๋ฒ์์ ์ญํ - Realm ๋ด์ ๋ชจ๋ ํด๋ผ์ด์ธํธ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๊ณตํต์ผ๋ก ์ฌ์ฉ - ์ผ๋ฐ์ ์ผ๋ก ์กฐ์ง ์ ์ฒด์ ์ ์ฉ๋๋ ๊ด๋ฒ์ํ ๊ถํ์ ์ ์ โ ๏ธ์ฃผ์โ ๏ธ ์ ์ญ์ ์ผ๋ก ์ฌ์ฉ ๊ฐ๋ฅํ์ง๋ง, ์๋์ผ๋ก ๋ชจ๋ ์ฌ์ฉ์์๊ฒ ํ ๋น๋์ง๋ ์์ |
Client Role [๋งํฌ] |
- ํน์ ํด๋ผ์ด์ธํธ ์ ํ๋ฆฌ์ผ์ด์
์ ํ์ ๋ ์ญํ - ํด๋น ์ ํ๋ฆฌ์ผ์ด์ ์ ํนํ๋ ์ธ๋ถ์ ์ธ ๊ถํ์ ์ ์ - ๋ค๋ฅธ ํด๋ผ์ด์ธํธ์ ๋ ๋ฆฝ์ ์ผ๋ก ๊ด๋ฆฌ |
Default Role [๋งํฌ] |
- ์๋ก์ด ์ฌ์ฉ์๊ฐ ์์ฑ๋ ๋ ์๋์ผ๋ก ํ ๋น๋๋ ์ญํ - `default-roles-{realm-name}` Composite Role์ ํตํด ๊ด๋ฆฌ - Realm Role์ด๋ Client Role์ Default Role๋ก ์ค์ ๊ฐ๋ฅ |
Role๋ค์ ์ํธ ๋ฒ ํ์ ์ธ ๊ฒ์ด ์๋๋ฉฐ, ์กฐํฉํ์ฌ ๊ณ์ธต์ ์ด๊ณ ์ธ๋ฐํ ์ ๊ทผ ์ ์ด๊ฐ ๊ฐ๋ฅํด์ง๋ค.
์๋ ์๋๋ฆฌ์ค์์ ๊ฐ Role๋ค์ ํจ๊ป ์ฌ์ฉํ๋ ์์๋ฅผ ํ์ธํด๋ณด์.
[Realm Role & Client Role ํผ์ฉ ์๋๋ฆฌ์ค]
Realm Role (USER)
- ์์คํ
์ ๋ฐ์ ๊ธฐ๋ณธ ์ ๊ทผ ๊ถํ
- ๋ชจ๋ ํด๋ผ์ด์ธํธ ์ฑ์์ ์ ํจ
- "์ด ์ฌ์ฉ์๊ฐ ์จ๋ฒ ์๋น์ค๋ฅผ ์ฌ์ฉํ ์๊ฒฉ์ด ์๋๊ฐ?"
Client Role ({์ฌ์ฉ์ID}:all_access)
- ์ฌ์ฉ์ ๋ณธ์ธ์ ์จ๋ฒ์ ๋ํ ๋ชจ๋ ๊ถํ
- ์กฐํ/์์ /์ญ์ ๋ฑ ๋ชจ๋ ๊ธฐ๋ฅ ์ฌ์ฉ ๊ฐ๋ฅ
- "์ด ์ฌ์ฉ์๊ฐ ๋ณธ์ธ์ ์จ๋ฒ์ ๋ํ ๋ชจ๋ ๊ถํ์ ๊ฐ์ง๋๊ฐ?"
์ด๋ฐ์์ผ๋ก ์ฌ์ฉ์๊ฐ ์ฌ์ง์ฑ์ ์ฌ์ฉํ ์๋ ์์ง๋ง, ๋ชจ๋ ์ฌ์ง์ฒฉ์ด ์๋ ๋ณธ์ธ์ ์ฌ์ง์ฒฉ๋ง ์ ๊ทผํ์ฌ ์์ ํ ์ ์๋๋ก
๋ ๊ถํ์ ์กฐํฉํ์ฌ ๊ณ์ธต์ ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค.
๐ ์ ๋ฆฌ
- Role์ ๋จ๋ ์ผ๋ก ์ฌ์ฉํ ์ ์๊ณ , ๋ฐ๋์ Userํน์ Group์ ๋งคํ๋์ด์ผ ํ๋ค.
- Keycloak์์๋ RBAC, UBAC, GBAC ๋ณ๊ฐ์ ๋ค๋ฅธ ๊ฐ๋ ์ด ์๋๋ผ ๊ฐ์ ์ ๊ทผ์ ์ด ๋ฉ์ปค๋์ฆ์ ๊ฐ์ง๊ณ ์๋ค๊ณ ํ ์ ์๋ค.
- ๋ชจ๋ ์ข ๋ฅ์ Role์ Group์ด๋ User์๊ฒ ์ง์ ํ ๋น ๊ฐ๋ฅํ๋ค.
- Group์ ํ ๋น๋ Role์ ํด๋น Group์ ๋ชจ๋ ๋ฉค๋ฒ๊ฐ ์์๋ฐ๋๋ค.
- ํ ์ฌ์ฉ์๋ ์ฌ๋ฌ ๊ฒฝ๋ก(Default, Group, ์ง์ ํ ๋น)๋ก Role์ ๊ฐ์ง ์ ์๋ค.
์ค์ต) Role ์ฌ์ฉ ์ค์ตํ๊ธฐ
RealmRole, User์ ํ ๋น
Realm Role์ ์๋์ ๊ฐ์ด ์ค์ ํ๋ฉด ๋๋ฉฐ,
Client Role์ [Clients > ์์ฑํ Client > Roles ํญ > Create role] ์์ ์์ฑํ ์ ์๋ค.
์ด ์ค์ต์์๋ Realm Role๋ง ๊ฐ๋ตํ ์ค๋ช
ํ๋ค.
1. Realm Role์ ์์ฑํ๋ค.
2. ์ฌ์ฉ์์๊ฒ Realm Role์ ํ ๋นํ๋ค.
3. ์ฌ์ฉ์์ ์ฐ๊ฒฐ๋ Realm Role ํ์ธ
๋จผ์ Users์ Realm Role์ด ์ ์ฐ๊ฒฐ๋์๋์ง ์ ๊ฒํ๋ค.
ํฐ๋ฏธ๋์์ ํธ์ถํ ์์ ์ด๋ฏ๋ก, Direct access grants๋ฅผ ํ์ฉํด์ค๋ค.
์ดํ ์๋ ํ๊ฒฝ๋ณ์ ๋ถ๋ถ์ ๋ด ํ๊ฒฝ์ ๋ง์ถฐ ๋ณ๊ฒฝํ๊ณ , ํฐ๋ฏธ๋์์ ์คํํด๋ณด์.
# 1. keycloak ํ
์คํธ์๋ฒ ์คํ
docker run -p 8080:8080 \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak \
start-dev
# 2. ํ๊ฒฝ๋ณ์ ์ค์
export ADMIN_USERNAME="admin"
export ADMIN_PASSWORD="admin"
export CLIENT_ID="admin-cli"
export REALM_NAME="my-realm"
export KEYCLOAK_URL="http://localhost:8080"
export KEYCLOAK_USERNAME=user01
# 3. ๊ด๋ฆฌ์ ํ ํฐ ํ๋
export ADMIN_TOKEN=$(curl -X POST "${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=${CLIENT_ID}" \
-d "username=${ADMIN_USERNAME}" \
-d "password=${ADMIN_PASSWORD}" \
-d "grant_type=password" | jq -r '.access_token')
# 4. ์ฌ์ฉ์ ID ์กฐํ
export USER_ID=$(curl -X GET "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users?username=${KEYCLOAK_USERNAME}" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" | jq -r '.[0].id')
# 5. Role ๋งคํ ํ์ธ (๊ด๋ฆฌ์ ํ ํฐ ์ฌ์ฉ)
curl -X GET "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users/${USER_ID}/role-mappings/realm" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" | jq '.' --color-output
๊ฒฐ๊ณผ๋ก ์๋์ ๊ฐ์ด ์๋ก ์์ฑํ my-realm-role์ด ๋งคํ๋๊ฒ์ ํ์ธํ ์ ์๋ค.
ClientRole, Group์ ํ ๋น
1. Client Role ์์ฑ
[client > 'my-client' > Roles > create role] ์ผ๋ก ์๋ก์ด Role (my-client-role)์ ์์ฑํ๋ค.
2. ๊ทธ๋ฃน์ ClientRole์ ๋งคํํ๋ค.
3. ๊ทธ๋ฃน์ ClientRole ๋งคํ๋๊ฒ ํ์ธ
# 1. ํ๊ฒฝ๋ณ์ ์ค์
export ADMIN_USERNAME="admin"
export ADMIN_PASSWORD="admin"
export CLIENT_ID="my-client"
export REALM_NAME="my-realm"
export KEYCLOAK_URL="http://localhost:8080"
export KEYCLOAK_USERNAME="user01"
export KEYCLOAK_GROUPNAME="my-group"
# 2. ๊ด๋ฆฌ์ ํ ํฐ ํ๋
export ADMIN_TOKEN=$(curl -X POST "${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=admin-cli" \
-d "username=${ADMIN_USERNAME}" \
-d "password=${ADMIN_PASSWORD}" \
-d "grant_type=password" | jq -r '.access_token')
# 3. ์ฌ์ฉ์ ID ์กฐํ
export USER_ID=$(curl -X GET "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users?username=${KEYCLOAK_USERNAME}" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" | jq -r '.[0].id')
# 4. Client ID(UUID) ์กฐํ
export MY_CLIENT_ID=$(curl -X GET "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients?clientId=${CLIENT_ID}" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" | jq -r '.[0].id')
# 5. ์ฌ์ฉ์์ Client Role ๋งคํ ํ์ธ (์ง์ ํ ๋น๋ role)
echo "Direct Client Roles:"
curl -X GET "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users/${USER_ID}/role-mappings/clients/${MY_CLIENT_ID}" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" | jq '.' --color-output
# 6. ์ฌ์ฉ์์ Effective Client Role ๋งคํ ํ์ธ (๊ทธ๋ฃน์์ ์์๋ฐ์ role ํฌํจ)
echo -e "\nEffective Client Roles (including inherited from groups):"
curl -X GET "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users/${USER_ID}/role-mappings/clients/${MY_CLIENT_ID}/composite" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" | jq '.' --color-output
# 7. ๊ทธ๋ฃน ID ์กฐํ ๋ฐ Client Role ๋งคํ ํ์ธ
export MY_GROUP_ID=$(curl -X GET "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/groups?search=${KEYCLOAK_GROUPNAME}" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" | jq -r '.[0].id')
echo -e "\nGroup Client Roles:"
curl -X GET "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/groups/${MY_GROUP_ID}/role-mappings/clients/${MY_CLIENT_ID}" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" | jq '.' --color-output
๊ทธ๋ฃน์ ์ฐ๊ฒฐ๋ ClientRole, ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์์๊ฒ ์์๋ ClientRole๋ ์ ๋ช ๋ น์ด๋ฅผ ํตํด ํ์ธํ ์ ์๋ค.
ABAC (Attribute-Based Access Control)
๊ฐ๋ ์ดํดํ๊ธฐ
Keycloak์์ ABAC(Attribute-Based Access Control)์ ์ฌ์ฉ์, ๋ฆฌ์์ค, ํ๊ฒฝ์ ์์ฑ(Attribute)์ ๊ธฐ๋ฐ์ผ๋ก ์ ๊ทผ์ ์ ์ดํ๋ ๋ฐฉ์์ด๋ฉฐ, Role๊ธฐ๋ฐ์ ์ ๊ทผ์ ์ด๋ณด๋ค ๋ ์ ์ฐํ๊ณ ์ธ๋ฐํ ์ ๊ทผ์ ์ด๊ฐ ๊ฐ๋ฅํ๋ค.
Attribute๋ ๋ง์ฐฌ๊ฐ์ง๋ก ๊ธฐ๋ณธ์ ์ผ๋ก๋ User์ Group์ ํ ๋นํ์ฌ ์ฌ์ฉํ๋ฉฐ,
๊ทธ ์ธ์๋ Organization, Client, Identity Provider ๋ฑ ๋ค์ํ Keycloak ์ปดํฌ๋ํธ์ Attribute๋ฅผ ์ถ๊ฐํ ์ ์๋ค.
(core concepts and terms)
ABAC์ ํ์ธํ๊ธฐ ์ ์ ๋จผ์ ์ฃผ์ ๊ฐ๋ ๋ถํฐ ํ์ธํด๋ณด์.
๐ Attribute
- Keycloak์์ ์ํฐํฐ(User, Group ๋ฑ)์ ์ถ๊ฐ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ Key-Value ํํ์ ๋ฐ์ดํฐ๋ฅผ ๋งํ๋ค.
- ๋์ ์ผ๋ก ์ถ๊ฐ/์์ ์ด ๊ฐ๋ฅํ๋ฉฐ, ํ๋์ Key์ ์ฌ๋ฌ Value๋ฅผ ๊ฐ์ง ์ ์์ผ๋ฉฐ,
ABAC(Attribute Based Access Control)์์ ์ ๊ทผ ์ ์ด ๊ฒฐ์ ์ ์ฌ์ฉ๋๋ค. - Group์์ User๋ก ์์ ๊ฐ๋ฅ ํ๋ค. (Group → User)
- e.g. department: "HR", location: ["Seoul", "Busan"]
๐ Protocol Mapper
- Attribute๋ฅผ Token Claim์ผ๋ก ๋ณํํ๋ ์ค์ ์ด๋ค.
- Keycloak์ JavaDoc ์ธํฐํ์ด์ค ๊ตฌํ์ฒด ๋งํฌ
- Mapper ์ข
๋ฅ:
- User Property Mapper: ๊ธฐ๋ณธ ์ฌ์ฉ์ ์์ฑ ๋งคํ
- User Attribute Mapper: ์ปค์คํ ์ฌ์ฉ์ ์์ฑ ๋งคํ
- Group Membership Mapper: ๊ทธ๋ฃน ์ ๋ณด ๋งคํ
- Role Mapper: ์ญํ ์ ๋ณด ๋งคํ
{
"name": "department", // Mapper์ ์ด๋ฆ (๊ด๋ฆฌ์๊ฐ ๊ตฌ๋ถํ๊ธฐ ์ํ ์ฉ๋)
"protocol": "openid-connect", // ์ฌ์ฉํ ํ๋กํ ์ฝ (๋ณดํต OpenID Connect ์ฌ์ฉ)
"protocolMapper": "oidc-usermodel-attribute-mapper",// Mapper์ ํ์
(User Attribute๋ฅผ ๋งคํ)
"config": { // ์ค์ ๋งคํ ์ค์
"user.attribute": "department", // Keycloak์ ์ ์ฅ๋ ์์ฑ ์ด๋ฆ
"claim.name": "department", // ํ ํฐ์ ํฌํจ๋ ๋ ์ฌ์ฉํ ์ด๋ฆ
"token.claim": "true" // ํ ํฐ์ ํฌํจํ ์ง ์ฌ๋ถ
}
}
๐ Token Claim
- ํ ํฐ์ ํฌํจ๋๋ ์ ๋ณด์ ํ ์กฐ๊ฐ์ผ๋ก, ์ฌ์ฉ์๋ ํ ํฐ ์์ฒด์ ๋ํ statement๋ฅผ ๋งํ๋ค.
- Protocol Mapper๋ฅผ ํตํด Attribute๋ฅผ Claim์ผ๋ก ๋ณํํ์ฌ ํ ํฐ์ ํฌํจ์ํฌ ์ ์๋ค.
- ํด๋ผ์ด์ธํธ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฉ์ ์ ๋ณด๋ ๊ถํ ํ์ธ์ ์ฌ์ฉํ๋ค.
{
// ๊ธฐ๋ณธ Claims
"sub": "user123",
"iss": "http://keycloak.example.com",
"exp": 1516239022,
// User Attributes -> Claims
"user_attributes": {
"department": "HR",
"location": ["Seoul"]
},
// Group Attributes -> Claims
"group_attributes": {
"costCenter": "HR001"
}
}
๐ Attribute์ Token Claim ๊ด๊ณ
์ฌ์ฉ์๋ ๊ทธ๋ฃน์ ์์ฑ(attributes)์ protocol mapper์ค์ ์ ํตํด Token์ Claims ํํ๋ก ๋ด๊ฒจ ์ ๋ฌ๋๋ค.
์ฆ,Attribute๋ Keycloak ๋ด๋ถ ๋ฐ์ดํฐ์ด๊ณ , Claim์ ์ด ๋ฐ์ดํฐ๋ฅผ ํ ํฐ์ ํตํด ์ธ๋ถ๋ก ์ ๋ฌํ๋ ๋ฐฉ์์ด๋ค.
์ค์ต) Attribute ์ฌ์ฉ๋ฐฉ๋ฒ
User Attribute ์ฌ์ฉํ๊ธฐ
๋จผ์ ์ฌ์ฉ์ ์์ฑ(Attributes)๋ฅผ ์ค์ ํด๋ณด์.
1. [realm settings > User profile > create attribute]๋ฅผ ํด๋ฆญํ๊ณ department ์์ฑ์ ๋ง๋ ๋ค.
2. User Details์ ์๋ก ์๊ธด Department์ ๊ฐ์ ์ ๋ ฅํ๋ค.
3. ์ฌ์ฉ์์ Attribute๊ฐ ์ ๋๋ก ์ค์ ๋์๋์ง ํ์ธํ๋ค.
# 1. ํ๊ฒฝ๋ณ์ ์ค์
export ADMIN_USERNAME="admin"
export ADMIN_PASSWORD="admin"
export CLIENT_ID="my-client"
export REALM_NAME="my-realm"
export KEYCLOAK_URL="http://localhost:8080"
export USER_NAME="user01"
# 2. ๊ด๋ฆฌ์ ํ ํฐ ํ๋
export ADMIN_TOKEN=$(curl -X POST "${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=admin-cli" \
-d "username=${ADMIN_USERNAME}" \
-d "password=${ADMIN_PASSWORD}" \
-d "grant_type=password" | jq -r '.access_token')
# 3. ์ฌ์ฉ์ ID ์กฐํ
export USER_ID=$(curl -X GET "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users?username=${USER_NAME}" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" | jq -r '.[0].id')
echo $USER_ID
# 4. Client ID(UUID) ์กฐํ
export CLIENT_UUID=$(curl -X GET "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/clients?clientId=${CLIENT_ID}" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json" | jq -r '.[0].id')
echo $CLIENT_UUID
# 5. ํ์ฌ ์ฌ์ฉ์ ์ ๋ณด ์กฐํ
export USER_INFO=$(curl -X GET "${KEYCLOAK_URL}/admin/realms/${REALM_NAME}/users/${USER_ID}" \
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
-H "Content-Type: application/json")
echo $USER_INFO | jq
# 6. User Attribute ์กฐํ
echo "Current User Attributes:"
echo ${USER_INFO} | jq '.attributes' --color-output
user attribute์ department๊ฐ ์ถ๊ฐ๋๊ฒ์ ๋ณผ ์ ์๋ค.
4. Protocol Mapper๋ฅผ ์ค์ ํ๋ค.
5. ์ด์ AccessToken์ ๋ฐ๊ธ๋ฐ๊ณ Attribute๊ฐ ํฌํจ๋์๋์ง ํ์ธํ๋ค.
# 1. ํ๊ฒฝ๋ณ์ ์ค์
export USER_NAME="user01"
export USER_PASSWORD="user01"
export CLIENT_ID="my-client"
export REALM_NAME="my-realm"
export KEYCLOAK_URL="http://localhost:8080"
# 1. ์ฌ์ฉ์ ํ ํฐ ๋ฐ๊ธ
export USER_TOKEN=$(curl -X POST "${KEYCLOAK_URL}/realms/${REALM_NAME}/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=${CLIENT_ID}" \
-d "username=${USER_NAME}" \
-d "password=${USER_PASSWORD}" \
-d "grant_type=password")
# 2. Access Token ์ถ์ถ
export ACCESS_TOKEN=$(echo $USER_TOKEN | jq -r '.access_token')
# 3. Access Token ๋์ฝ๋ฉ (JWT)
echo $ACCESS_TOKEN | cut -d"." -f2 | tr -d '-' | tr '_' '/' | sed -e 's/$/\=/' | base64 -d | jq '.' --color-output
protocol mapper๊น์ง ์ค์ ํ๊ณ ๋๋ฉด AccessToken์ Department๊ฐ ์ถ๊ฐ๋์ด ์ ๋ฌ๋๋ค.
RBAC vs ABAC
์ค์ ๋ก๋ ํ๋๋ง ์ฌ์ฉํ๋๊ฒ์ด ์๋๋ผ
๊ธฐ๋ณธ์ ์ธ ๊ถํ์ RBAC์ผ๋ก ๊ด๋ฆฌํ๊ณ , ํน์ํ ์ํฉ์ ABAC์ผ๋ก ๋ณด์ํ๋ ํํ๋ก ๋ ๋ฐฉ์์ ํผํฉํ์ฌ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
๊ตฌ๋ถ | RBAC | ABAC |
์ค๋ช | Role๊ธฐ๋ฐ์ ์ ๊ทผ์ ์ด {
"user": "user01",
"roles": ["admin"],
"groups": ["/hr/manager"],
"permissions": ["read", "write"]
}
|
์์ฑ ๊ธฐ๋ฐ์ ์ ๊ทผ์ ์ด {
"user": {
"department": "HR",
"level": "senior",
"location": "Seoul",
"clearance": "top-secret"
},
"resource": {
"type": "document",
"classification": "confidential",
"department": "HR"
},
"action": "read",
"environment": {
"time": "09:00-18:00",
"ip_range": "10.0.0.0/24"
}
}
|
ํน์ง | ๋ฏธ๋ฆฌ ์ ์๋ ์ญํ /๊ทธ๋ฃน์ ๊ธฐ๋ฐ ๊ด๋ฆฌ๊ฐ ๋จ์ ๋ณ๊ฒฝ์ด ์ ์ ํ๊ฒฝ์ ์ ํฉ ํ์ฅ์ฑ ์ ํ์ |
๋ค์ํ ์์ฑ ๊ธฐ๋ฐ ๊ฒฐ์ ์ ์ฐํ ์ ์ฑ ์ค์ ๊ฐ๋ฅ ์ํฉ์ ๋ฐ๋ฅธ ๋์ ์ ๊ทผ ์ ์ด ๋ณต์กํ ๊ท์น ์ง์ |
์์ | @RolesAllowed("admin") public void adminMethod() { // ๊ด๋ฆฌ์๋ง ์ ๊ทผ ๊ฐ๋ฅ } |
@Attribute("department == 'HR' && time.between('09:00', '18:00')") public void hrDocumentAccess() { // HR๋ถ์ ์ง์์ด ์ ๋ฌด์๊ฐ ๋ด ์ ๊ทผ } |
์๋๋ฆฌ์ค | ์กฐ์ง ๊ตฌ์กฐ๊ฐ ๋ช
ํํ ๊ธฐ์
์์คํ
๊ถํ ์ฒด๊ณ๊ฐ ๋จ์ํ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ฌ์ฉ์ ๊ทธ๋ฃน์ด ๋ช ํํ ์์คํ |
๋ณต์กํ ๋ณด์ ์๊ตฌ์ฌํญ์ด ์๋ ์์คํ
์๊ฐ/์์น ๊ธฐ๋ฐ ์ ๊ทผ ์ ์ด ํ์ ๋ค์ํ ์กฐ๊ฑด์ ๋ฐ๋ฅธ ๋์ ๊ถํ ํ์ |
๐ ์ฃผ์์ฌํญ
Role Explosion (์ญํ ํญ๋ฐ) ๋ฌธ์ ๋ฅผ ์ฃผ์ํด์ผํ๋ค. ์ญํ ์ ์ธ๋ถํ๋ ์ธ๊ฐ๋ฅผ ์ํ ์ญํ ์ฌ์ฉํ๋๊ฒ์ ์ง์ํ๊ณ , ์กฐ์ง ๊ตฌ์กฐ/์ง๋ฌด๋ฅผ ๋ฐ์ํ๋๋ก ํ๋๊ฒ์ด ์ข๋ค. (ADMIN, READ_USER ๋ฑ)
- ์ญํ (Role)์ ํฐ ๋ฒ์ฃผ๋ก ์ ์ํ๋ค. (admin, superuser, readonlyuser ํน์ department ๋ฑ)
- ์ธ๋ถ ์ ๊ทผ์ ์ด๋ ์์ฑ(attributes)๋ฅผ ์ฌ์ฉํ๋ค.
{
"accessPolicy": {
"role": "MANAGER",
"requiredAttributes": {
"department": ["HR", "IT"],
"location": "Seoul",
"timeWindow": "businessHours"
}
}
}
Role Explosion (์ญํ ํญ๋ฐ) ๋ฌธ์ ๋,
์ธ๋ถ์ ์ธ ๋ถ๋ถ๊น์ง ์ ๋ถ Role๋ก ์ ์ํ์ฌ Role์ด ์์ฒญ ๋ง์์ง๋๊ฒ