이쿠의 슬기로운 개발생활

함께 성장하기 위한 보안 개발자 EverNote 내용 공유

클라우드/Kubernetes

19. Kubernetes 인증 (Webhook 외부 인증 LDAP)

이쿠우우 2020. 8. 29. 17:53
반응형

Kubernetes Webhook 외부 인증 LDAP

 


Kubernetes 인증 관련 글 이동 

1. Kubernetes 인증 ( Authentication ) 이론
2. Kubernetes 인증 ( Basic Authentication )
3. Kubernetes 인증 ( TLS )
4. Kubernetes 인증 ( Bearer Token )
5. Kubernetes 인증 ( Webhook )
6. Kubernetes 인증 ( Webhook 외부 인증 연동 )
7. Kubernetes 인증 ( Webhook 외부 인증 LDAP )
8. Kubernetes 인증 ( Proxy )


 

 

참고) 환경

 

LDAP Server

 OS = CentOS 7

 리눅스 커널 버전 : Linux 3.10.0-1062.el7.x86_64

 

Webhook Server

 OS = CentOS 7

 리눅스 커널 버전 : Linux 3.10.0-1062.el7.x86_64

 

Master Node server

 OS = CentOS 7

 리눅스 커널 버전 : Linux 3.10.0-1062.el7.x86_64

 docker version : 1.13.1

 api verison : 1.26

 

Worker Node server

 OS = CentOS 7

 리눅스 커널 버전 : Linux 3.10.0-1062.el7.x86_64

 docker version : 1.13.1

 api verison : 1.26

 

Kubernetes version

 1.18

 

 

 


 

 

Webhook 외부 인증 LDAP 서버 구축 및 적용

 

 

[목표]

LDAP에 사용자 계정, 패스워드를 저장하고 있고 

이 정보를 Webhook 을 통해 Kubernetes 와 연동하는 형태로

kubernetes 인증 서버를 구성.

 


 

1. LDAP 이란?

 

LDAP를 이해하기 위해 필요한 지식 : 디렉토리, 디렉토리 서비스

 

[디렉토리란?]

일반적으로 디렉토리는 데이타의 집합 또는 리스트를 뜻함.

디렉토리의 역할은 정보를 구조적으로 저장하고 효율적으로 검색할 수 있게하여 관심있는 사람들이 원할 때 이용할 수 있도록 해주는 것.

실생활 예) : 전화 번호부

 

[디렉토리 서비스란?]

사용자가 디렉토리에 저장된 정보에 접근하게 해주는 솔루션.

디렉토리 서비스는 일반적으로 소프트웨어로 제공됨.

네트워크를 통해 디렉토리에 접근해야한다면 적절한 프로토콜이 정의되어야함

그 프로토콜이 바로 LDAP

실생활 예) : 전화 번호부를 보고 말해주는 콜센터 직원

 

[ LDAP (Lightweight Directory Access Protocol) ]

LDAP는 조직이나 개체, 인터넷 등 네트워크 상에 있는 파일이나 장치들과 같은 자원의 위치를 찾을 수 있게 해주는 소프트웨어 응용 프로토콜.

전자 디렉토리 서비스를 전달하는 일련의 컴퓨터 네트워크 표준인 X.500에서 정의한 DAP 프로토콜의 경량화 버전.

디렉토리 서비스 엑세스를 위한 클라이언트 - 서버 프로토콜로 TCP/IP 위에서 디렉터리 서비스를 조회하고 수정하는 역할을 수행함.

LDAP 정보 모델에서 데이타는 계층적인 형태의 트리구조를 이루는 엔트리 안에 저장.

LDAP는 엔트리의 생성, 수정, 삭제, 검색 기능을 가지고 있음.

DN ( Distinguished Name )  : 엔트리의 유일한 이름, 트리구조 안에서 위치를 나타냄.

속성 (attribute) : 엔트리는 Key : Value 쌍으로 구성됨.

 

 

 


 

 

 

2. LDAP 서버 구축

 

2.1. LDAP tool download 후 실행

 

[ Download ]

yum update

yum -y install openldap compat-openldap openldap-clients openldap-servers openldap-servers-sql openldap-devel

 

[ Start ]

systemctl start slapd

systemctl enable slapd

 

[ 실행 확인 ]

netstat -antup | grep -i 389

 

[ version 확인 ]

ldapsearch -VV

 

 

2.1. LDAP 설정

 

[ 명령어 ]

slappasswd

생성한 Password를 미리 복사해놔야함

 

참고) LDAP 설정 파일 경로 : /etc/openldap/slapd.d

 

설정 파일 중 olcSuffix, olcRootDN, olcRootPW 변경을 해줘야함

해당 3가지 설정에 해당하는 파일 경로 : /etc/openldap/slapd.d/cn=config 의

olcDataBase={2)hdb.ldif 파일.

하지만 해당 파일을 직접 수정하지 않고 임시로 만든 뒤에 설정을 적용.

 

[ db.ldif 예제 ]

dn: olcDatabase={2}hdb,cn=config

changetype: modify

replace: olcSuffix

olcSuffix: dc=iksoon,dc=ldap,dc=com # dc는 사용할 도메인을 넣어줌 (iksoon.ldap.com)

 

 

dn: olcDatabase={2}hdb,cn=config

changetype: modify

replace: olcRootDN

olcRootDN: cn=ldapadm,dc=iksoon,dc=ldap,dc=com # dc는 사용할 도메인을 넣어줌 (iksoon.ldap.com)

 

 

dn: olcDatabase={2}hdb,cn=config

changetype: modify

replace: olcRootPW

olcRootPW: {SSHA}mWnRDryXwmtKAXBqjv2IkHGpq4P2usgW # 상위에서 slappasswd 명령으로 생성한 Password.

 

 

작성한 db.ldif 파일 내용을 ldap server에 업데이트

명령어 : ldapmodify -Y EXTERNAL  -H ldapi:/// -f db.ldif

 

 

2.2. monitor 접속 계정 설정

monitor.ldif 파일 만들어서 업데이트 해야함

 

[ monitor.ldif 예제 : ldapadm 계정만 접속 ]

dn: olcDatabase={1}monitor,cn=config

changetype: modify

replace: olcAccess

olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external, cn=auth" read by dn.base="cn=ldapadm,dc=iksoon,dc=ldap,dc=com" read by * none

[ 설정 업데이트 ]

명령어 : ldapmodify -Y EXTERNAL  -H ldapi:/// -f monitor.ldif

 

 

2.3. LDAP Database 설정

[ LDAP Database 설정 파일 경로 ]

/usr/share/openldap-servers

 

해당 디렉터리의 DB_CONFIG.example 파일을 

/var/lib/ldap/ 경로로 복사하고 권한을 설정.

 

 

2.4. LDAP 스키마 적용

 

[ 명령어 ]

ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/cosine.ldif

ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/nis.ldif

ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/inetorgperson.ldif

 

 

 

 

2.4. LDAP 도메인 정보 변경

 

도메인을 iksoon.ldap.com 으로 변경하는 작업

예제 파일을 생성하여 해당 파일을 설정에 적용해야함.

 

[ base.ldif 예제 파일 ]

dn: dc=iksoon,dc=ldap,dc=com

dc: iksoon

objectClass: top

objectClass: domain

 

 

dn: cn=ldapadm, dc=iksoon,dc=ldap,dc=com

objectClass: organizationalRole

cn: ldapadm

description: LDAP Manager

 

 

dn: ou=People, dc=iksoon,dc=ldap,dc=com

objectClass: organizationalUnit

ou: People

 

 

dn: ou=Group, dc=iksoon,dc=ldap,dc=com

objectClass: organizationalUnit

ou: Group

 

 

[ 적용 명령어 ]

ldapadd -x -W -D "cn=ldapadm,dc=iksoon,dc=ldap,dc=com" -f base.ldi

 

[ 서버에 도메인 설정 ]

vi /etc/hosts

127.0.0.1  iksoon.ldap.com

추가 후 저장

 

 

2.5. 예제 계정 생성

[ iksoonLDAPuser.ldif 예제 ]

dn: uid=iksoonldap,ou=People,dc=iksoon,dc=ldap,dc=com

objectClass: top

objectClass: account

objectClass: posixAccount

objectClass: shadowAccount

cn: iksoonldap

uid: iksoonldap

uidNumber: 9999

gidNumber: 100

homeDirectory: /home/iksoonldap

loginShell: /bin/bash

gecos: iksoonldap [Admin (at) nGle]

userPassword: {crypt}x

shadowLastChange: 17058

shadowMin: 0

shadowMax: 99999

shadowWarning: 7

 

 

[ 적용 명령어 ]

ldapadd -x -W -D "cn=ldapadm,dc=iksoon,dc=ldap,dc=com" -f iksoonLDAPuser.ldif

 

[ 등록한 계정 Password 설정 ]

명령어 :

ldappasswd -s qwer1234 -W -D "cn=ldapadm,dc=iksoon,dc=ldap,dc=com" -x "uid=iksoonldap,ou=People,dc=iksoon,dc=ldap,dc=com"



[ 옵션 설명 ]

-s = Password

-D = ldapdm 정보

-x = User

 

[ 등록한 User 검색 ]

명령어 : 

ldapsearch -x cn=iksoonldap -b dc=iksoon,dc=ldap,dc=com

 

 

2.6. 참고)

[계정 삭제 명령어 ]

ldapdelete -x -D cn=ldapadm,dc=iksoon,dc=ldap,dc=com -W  uid=iksoonldap,ou=People,dc=iksoon,dc=ldap,dc=com

3. PHP LDAP Admin 연동

 

위에서 Cli를 통해 LDAP을 관리하기는 너무 복잡하고 힘듬.

PHP LDAP Admin 을 사용하면 편하게 LDAP을 관리할 수 있음

 

3.1. install php package

[ download 명령 ]

yum install -y php-ldap php-mbstring php-pear php-xml

yum install -y epel-release

yum -y install phpldapadmin

 

 

3.2. phpldapadmin 설정 

 

vi /etc/phpldapadmin/config.php

398번째 줄을 보면 

//$servers->setValue('login','attr','dn');

$servers->setValue('login','attr','uid');

이렇게 되어 있는데

 

$servers->setValue('login','attr','dn');

//$servers->setValue('login','attr','uid');

이렇게 바꾸어줌

 

그리고 542, 545 번째 줄을

상위 OpenLDAP 설정 시 사용했던 값으로 넣어줌

 

[ 설정 저장 후 재기동 ]

systemctl restart httpd

 

3.3. phpldapadmin 접속

[ 접속 주소 ]

localhost/ldapadmin/index.php

 

id는 상위에서 설정했던 dn 주소 : cn=ldapadm,dc=iksoon,dc=ldap,dc=com

password 도 상위에서 설정한 값을 넣어줌

 

 

 

[ 접속 완료 ]

 

 

 

 

 

 


 

 

 

4. Webhook 인증 서버 구축을 위해 python 설치 및 flask 패키지 설치

(WebHook 인증 서버를 구축할 서버에서 작업)

 

CentOS7은 기본적으로 Python 2.7.5 version 이 설치되어 있음

 

Python 3.8 version 으로 변경하는 작업 진행

 

4.1. 먼저 python 설치 시 필요한 tool 설치

yum install -y gcc openssl-devel bzip2-devel libffi-devel wget

yum update

 

4.2. python 최신버전 설치

wget https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz   

tar xvf Python-3.8.5.tgz

cd Python-3.8.5/

./configure --enable-optimizations

make altinstall

 

4.3. python 3.8을 main 으로 쓸 수 있도록 링크 작업 진행

ls -l /bin/python*

unlink /bin/python

ln -s /usr/local/bin/python3.8 /bin/python

 

4.4. pip 명령으로 flask 패키지 설치

python3.8을 설치해서 pip3.8 명령으로 pip 를 사용해야함

[ 명령어 ]

pip3.8 install flask

 

flask 란
플라스크(Flask)는 아주 가벼운 마이크로(Micro) 서버 프레임워크.
마이크로라는 이름에 걸맞게 아주 작고 가벼우며, 빠르게 동작. 
Flask는 서버 프레임워크에 필요한 핵심 기능만 제공하고, 
나머지는 개발자가 직접 개발하거나 다른 패키지로 확장시켜서 사용되도록 유연하게 설계되어있음

4.5. pip 명령으로 ldap 패키지 설치

[ 명령어 ]

pip3.8 install python-ldap

 

 


 

 

5. Webhook 인증 서버 구축

(WebHook 인증 서버를 구축할 서버에서 작업)

 

 

[ iksoon-webhook-LDAP.py ]

(상위에서 구축한 LDAP 인증서버와 연동하는 코드)

from flask import Flask, request, jsonify

import pprint

import requests

import ldap

from requests.auth import HTTPBasicAuth

 

app = Flask(__name__)

 

@app.route('/', methods=['POST'])

 

def auth():

 

        # User가 kubernetes API Server에 인증 요청

        # Kubernetes API Server 가 사전에 정의 된 Webhook Server로 REST 요청보낸것을 받아오는 코드

        tokenReview = request.json

        print("\n")

        print("\n")

        pprint.pprint('---return result---')

        pprint.pprint(tokenReview)

 

        print("\n")

 

        # Webhook Server에 연동되어있는 인증 서버에서 인증 결과 받아옴

        tokenReview['status'] = external_auth_LDAP(tokenReview)

        pprint.pprint('---return result---')

        pprint.pprint(tokenReview)

 

 

        # Webhook Server에서 Kubernetes API Server로 인증결과 보냄

        return jsonify(tokenReview)

 

 

# 외부 인증 시스템

def external_auth_LDAP(tokenReview):

        try:

                user, pw = tokenReview['spec']['token'].split (':')

 

                # 예제에서 생성했던 도메인 정보 (/etc/hosts 에 localhost로 명시했음)

                ldap_address = "ldap://iksoon.ldap.com:389"

 

                ldap_object = initialize_ldap(ldap_address)

                ldap_result = authenticate(ldap_object, ldap_address, user, pw)

 

                print('result: %s'%(ldap_result))

 

                if ldap_result == True:

                        status = {}

                        status['authenticated'] = True

                        status['user'] = {

                                'username': user,

                                'uid': user,

                                #'groups': ['system:masters'] # 주석 해제하면 관리자 권한 부여

                        }

                else :

                        status = {}

                        status['authenticated'] = False

        except:

                status = {}

                status['authenticated'] = False

        return status

 

 

def authenticate(ldap_object, ldap_address, user_name, password):

        try:

                ldap_object.simple_bind_s(user_name, password)

        except ldap.INVALID_CREDENTIALS:

                ldap_object.unbind()

                return False

        except Exception as e:

                print(e)

                return False

        return True

 

 

 

def initialize_ldap(ldap_address):

        ldap_object = ldap.initialize(ldap_address)

        return ldap_object

 

 

if __name__ == '__main__':

        app.run(host= '0.0.0.0', port=6000, debug=True)

 

[ 서버 실행 ]

python iksoon-webhook-LDAP.py

 

[ 결과 ]

정상 실행됨

 

 

 

 

 


 

 

 

6. Kubernetes Webhook 인증 설정 작업

(Kubernetes Master Node 에서 작업)

 

kubernetes 가 webhook 서버에 연결하기 위해 webhook server 의 정보를 설정해야함

설정 방법은 kubeconfig 파일(/etc/kubernetes/admin.conf)의 형식과 동일함

해당 설정을 kube-apiserver 가 가져감

 

참고(상위에서 작업한 webhook server ip)  : 10.0.2.5

 

[ /etc/kubernetes/pki/webhook.yaml 설정 ]

apiVersion: v1

kind: Config

clusters:

- cluster:

    insecure-skip-tls-verify: true

    server: http://10.0.2.5:6000  # WebHook server의 주소를 명시함

  name: kubernetes

contexts:

- context:

    cluster: kubernetes

    user: kube-apiserver

  name: iksoon-test@kubernetes

current-context: iksoon-test@kubernetes

users:

- name: kube-apiserver

/etc/kubernetes/manifests 경로의  kube-apiserver.yaml 파일 설정 추가

 

[설정 ]

--authentication-token-webhook-config-file=[상위에서 생성한 webhook 설정 yaml 파일 경로]

예) --authentication-token-webhook-config-file=/etc/kubernetes/pki/webhook.yaml

 

추가 후  적용 명령어 : kubeadm init --config kube-apiserver.yaml

 또는

kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml

적용 후 

kubectl get all -n kube-system 으로 상태 확인.

 

 


 

 

 

7. Kubernetes Webhook 인증 RBAC 연동

(Kubernetes Master Node 에서 작업)

 

RoleBinding 의 Subjects.kind.name 을 

Basic Authentication server의 id 와 매칭 시켜주면 

role 이 해당 id 에 적용 됨.

 

[ 예제 role.yaml 파일 (role과 rolebinding 을 생성함 ]

apiVersion: rbac.authorization.k8s.io/v1

kind: Role

metadata:

  namespace: iksoon-ns

  name: iksoon-role-ldap

rules:

- apiGroups: ["", "extensions", "apps"]

  resources: ["deployments", "pods", "replicasets"]

  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # you can also use ["*"]

 

---

 

apiVersion: rbac.authorization.k8s.io/v1

kind: RoleBinding

metadata:

  name: iksoon-rb-ldap

  namespace: iksoon-ns

subjects:

- kind: User

  name: "cn=ldapadm,dc=iksoon,dc=ldap,dc=com" # LDAP server 에 등록된 계정과 동일하게 설정 

            # 정확히 는 tokenReview request 의 uid 와 username 과 동일한 값이여야함

roleRef:

  kind: Role

  name: iksoon-role-ldap

  apiGroup: rbac.authorization.k8s.io

 

 

 


 

 

 

 

8. Client 에서 kubeconfig.conf 파일 작성

(kubectl 명령으로 붙고자하는 Client 에서 작업)

 

kubectl 을 사용할 client kubeconfig 파일 환경변수 설정 작업 진행

export KUBECONFIG="kubeconfig 파일 경로"

 

[ kubeconfig.conf 파일 예시 ]

apiVersion: v1

kind: Config

clusters:

- cluster:

    insecure-skip-tls-verify: true

    certificate-authority-data:

    server: https://10.0.2.15:6443 # kubernetes master node 주소: apiserver 접근 포트

  name: kubernetes

contexts:

- context:

    cluster: kubernetes

#  namespace: iksoon-ns # 해당 설정을 하게 되면 kubectl 명령 시 default namespace 가 iksoon-ns 로 설정됨

    user: iksoon

  name: iksoon-test@kubernetes

current-context: iksoon-test@kubernetes

preferences: {}

users:

- name: iksoon

  user:

    token: "cn=ldapadm,dc=iksoon,dc=ldap,dc=com:qwer1234"  # [ID : Password] LDAP Server에 등록되어있는 ID, PW를 입력

kubeconfig.conf 로  KUBECONFIG 환경변수가 설정되어 있어서

kubectl 명령하면 default 로 KUBECONFIG 경로를 참조해서 동작함

 

참고) kubeconfig.conf 파일로 default NameSpace 변경 방법

[ 명령어 ]

kubectl config set-context --current --namespace=iksoon-park

 

[ 변경 후 확인 명령어 ]

kubectl config view

확인해보면 -context.namespace 항목이 추가된것을 확인할 수 있음

 

 

 


 

 

9. 결과 확인

(kubectl 명령으로 붙고자하는 Client 에서 작업)

 

[ client 에서 kubectl 명령 실행 결과 ]

정상 

 

만약 인증에 실패했을경우 아래와 같은 결과가 나옴

 

role에 권한이 없을 경우는 아래와 같은 결과가 나옴

 

 

 

[ 결과 성공 시 webhook flask server log 상태 ]

아래와 같이 flask 코드에서 무조건 성공으로 리턴하는 data를 확인할 수 있음

 

예제는 Token을 ID:Password 형식으로 전달했지만

실제 요청은 해당 ID:Password 를 Base64 encoding (혹은 유사한 encoding) 방식으로 보관하는것이 좋음

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

참고) python LDAP 참고 소스

 

[ python-ldap download 방법 ] 

명령어 : pip3.8 install python-ldap

 

Python-ldap 를 활용해서 사용자의 ID, PW 정보를 받아 인증하는 테스트 코드

 

[ 성공 시 return 값 ]

(97, [], 1, []) Succesfully authenticated

 

[ 인증 실패시 return 값 ]

Wrong username or password

 

import ldap

 

def authenticate(ldap_object, ldap_address, user_name, password):

        try:

                print(ldap_object.simple_bind_s(user_name, password))

        except ldap.INVALID_CREDENTIALS:

                ldap_object.unbind()

                return 'Wrong username or password'

        except Exception as e:

                print(e)

                return "error"

        return "Succesfully authenticated"

 

 

def initialize_ldap(ldap_address):

        ldap_object = ldap.initialize(ldap_address)

        return ldap_object

 

 

def main():

        ldap_address = "ldap://iksoon.ldap.com:389" # 예제에서 생성했던 도메인 정보 (/etc/hosts 에 localhost로 명시했음)

        user_name = "cn=ldapadm,dc=iksoon,dc=ldap,dc=com" # 예제에서 생성했던 user 정보

        password = "qwer1234" 

 

        ldap_object = initialize_ldap(ldap_address)

        print(authenticate(ldap_object, ldap_address, user_name, password))

 

 

 

if __name__ == '__main__':

        main()

 

 

 


제 글을 복사할 시 출처를 명시해주세요.
글에 오타, 오류가 있다면 댓글로 알려주세요! 바로 수정하겠습니다!


 

 

 

 

 

 

 

 

 

 

 

참고

webhook 참고

https://learnk8s.io/kubernetes-custom-authentication

https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication

 

LDAP Webhook

https://itnext.io/implementing-ldap-authentication-for-kubernetes-732178ec2155#bfce

 

LDAP 구축

https://dejavuqa.tistory.com/264

 

디렉터리, 디레터리 서비스, LDAP

https://m.blog.naver.com/PostView.nhn?blogId=jaehun922102&logNo=120128137819&proxyReferer=https:%2F%2Fwww.google.com%2F

 

python ldap 라이브러리 참고

https://jangseongwoo.github.io/ldap/ldap_python/

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형

'클라우드 > Kubernetes' 카테고리의 다른 글

21. Helm  (0) 2020.09.07
20. Kubernetes 인증 ( Proxy )  (0) 2020.08.29
18. Kubernetes 인증 ( Webhook 외부 인증 연동 )  (0) 2020.08.29
17. Kubernetes 인증 ( Webhook )  (0) 2020.08.29
16. Kubernetes 인증 ( Bearer Token )  (0) 2020.08.29