[2025_OSC] keystone 이란?

keystone이란?

  • 사용자 인증을 통해 물리 서버 내의 자원을 사용할 수 있도록 관리
  • Keystone에 핵심은 인증(Authorization)권한(Authentication)

Keystone 등장배경

2010년 10월, Austin Release Diablo Release

  • 처음으로 릴리지 한 Austin(2010.10.21) Release은 단순 Compute(컴퓨트 서비스)와 Storage(오브젝트 파일 스토리지 서비스), Image(이미지 관리 서비스)만 존재
  • 그 후 프로젝트를 만들고 Bexar(2011.02.03) Release가 되면서 Compute는 Nova로, Storage는 Swift로, Image는 Glance로 이름을 변경
  • 그리고 그 다음버전인 Diablo(2011.04.15) Release까지 기능 업그레이드

기존에 로그인으로 오픈스택 서비스를 인증하는 방식을 사용

2012년 5월, Essex Release

  • Essex Release하면서 새로운 기능을 추가하게 되었는데 그 기능들은 Keystone과 Horizon
  • Keystone은 Nova, Glance, Swift와 같은 서비스들의 인증을 담당하는 프로젝트이며, Horizon은 서비스들을 좀 더 쉽게 사용하기 위해 사용자들에게 Dashboard를 제공하는 프로젝트

중앙집중식 인증

  • 지금 오픈스택에서 사용중인 인증 같은 flow는 중앙집중식 인증시스템이 아니면 flow가 복잡
  • 그래서 통합하여 관리하는 용도로 나온 것이 Keystone이며, 중앙 집중식 인증과 서비스 카탈로그 등을 사용

Openstack의 Keytone 인증방식

openstack server list 분석

$ openstack server list --debug
...
Making authentication request to http://$KEYSTONE_IP/identity/v3/auth/tokens
Resetting dropped connection: $KEYSTONE_IP
http://$KEYSTONE_IP:80 "POST /identity/v3/auth/tokens HTTP/1.1" 201 2835
{"token": {"methods": ["password"], "user": {"domain": {"id": "default", "name": "Default"}, "id": "60bfb3b044a247efacbc9e23e91c6aca", "name": "demo", "password_expires_at": null}, "audit_ids": ["3EKmLZ3fTN-LMJWYFrS4YA"], "expires_at": "2023-05-08T02:38:51.000000Z", "issued_at": "2023-05-08T01:38:51.000000Z", "project": {"domain": {"id": "default", "name": "Default"}, "id": "82d979aa63704456916d3b4308716a19", "name": "demo"}, "is_domain": false, "roles": [{"id": "c0e793ab50a941928d34cdd27512d9bb", "name": "reader"}, {"id": "f7ed97f399c540ae911074ecce2a61d6", "name": "anotherrole"}, {"id": "d5755a35981f430d85b454d1d0143d67", "name": "member"}], "catalog": [{"endpoints": [{"id": "b06e4484df4d482cbd21c5f6df50b603", "interface": "public", "region_id": "RegionOne", "url": "http://$KEYSTONE_IP/placement", "region": "RegionOne"}], "id": "13bdc335079c420c84629a882f913e7a", "type": "placement", "name": "placement"}, {"endpoints": [{"id": "efd57df796c0422e986647a00d3638d9", "interface": "public", "region_id": "RegionOne", "url": "http://$KEYSTONE_IP/identity", "region": "RegionOne"}], "id": "2d52faffede44b0695655d9f89252d25", "type": "identity", "name": "keystone"}, {"endpoints": [{"id": "40fb127cb9134193892f4c3591d54d14", "interface": "public", "region_id": "RegionOne", "url": "http://$KEYSTONE_IP/volume/v3/82d979aa63704456916d3b4308716a19", "region": "RegionOne"}], "id": "3af8a14f607e41c1920a70aede6bc37b", "type": "block-storage", "name": "cinder"}, {"endpoints": [{"id": "9fa7133739944d05b7e6986423034c36", "interface": "public", "region_id": "RegionOne", "url": "http://$KEYSTONE_IP:9696/networking", "region": "RegionOne"}], "id": "6d705449067f45709ab9ba4a8558947e", "type": "network", "name": "neutron"}, {"endpoints": [{"id": "5662e63f59b74145bf6a495d1a6f9ed5", "interface": "public", "region_id": "RegionOne", "url": "http://$KEYSTONE_IP/compute/v2.1", "region": "RegionOne"}], "id": "72b1b50859674da5af97f24526ea4371", "type": "compute", "name": "nova"}, {"endpoints": [{"id": "f7dd202243584aeaaebd8b98d5c117f6", "interface": "public", "region_id": "RegionOne", "url": "http://$KEYSTONE_IP/compute/v2/82d979aa63704456916d3b4308716a19", "region": "RegionOne"}], "id": "73b477e763cd419898adf2520a18bcee", "type": "compute_legacy", "name": "nova_legacy"}, {"endpoints": [{"id": "133e7c186e1b40fbb81ab0e6f75a31b4", "interface": "public", "region_id": "RegionOne", "url": "http://$KEYSTONE_IP/volume/v3/82d979aa63704456916d3b4308716a19", "region": "RegionOne"}], "id": "949e75df531646a691386be4159a4301", "type": "volumev3", "name": "cinderv3"}, {"endpoints": [{"id": "8986921c20b34c84b6cd66dce7778068", "interface": "public", "region_id": "RegionOne", "url": "http://$KEYSTONE_IP/image", "region": "RegionOne"}], "id": "d3c65b00964546afa4f976395f2d3b3e", "type": "image", "name": "glance"}]}}
...
...
http://$KEYSTONE_IP:80 "GET /image HTTP/1.1" 300 1347
RESP: [300] Connection: close Content-Length: 1347 Content-Type: application/json Date: Mon, 08 May 2023 01:38:52 GMT Server: Apache/2.4.52 (Ubuntu)
RESP BODY: {"versions": [{"id": "v2.16", "status": "CURRENT", "links": [{"rel": "self", "href": "http://$KEYSTONE_IP/image/v2/"}]}, {"id": "v2.15", "status": "SUPPORTED", "links": [{"rel": "self", "href": "http://$KEYSTONE_IP/image/v2/"}]}, {"id": "v2.14", "status": "SUPPORTED", "links": [{"rel": "self", "href": "http://$KEYSTONE_IP/image/v2/"}]}, {"id": "v2.9", "status": "SUPPORTED", "links": [{"rel": "self", "href": "http://$KEYSTONE_IP/image/v2/"}]}, {"id": "v2.7", "status": "SUPPORTED", "links": [{"rel": "self", "href": "http://$KEYSTONE_IP/image/v2/"}]}, {"id": "v2.6", "status": "SUPPORTED", "links": [{"rel": "self", "href": "http://$KEYSTONE_IP/image/v2/"}]}, {"id": "v2.5", "status": "SUPPORTED", "links": [{"rel": "self", "href": "http://$KEYSTONE_IP/image/v2/"}]}, {"id": "v2.4", "status": "SUPPORTED", "links": [{"rel": "self", "href": "http://$KEYSTONE_IP/image/v2/"}]}, {"id": "v2.3", "status": "SUPPORTED", "links": [{"rel": "self", "href": "http://$KEYSTONE_IP/image/v2/"}]}, {"id": "v2.2", "status": "SUPPORTED", "links": [{"rel": "self", "href": "http://$KEYSTONE_IP/image/v2/"}]}, {"id": "v2.1", "status": "SUPPORTED", "links": [{"rel": "self", "href": "http://$KEYSTONE_IP/image/v2/"}]}, {"id": "v2.0", "status": "SUPPORTED", "links": [{"rel": "self", "href": "http://$KEYSTONE_IP/image/v2/"}]}]
...
...
http://$KEYSTONE_IP:80 "GET /compute/v2.1 HTTP/1.1" 200 392
RESP: [200] Connection: close Content-Length: 392 Content-Type: application/json Date: Mon, 08 May 2023 01:38:52 GMT OpenStack-API-Version: compute 2.1 Server: Apache/2.4.52 (Ubuntu) Vary: OpenStack-API-Version,X-OpenStack-Nova-API-Version X-OpenStack-Nova-API-Version: 2.1 x-compute-request-id: req-2b54695e-a3e7-4b20-a63f-12bbfd2e387b x-openstack-request-id: req-2b54695e-a3e7-4b20-a63f-12bbfd2e387b
RESP BODY: {"version": {"id": "v2.1", "status": "CURRENT", "version": "2.95", "min_version": "2.1", "updated": "2013-07-23T11:33:21Z", "links": [{"rel": "self", "href": "http://$KEYSTONE_IP/compute/v2.1/"}, {"rel": "describedby", "type": "text/html", "href": "http://docs.openstack.org/"}], "media-types": [{"base": "application/json", "type": "application/vnd.openstack.compute+json;version=2.1"}]}}
GET call to http://$KEYSTONE_IP/compute/v2.1 used request id req-2b54695e-a3e7-4b20-a63f-12bbfd2e387b
REQ: curl -g -i -X GET "http://$KEYSTONE_IP/compute/v2.1/servers/detail?flavor=None&image=None&name=None&project_id=None&reservation_id=None&status=None&user_id=None&deleted=False&host=None&ip=None&ip6=None&changes-since=None&changes-before=None" -H "Accept: application/json" -H "OpenStack-API-Version: compute 2.73" -H "User-Agent: openstacksdk/1.0.1 keystoneauth1/5.1.2 python-requests/2.28.2 CPython/3.10.6" -H "X-Auth-Token: {SHA256}53f1ee40c0bd2fdb7690380f4ccffba6b1829b2474d5103284cfa8c34887e57d" -H "X-OpenStack-Nova-API-Version: 2.73"
Resetting dropped connection: $KEYSTONE_IP
http://$KEYSTONE_IP:80 "GET /compute/v2.1/servers/detail?deleted=False HTTP/1.1" 200 15
RESP: [200] Connection: close Content-Length: 15 Content-Type: application/json Date: Mon, 08 May 2023 01:38:52 GMT OpenStack-API-Version: compute 2.73 Server: Apache/2.4.52 (Ubuntu) Vary: OpenStack-API-Version,X-OpenStack-Nova-API-Version X-OpenStack-Nova-API-Version: 2.73 x-compute-request-id: req-feb5e4e0-a692-4b35-94af-b620fa66b4f2 x-openstack-request-id: req-feb5e4e0-a692-4b35-94af-b620fa66b4f2
RESP BODY: {"servers": []}

keystone token 구조

Fernet Token이란?

  • Fernet 토큰은 사용자 인증을 나타내는 전달자 토큰
  • Fernet 토큰은 MessagePacked 라는 라이브러리를 사용하여 페이로드부분에 ID 및 권한등의 데이터를 저장
  • 그런 다음 페이로드는 웹 URL 및 헤더에서 사용하기 위해 Fernet 메시지로 래핑
  • fernet 토큰 내부의 데이터는 대칭 암호화 키 또는 fernet 키를 사용하여 보호

Fernet Token 분석

keystone은 fernet token을 생성하기 위해 key 저장소가 필요하다.
이 key는 토큰의 페이로드를 구성하는 정보를 암호화하고 해독하는데 사용되고, 저장소의 각 key는 세가지 상태 중 하나를 가진다.
key의 상태는 keystone이 fernet 토큰과 함께 키를 사용하는 방법을 결정한다.

key의 유형

  • Primary key(기본키)
  • key 저장소에는 단 하나의 Primary key만 있다.
  • 토큰을 암호화하고 해독하는데 사용된다.
  • 가장 높은 인덱스로 이름이 지정된다.
  • Secondary key(보조키)
  • Secondary key는 한 시점에서 Primary key였지만 다른 Primary key 대신 강등되었다. → 예전 Primary key 키
  • 토큰의 암호 해독만 허용된다.
  • Staged key(준비키)
  • 저장소에는 Staged key가 하나만 있을 수 있으며 반드시 있어야 한다.
  • Secondary key와 마찬가지로 토큰을 해독할 수 있다.
  • Staged key는 Primary key가 된 적이 없고 이 key는 항상 저장소에서 0 이라는 이름을 가진다.

fernet key 수명주기

Staged key ➡️ Primary key ➡️ Secondary key
  • 새로운 토큰은 Primary key로만 암호화될 수 있다. (Staged key, Secondary key는 암호화하는데 사용되지 않음)
  • Staged key
  • 이벤트 순서와 각 키 유형의 속성이 지정된 특수키
  • 아직 토큰을 암호화할 기회가 없었지만 여전히 토큰을 해독할 수 있는 저장소의 유일한 키
  • 하나의 keystone 노드에서 key 회전을 수행하고 일정 기간 동안 새 key set를 배포할 수 있는 기회 제공
  • Primary key로 암호화된 토큰은 key가 아직 staged 상태인 노드에서 암호를 해독하고 유효성을 검사할 수 있다.

fernet key repository

  • keystone process는 이 위치에서 key를 읽고 쓸 수 있고 key는 절대 유출이 되선 안된다.
[fernet_tokens]
key_repository = /etc/keystone/fernet-keys/

Rotate and Distribute keys

  • keystone-manage 명령줄 도구는 key rotation 메커니즘을 포함하고 있다.
  • 이 메커니즘은 key를 초기화하고 회전시키지만 key를 keystone node에 분배하지는 않는다.
  • keystone 배포 전반에 걸친 key 배포는 구성 관리 도구를 통해 처리하는 것이 가장 좋지만 새 Primary key가 먼저 배포되어야 한다.
  • 그런 다음 keystone-manage fernet_rotate 를 사용하여 key 저장소를 회전시킨다.

Add new keystone nodes to a deployment

특정 노드가 기존 클러스터에 가입하여 토큰을 발급하고 검증할 수 있도록 허용하기 전에 클러스터의 나머지 노드와 동일한 key 저장소를 가지고 있어야 한다.

Distribute keys

  • 매번 회전할 때마다 동일한 keystone 노드에서 key를 회전 및 분배할 필요는 없다.

  • 하나의 Keystone node에서 key를 회전하면 해당 노드의 key 저장소가 클러스터의 나머지 노드로 배포된다.

  • 각 노드의 key 저장소 상태가 동일한지 확인한 후에 클러스터의 다른 노드에서 회전하여 배포할 수 있다.

  • 위 과정들의 key 배포는 multi-node keystone 배포시에만 필요하다.

  • rsync 와 같은 것을 활용하여 key distribution을 관리할 수 있다.

  • Rsync(Remote Synchronization)

  • 원격으로 파일과 디렉토리를 복사하고 동기화하기 위해 사용하는 툴

  • 서버 ↔️ 서버간 사용자가 원하는 특정 디렉토리 및 파일을 동기화

  • link, device, 파일 소유자와 그룹 권한 등 파일 부가정보까지 복사 가능

# apt-get install rsync
  • kolla 참조
# apt-get -y install cron openssh-client rsync

Key rotation

  • 현재 Staged key를 Primary key로 승격하고 새로운 Staged key를 만들고 오래된 Secondary key를 제거하는 단일 작업이다.
  • 단일 노드에서 이 작업을 수행하고 클러스터의 나머지 부분에 key 저장소를 배포하기 전에 rotation이 잘 이루어졌는지 확인해야 한다.
  • Staged key를 사용하면 클러스터의 나머지 부분과 상태를 동기화하기 전에 새 key 저장소를 검사할 수 있다.
  • key rotation과 key distribution은 분리되어 있는 작업이다.
1. 배포된 모든 keystone node가 동일한 key 저장소를 가지고 있는지 확인한다.
2. 클러스터에서 rotate operation을 수행할 keystone node를 선택한다.
3. key를 회전
  • 성공
  • 새로운 staged key가 생성된다.
  • 이전의 staged key가 새로운 primary key가 되어야 한다.
  • max_active_keys 제한에 따라 secondary key가 생길 수 있다.
  • 이때 회전한 노드는 다른 모든 노드가 staged key로 가져야 하는 Primary key를 사용하여 fernet 토큰을 생성한다.
  • 클러스터의 다른 모든 노드는 새 primary key로 만든 토큰을 해독할 수 있어야 한다.
  • 실패
  • 키를 회전시킨 특정 노드의 문제를 조사한다.
  • keystone-manage fernet_rotate의 로그는 특정 오류에 대한 자세한 정보를 제공한다.
3개의 좋아요