[keycloak] RADIUS 서버 설치 및 Keycloak 연동 설정


RADIUS 서버 용도에 대해서

  • Wifi 접속 인증으로 사용하기 위해서는 RADIUS 서버가 EAP를 지원해야 합니다.
    • 단말에서 사용자가 입력한 ID/PW를 RADIUS 서버에서 복호화해서 평문으로 추출 할수 있어야 Keycloak에 로그인 요청을 할 수 있습니다.
    • EAP 규격중 EAP-TTLS를 사용하고 2차인증으로 PAP를 사용합니다. PEAP, CHAP 등의 방식은 hash를 이용해 handshake하는 방식이라 RADIUS 서버에서 사용자가 입력한 Password를 알아 낼수 없습니다.
  • Wifi 접속 인증이 필요없고, SSH 접속인증을 Keycloak과 연동하기 RADIUS 서버를 사용하는 경우 EAP 관련 설정은 불필요합니다.

구성

  • Docker Hub에 공개되어 있는 이미지를 기반으로 python 사용이 가능하도록 수정한 이미지를 생성해 사용합니다.
  • FreeRADIUS는 다양한 모듈을 제공합니다. 그중에 Python 모듈(rlm_python)을 이용해 keycloak과 연동합니다.
    • 많은 모듈을 제공하는데 Keycloak 모듈은 지원 안해요 ㅠ_ㅠ
    • Perl 모듈(rlm_perl)도 있는데 전 Perl은 쓸 자신이 없네요.
    • REST 모듈(rlm_rest)로도 할 수 있을거 같은데.. keycloak 연동시에 API 요청을 여러번 해야 해서 이걸로 하면 처리가 힘들 수 있을거 같아서 검토하지 않았습니다.
  • 처리 흐름은 아래와 같습니다. {{ }} graph LR; CLIENT[시작: Client] –>|1. RADIUS 요청| FREE_RADIUS(FreeRADIUS) subgraph Radius-Server FREE_RADIUS –>|2| PYTHON_MODULE(Python모듈) end PYTHON_MODULE –>|3. 권한확인 요청| KEYCLOAK[Keycloak] KEYCLOAK –>|4. 권한확인 응답| PYTHON_MODULE PYTHON_MODULE –>|5| FREE_RADIUS FREE_RADIUS –>|6. RADIUS 응답| CLIENT {{ }}

설치시 주의사항

  • Radius는 UDP Port2개를 사용합니다.(default: 1812, 1813)
  • UDP는 AWS에서 Load Balancer를 사용할 수 없습니다. 얼마 전부터 지원시작
    • Kubernetes에 설치시 ingress의 유형에 따라 UDP를 지원하지 않을 수 있습니다.
    • 그냥 NodePort로 노출 시키고, Route53에서 Kubernetes에 사용된 각 Node(EC2 Instance)의 Public IP로 Round Robin으로 분산 시켜도 됩니다.(참고)
    • Kubernetes에서 NodePort를 사용 할 경우 사용가능한 Port Range는 30000-32767 입니다.(Radius의 기본 Port번호 사용 불가)

FreeRADIUS 설정 및 Docker Image 생성

  • Docker Hub에 공개되어 있는 이미지를 그대로 이용하면 좋지만… 기본 이미지는 python을 지원하지 않습니다.
  • Python 모듈에서 암복호화 기능도 사용할 예정이므로 pycrypto도 필요합니다.
  • FreeRADIUS 설정 파일들은 굳이 Docker Image에 넣지 않고 실행시 mount 해도 상관없습니다만, 저는 Docker Image에 설정파일을 수정해서 포함 시켰습니다.
  • FreeRADIUS에서 사용 할 인증서 파일도 생성해서 이미지에 포함시킵니다.(이것도.. 실행시 mount 해도 무방합니다.)

1. 설정 디렉토리 가져오기.

  • 먼저 Docker Hub에 공개되어 있는 이미지를 실행시킨 후 설정 디렉토리(/etc/freeradius)를 추출 합니다.
  • 추출한 설정 파일을 적절히 수정하고, 수정한 파일을 적용해서 Docker Image를 새롭게 생성할 예정입니다.
$ docker run -d --name freeradius-server freeradius/freeradius-server
$ docker cp freeradius-server:/etc/freeradius freeradius-config
$ docker kill freeradius-server
  • 설정 디렉토리 구조(수정 불필요 파일은 생략..)
.
├── certs
│   ├── Makefile
│   ├── README
│   ├── ca.cnf
│   ├── client.cnf
│   ├── dh
│   ├── inner-server.cnf
│   ├── passwords.mk
│   └── server.cnf
├── clients.conf
├── mods-available
│   ├── ...
│   └── python
├── mods-config
│   └── python
│       ├── example.py
│       └── radiusd.py
├── mods-enabled
│   ├── ...
│   └── utf8
├── radiusd.conf
├── sites-enabled
│   ├── default
│   └── inner-tunnel
├── templates.conf
├── trigger.conf
└── users

2. 인증서 생성(Wifi 인증)

  • Wifi 인증EAP-TLS, EAP-TTLS 등을 사용해야 할 경우 필요합니다. 아닐 경우 다음 단게로…
  • 추출한 설정 디렉토리중 certs/README를 참조해서 진행합니다.
  • 추가로 이곳도 참조하시면 좋습니다.
  • 먼저 기존 파일을 제거합니다. (아래 작업들은 cert 디렉토리에서 수행합니다.)
$ rm -f *.pem *.der *.csr *.crt *.key *.p12 serial* index.txt*
  • ca.cnf, server.cnf, client.cnf를 열고 수정합니다.(저는 아래 항목들을 수정했습니다.)
    • default_days: 인증서 유효기간
    • countryName ~ commonName: 인증서에 포함시킬 정보
    • input_password & output_password: 인증서 암호
$ make ca.pem
$ make ca.der
$ make server.pem
$ make server.csr
$ make client.pem
$ openssl dhparam -check -text -5 4096 -out dh   # 오래 걸릴 수 있음... 몇분~몇십분

3. FreeRADIUS 기본 설정

  • ... 부분은 기본 설정 유지한 부분.
  • client.conf
    • SHARED_SECRET 실행시 환경변수로 지정합니다.
client localhost {
  ...
	secret = $ENV{SHARED_SECRET}
  ...
}
  • sites-enabled/default
    • 기본 리퀘스트 처리를 위한 설정.
    • eap 처리 설정 + authorize와 authenticate 단계에서 python 모듈을 사용하도록 추가
    • authorize 단계에서 실행된 python script에서 Auth-Type=KEYCLOAK를 설정 할 예정.
server default {

    authorize {
        ...
        eap {
            ok = return
            updated = return
        }
        ...
        python
    }

    authenticate {
        ...
        Auth-Type KEYCLOAK {
            python
        }
        ...
        eap
    }
    
...
}
  • sites-enabled/inner-tunnel
    • mods-enabled/eap에서 EAP-TTLS의 virtual-server로 inner-tunnel이 설정됨.
    • 들어온 RADIUS요청이 EAP-TTLS일 경우 sites-enabled/default에서 eap 관련 처리가 되고 inner-tunnel로 전달 됨.
server inner-tunnel {
    authorize {
        ...
        pap
        python
    }

    authenticate {
        Auth-Type KEYCLOAK {
            python
        }
        ...
        eap
    }
}
  • mods-enabled/eap
    • default_eap_typettls(EAP-TTLS)로 지정.
    • tls, ttls 관련 설정을 빼고 다른 EAP Type 들은 주석처리.
    • private_key_password는 인증서 생성시에 만들때 쓴 암호 입력.
eap {
    ...
	default_eap_type = ttls
	ignore_unknown_eap_types = yes

	#md5 {
	#}

	#pwd {
	#}

	#leap {
	#}

	#gtc {
	#}

	tls-config tls-common {
		private_key_password = <private_key_password>
        ...
	}


	tls {
        ...
		tls = tls-common
        ...
	}

	ttls {
        ...
		tls = tls-common
        ...
		virtual_server = "inner-tunnel"
        ...
	}

	#peap {
	#}

	#mschapv2 {
	#}

	#fast {
	#}
}
  • mods-enabled/python
    • python 모듈 실행 설정
    • python_path:로 구분해서 참조 할 모듈의 path를 지정.
    • command line에서 python을 실행하는 경우 기본 python module 및 추가 설치된 module 들의 path가 기본 설정된 채로 실행되지만 FreeRADIUS에서 python을 실행해 줄때는 그렇지 않으므로 스크립트에서 사용 할(import 하는..) 모듈 들이 있는 path를 추가해 주어야함.
    • Dockerfile에서 이미지 생성시 추가하는 모듈들의 docker image 내 경로를 확인해서 추가해 주어야함.
    • 아래는 pycrypto가 설치된 경로를 추가한 설정임. (아래 Docker file 참조)
python {
	python_path="/usr/lib/python2.7/:/usr/local/lib/python2.7/dist-packages:${modconfdir}/python/:/usr/lib/python2.7/lib-dynload/"
	module = keycloak
	pass_all_vps = no
	pass_all_vps_dict = yes

    ...

	mod_authorize = ${.module}
	func_authorize = authorize

	mod_authenticate = ${.module}
	func_authenticate = authenticate

    ... 
}
  • mods-config/python/keycloak.py
    • 원래 해당 디렉토리에는 radiusd.py / example.py 두개 파일이 있습니다.
    • radiusd.py는 반환값 상수 및 로그 출력을 위한 함수가 선언되어 있습니다.(자동생성된 파일로 수정해도 반영되지 않습니다.)
    • example.py -> keycloak.py로 변경(위 mods-enabled/python 파일에서도 미리 변경해두었음.)
    • 각 단계별 함수에 넘어오는 ptuple이며, 일반적으로 (('User-Name', 'ruinnel'), ('User-Password', 'password'), ('NAS-Identifier', 'client-identifier'), ...)으로 전달 됩니다.
    • 인증 방식이 PAP가 아닐 경우 User-Password는 전달되지 않습니다.
    • authorize단계에서는 username / password가 있는 경우 Auth-Type=KEYCLOAK으로 설정하며, authenticate단계에서 실제 인증을 처리.
      • sites-enabled 설정의 authenticate에서 Auth-Type 분기처리가 되어있어서 username / password가 없으면 authenticate 함수는 호출되지 않음.
    • 실제 인증처리 로직은 keycloak API 참고 하셔서 구현하시면 됩니다.
      • 회사에서 작업한 소스를 다 깔 순 없잖아요 ㅎㅎ
      • 꼭 keycloak과 연동 안하고 python으로 어떤 처리든 하고 결과만 반환하면 되니 원하는 서버 혹은 DB 베이스로 인증처리를 하시면 됩니다.
#! /usr/bin/env python2

import radiusd
...

def authorize(p):
 username = getUserName(p)
 password = getUserPassword(p)
 if username and password:
   # tuple로 반환시 `인증결과, response, FreeRADIUS 내부 처리 변수` 순으로 반환 가능
   return (
     radiusd.RLM_MODULE_UPDATED,
     (),
     (('Auth-Type', "KEYCLOAK"),)
   )
 else :
   # 처리 결과 반환
   return radius.RLM_MODULE_REJECT

def authenticate(p):
 username = getUserName(p)
 password = getUserPassword(p)
 nasId = getNasIdentifier(p)
 
 # ...<인증 처리>...

 if authResult :
   return radiusd.RLM_MODULE_OK
 else :
   return radiusd.RLM_MODULE_REJECT

...

def getNasIdentifier(p) :
 for key, val in p :
   if key.upper() == "NAS-Identifier".upper():
     return val
 return ""

def getUserName(p) :
 for key, val in p :
   if key.upper() == "User-Name".upper():
     return val
 return ""

def getUserPassword(p) :
 for key, val in p :
   if key.upper() == "User-Password".upper():
     return val
 return ""

4. Docker Image 생성

  • Docker Hub에 공개되어 있는 이미지를 베이스.
    1. python 설치
    2. python 추가 모듈 설치(아래 예시는 pycrypto, pycrypto는 설치시 빌드가 필요해서 build-essential도 설치했다가 설치 후 제거)
    3. 위에서 수정한 설정파일들을 설정 디렉토리(/etc/raddb) 아래에 덮어씀.
FROM freeradius/freeradius-server:3.0.17

RUN \
  apt-get update --force-yes -y && \
  apt-get install -o Dpkg::Options::="--force-confold" --force-yes -y python python-dev curl build-essential

RUN curl -o pycrypto-2.6.1.tar.gz https://ftp.dlitz.net/pub/dlitz/crypto/pycrypto/pycrypto-2.6.1.tar.gz
RUN tar -xzf pycrypto-2.6.1.tar.gz
RUN rm -rf pycrypto-2.6.1.tar.gz
WORKDIR /pycrypto-2.6.1
RUN python setup.py install
WORKDIR /
RUN rm -rf pycrypto-2.6.1

RUN apt-get remove --force-yes -y --auto-remove build-essential
RUN apt-get purge --force-yes -y --auto-remove build-essential

COPY ./freeradius-config/radiusd.conf /etc/raddb/radiusd.conf
COPY ./freeradius-config/clients.conf /etc/raddb/clients.conf
COPY ./freeradius-config/sites-enabled/default /etc/raddb/sites-enabled/default
COPY ./freeradius-config/sites-enabled/inner-tunnel /etc/raddb/sites-enabled/inner-tunnel
COPY ./freeradius-config/mods-enabled/eap /etc/raddb/mods-enabled/eap
COPY ./freeradius-config/mods-enabled/python /etc/raddb/mods-enabled/python
COPY ./freeradius-config/certs /etc/raddb/certs
COPY ./freeradius-config/mods-config/python /etc/raddb/mods-config/python

WORKDIR /etc/raddb/mods-config/python
RUN rm -rf *.pyc

WORKDIR /
  • docker build & push
$ docker build -t ruinnel/radius-server:0.9.1 .
$ docker push -t ruinnel/radius-server:0.9.1

설치

  • docker-compose를 이용해 설치 하였습니다. (Kubernetes에 올리려 하였으나… 이런저런 사정상 docker-compose로 진행 회사에서 다른 서버에 설치 하래요)
  • docker-compose.yml 예시
version: "3"

services:
  radius-server:
    container_name: free-radius-server
    image: ruinnel/radius-server:0.9.1
    env_file:
      - ./config.env
    ports:
      - 31812:1812/udp
      - 31813:1813/udp
    volumes: []
      #- ./logs:/var/log/freeradius
  • config.env 예시
SHARED_SECRET=<my-shared-secret>
  • 설치 (docker-compose.yml, config.env가 있는 경로에서 실행)
$ docker-compose up -d

정리

  • 위에서 처리한 내용을 간단하게 정리하면 아래와 같습니다.
    1. FreeRADIUS에서는 authorize, authorize등 각 단계별로 사용 가능한 여러가지 모듈들이 있는데 그중 python 모듈을 사용하기 위한 설정 방법.
    2. Python 모듈을 사용 할 때 추가 python module 사용을 위한 설정 방법.
    3. Python 스크립트 작성 가이드.
    4. Docker Image 생성 및 docker-compose.yml 예시.

관련글 목록

1. Keycloak를 이용한 SSO 구축

2. Keycloak 설치하기(with Kubernetes)

3. RADIUS 서버 설치 및 Keycloak 연동 설정

4. Linux 서버의 SSH(+ sudo) 인증 설정

5. EAP-TTLS & PAP 접속 설정

번외. Custom Linux PAM을 이용해 Keycloak 계정으로 ssh, sudo 인증 하기

번외. TinyRadius로 RADIUS 서버 만들기(feat. netty)

comments powered by Disqus