본문 바로가기
Openstack

Openstack token provider(fernet)







Oepnstack Ocata 릴리즈 부터 기본적으로 fernet token 을 기본 토큰 프로바이더로 사용 되고 있다.
기존에 사용 하고 있던 UUID는 스토리지 공간 문제와 네트워크 트래픽 문제가 있으며, 
UUID를 대체하고자 나온 PKI/PKIZ는 큰 공유 캐시 문제와 UUID 보다 느린 성능으로 Ocata 릴리즈에서 아예 제거 되었다.

fernet은 대칭키 암호 인증 방식으로 , 암호화와 복호화에 동일한 키를 사용 한다. Openstack kilo 릴리즈에서 도입 되어 Ocata 릴리즈 부터 기본 token provider 로 지정 되었다.
또한, 다중키를 사용 하여 첫번째(현재키: Primary key)를 사용 하고 다른키(이전키: Secondary Key 및 준비키:Staged Key)를 이용 하여 복호화를 진행 한다. 
이를 통하여 키를 정기적으로 rotation 하여 보안을 강화 하고, 이전키로 만든 토큰은 계속 복호화에 사용 된다.


위 그림과 같이 맨 처음에 1,0 이라는 키가 생성이 되면 숫자 가장 높은 1이 Primary key 로 암호화/복호화를 진행 한다.
이때 1보다 작은 0은 Secondary Key 가 아닌 Staged Key 지정 되어 Primary key 가 될 준비를 한다.
(0 으로 지정된  key 는 Secondary Key 가 될 수 없으며, Secondary Key는 Primary key 로 사용 후 rotation 되어야만 Secondary Key 로 사용 할 수 있다.)

Staged Key 어떠한 수행도 하지 않고  Primary key가 rotation 되어 Secondary Key 가 되면 기존의 Primary key 보다 +1 되어 
Primary key 로 지정 되어 암호화/복호화를 수행 하며 Secondary Key 가 된 1 이라는 key 는 복호화만 진행 한다.
이때 다음 rotation을 위하여 0이라는 이름의 Staged Key 가 새로 생성 되어 , 다음 rotation 시 해당 Staged Key 가 Primary key 로 지정 된다.

정리 하면 fernet 에서 사용 되는 3가지 key 는 아래와 같은 특징을 갖고 있다.

  • Primary key : 현재키로 불리며 암호화/복호화를 진행 하여 가장 높은 색인 번호
  • Secondary key : 이전에 Primary key 로 사용 되고 교체되니 키로, 원래 암호화된 나머지 fernet 토큰을 해독 한다. 가장 높은 색인 번호의 key와 0을 제외한 key 로 지정 되어 있다.
  • Staged key :  항상 0으로 지정 되어 있으며, 가장 최근추가된 키로 다음 rotation시 Primary key 로 지정 된다 . 실제로는 불필요해 보지만 다중 클러스터 구조에서 미리 배포하여  Primary key 가 만료 되거나 rotation 시 한 번에 하나씩 순환을 수행하는데 필요 하다.(즉, 단일노드에서는 불필요) 

실제 수행 되는 key 는 /etc/keystone/fernet-keys 에서 확인 할 수 있다.


Fernet key를 실제로 확인 하면 256bit 구조에 128 bit SHA256 HMAC 으로 hash 되어 있으며, 나머지 128 bit 는  AES로 대칭키 암호화 되어 있다.

token이 생성 되면 위와 같은 정보로 구성 되어 있다 HMAC hash 를 제외한 부분에서 토큰의 버전,최근timestamp(UTC) +IV(initialization vector: aes 암복호화시 사용 https://en.wikipedia.org/wiki/Initialization_vector  참고) + 실제 암호화 텍스트로 구성 되어 있다.

실제 암호화 텍스브 부분은 위와 같이 token payload 의 속성으로 버전, id, project id 등 실제로 노출 불가한 정보들이 암호화 되어 있으며,
크기를 고정 하기 위하여 나머지 부분은 padding 되어 처리 되어 있다.



Fernet token in Kolla 

keystone-fernet 컨테이너 상으로 아래 같은 설정을 확인 할 수 있다.

기본 설치시 아래돠 같이 최대 4개의 키로 1일이라는 만료일로 fernet이 proveder 로 선택 되었다.

/etc/keystone/keystone.conf
...
[token]
revoke_by_id = False
provider = fernet
expiration = 86400 ## 토큰 만료 =>1
 
[fernet_tokens]
max_active_keys = 4 ## 최대 active key 갯수 (primary key + secondary key + Staged key)
 
...


rotate 은 매일 0시0분에 해당 스크립트를 실행 하도록 되어 있다.

/var/spool/cron/c/fernet-cron
0 0 * * * /usr/bin/fernet-rotate.sh


fernet-rotate.sh 를 확인 하면 키생성 후 다른 노드로 rsync 로 전달 하는 역할 만 한다.
/usr/bin/fernet-rotate.sh
#!/bin/bash
 
keystone-manage --config-file /etc/keystone/keystone.conf fernet_rotate --keystone-user keystone --keystone-group keystone
 
/usr/bin/rsync -az -e 'ssh -i /var/lib/keystone/.ssh/id_rsa -p 8023 -F /var/lib/keystone/.ssh/config' --delete /etc/keystone/fernet-keys/ keystone@controller02:/etc/keystone/fernet-keys
/usr/bin/rsync -az -e 'ssh -i /var/lib/keystone/.ssh/id_rsa -p 8023 -F /var/lib/keystone/.ssh/config' --delete /etc/keystone/fernet-keys/ keystone@controller03:/etc/keystone/fernet-keys

스크립트를 수동으로 실행 하면 아래와 같이 0 이 새로 생성 되고 11이 이전에 0으로 변경 되는 것을 확인 할 수 있다(시간으로 확인) 

/etc/keystone
## 실행전
(keystone-fernet)[root@controller02 keystone]# ls -al fernet-keys/
total 16
drwxrwx--- 2 keystone keystone 43 May  7 10:52 .
drwxr-x--- 1 root     keystone 46 May  7 12:28 ..
-rw------- 1 keystone keystone 44 May  7 10:52 0
-rw------- 1 keystone keystone 44 May  7 10:41 10
-rw------- 1 keystone keystone 44 May  6 23:14 8
-rw------- 1 keystone keystone 44 May  7 10:25 9
 
## 실행후
(keystone-fernet)[root@controller02 keystone]# ls -al fernet-keys/
total 16
drwxrwx--- 2 keystone keystone 44 May  7 12:54 .
drwxr-x--- 1 root     keystone 46 May  7 12:28 ..
-rw------- 1 keystone keystone 44 May  7 12:54 0
-rw------- 1 keystone keystone 44 May  7 10:41 10
-rw------- 1 keystone keystone 44 May  7 10:52 11
-rw------- 1 keystone keystone 44 May  7 10:25 9


그리고 , kolla로  deploy될 경우 docker 환경으로 내부의 key가 docker 재시작 할때 마다 새로 생성 된다.

kolla 에서는  /usr/bin/fernet-node-sync.sh 파일을 통하여 시작 할때 마다 sync 를 맞춘다.

아래와 같이 fetch_fernet_tokens.py 파일을 통하여 토큰의 시간을 확인 하고 생성  stable 인지 확인 하고 각 노드들로 sync 하는 스크립트 라는 것 을 확인 할 수 있다.

/usr/bin/fernet-node-sync.sh
#!/bin/bash
 
# Get data on the fernet tokens
TOKEN_CHECK=$(/usr/bin/fetch_fernet_tokens.py -t 86400 -n 4)
 
# Ensure the primary token exists and is not stale
if $(echo "$TOKEN_CHECK" | grep -q '"update_required":"false"'); then
    exit 0;
fi
 
# For each host node sync tokens
 /var/lib/keystone/.ssh/config' keystone@controller02:/etc/keyston /etc/keystone/fernet-keys
/usr/bin/rsync -azu --delete -e 'ssh -i /var/lib/keystone/.ssh/id_ /var/lib/keystone/.ssh/config' keystone@controller03:/etc/keyston /etc/keystone/fernet-keys


kolla_extend_start 파일을 보면  fernet-node-sync.sh 파일을 실행 하는 것을 확인 할 수 있다. 그러면 이것을 누가 어떻게 실행 할까 ?

i /usr/local/bin/kolla_extend_start
#!/bin/bash
 
FERNET_SYNC=/usr/bin/fernet-node-sync.sh
FERNET_TOKEN_DIR="/etc/keystone/fernet-keys"
 
if [[ -f "${FERNET_SYNC}" ]]; then
    ${FERNET_SYNC}
fi
 
if [[ $(stat -c %U:%G ${FERNET_TOKEN_DIR}) != "keystone:keystone" ]]; then
    chown keystone:keystone ${FERNET_TOKEN_DIR}
fi


해당   docker파일이 빌드 될때 runcommad 를 통해서 실행시 마다 sync를 한다는 것을 확인 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash
FROM {{ namespace }}/{{ image_prefix }}keystone-base:{{ tag }}
LABEL maintainer="{{ maintainer }}" name="{{ image_name }}" build-date="{{ build_date }}"
{% block keystone_fernet_header %}{% endblock %}
{% import "macros.j2" as macros with context %}
{% if base_distro in ['centos''oraclelinux''rhel'] %}
    {% set keystone_fernet_packages = [
        'cronie',
        'openssh-clients',
        'rsync'
    ] %}
{% elif base_distro in ['debian''ubuntu'] %}
    {% set keystone_fernet_packages = [
        'cron',
        'openssh-client',
        'rsync'
    ] %}
{% endif %}
{{ macros.install_packages(keystone_fernet_packages | customizable("packages")) }}
COPY fetch_fernet_tokens.py /usr/bin/
COPY keystone_bootstrap.sh /usr/local/bin/kolla_keystone_bootstrap
COPY extend_start.sh /usr/local/bin/kolla_extend_start
RUN chmod 755 /usr/local/bin/kolla_extend_start /usr/local/bin/kolla_keystone_bootstrap /usr/bin/fetch_fernet_tokens.py
{% block keystone_ernet_footer %}{% endblock %}
{% block footer %}{% endblock %}


참고: https://docs.openstack.org/keystone/latest/admin/identity-tokens.html


반응형