Freeradius With Okta Ldap (assign VLAN with OKTA attributes)

0. 개요

802.1x 프로젝트의 목표는 네트워크 인증시 조직정보를 기반으로 지정된 IP pool 을 할당해주는 것입니다.
이를 위해서 radius 서버가 할 일은

  • 인증 시 AD, okta 등에서 조직정보를 가져와 확인하고
  • 해당 정보를 기반으로 vlan 변경에 필요한 radius attribute 를 스위치에 전달하는 것

입니다.

radius 가 AD 에 대한 유저정보를 가져오기 위해선 LDAP 프로토콜을 사용하여야 합니다.
옥타도 LDAP interface 를 지원하므로, 옥타 LDAP 을 통해 radius 가 직접 유저정보를 가져오고,
인증을 LDAP 에 위임함으로써 옥타 비밀번호를 통해 인증할 수 있습니다. (AD 불필요)

본 문서는 Freeradius 를 통해 인증 시 옥타 LDAP 으로 인증을 대신하고,
유저정보를 통해 VLAN 을 할당하는 방법에 대해 기술한 문서입니다.

1. 배경지식

1-0. 전제

Okta Radius Agent and Authentication Protocols (AAA) AAA 구조

  • supplicant : 사용자 기기, Authenticator : 스위치 , Authenticaion Server : Radius server

이전의 테스트를 통해, Radius Server 가 보내는 응닶값(Access-Accept 패킷) 중

Tunnel-Type = VLAN,
Tunnel-Medium-Type = IEEE-802,
Tunnel-Private-Group-ID = "17"

이 포함되면, Tunnel-Private-Group-ID 값 대로 switch 가
supplicant 요청포트의 VLAN 을 변경함을 확인하였습니다.

1-1. OKTA LDAP Interface

What Is LDAP & How Does It Work? | OKTA

OKTA LDAP Interface 는 Organization 당 하나만 활성화할 수 있습니다.

옥타에서 테스트유저 test@test.com 를 생성하고,

다음과 같이 ldapsearch cli 로 uid 를 기반으로 질의를 하면

$ ldapsearch -H ldaps://{your_domain}.ldap.okta.com:636 \
-D "uid=freeradius@test.com,ou=users,dc=cio-sso,dc=okta,dc=com" \
-W -v -b dc={your_domain},dc=okta,dc=com uid="test@test.com"
ldap_initialize( ldaps://{your_domain}.ldap.okta.com:636/??base )
Enter LDAP Password:
filter: uid=test@test.com
requesting: All userApplication attributes
# extended LDIF
#
# LDAPv3
# base <dc=cio-sso,dc=okta,dc=com> with scope subtree
# filter: uid=test@test.com
# requesting: ALL
#

# test@test.com, users, cio-sso.okta.com
dn: uid=test@test.com,ou=users,dc=cio-sso,dc=okta,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
uid: test@test.com
uniqueIdentifier: 00uoyjiu7vq6NCf8h697
organizationalStatus: ACTIVE
givenName: test
sn: test
cn: test test
mail: test@test.com
division: test_div
displayName: test
o: test_org
title: Crew
department: test_department

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

User profile 정보를 불러올 수 있음을 알 수 있습니다. 테스트에서는 division attribute 를 vlan 할당에 사용해 보겠습니다.

FYI :

  • 질의는 okta read only admin 이상 권한자만 가능합니다.
  • password 입력시, {password},push {password},{6자리 otp} 를 넣어 mfa 를 수행할 수 있습니다.

2. FreeRadius 설정

2-0. FreeRadius LDAP 설정 - PAP

https://www.freeradius.org/documentation/freeradius-server/3.2.7/concepts/modules/ldap/authentication.html

ldap 모듈은 radius 인증에 여러 방식을 지원하지만, 옥타 LDAP Interface 는 User-Password 를 주지 않으므로,
Supplicant 에게서 PW 를 제공받아 Ldap 에 인증을 위임(ldap bind as user)하는 PAP 방식을 사용하여야 합니다.

이때 Radius Server 는 유저와 PW 정보를 저장하지 않습니다.

로컬 맥에서 freeradius 를 올리고, radclient 를 통해 PAP 인증을 테스트해봅시다.

## 설치
$ brew install freeradius-server
$ cd /opt/homebrew/Cellar/freeradius-server/3.2.7/etc/raddb/

## ldap module 활성화
$ cd mods-enabled; ln -s ../mods-available/ldap

mods-available/ldap 설정파일 :

ldap {
    # okta ldap interface, 질의 수행할 ldap 유저 정보, 
    # 해당 유저는 패스워드만으로 로그인 가능해야 함
+	server = 'ldaps://{your_domain}.ldap.okta.com'
+	identity = 'uid=freeradius@test.com,ou=users,dc=cio-sso,dc=okta,dc=com'
+	password = Test123!@#

+	base_dn = 'dc=cio-sso, dc=okta, dc=com'

	sasl { # 필요없음
	}
    
    # 해당 구문을 통해 LDAP Attribute 를 Radius Attribute 에 매핑할 수 있습니다.
    # 구문순서 : <radius attr> <op (=, :=, +=, -=)> <ldap attr>
    # Radius Attribute list : https://www.iana.org/assignments/radius-types/radius-types.xhtml
+	update {
+		# Reply-Message 에 division 정보를 추가합니다. (위 예시의 값은 Platform)
+		reply:Reply-Message := 'division'
+	}



    # 여기서부터는 기존파일 변경필요 X
	user_dn = "LDAP-UserDn"

	user {
		base_dn = "${..base_dn}"
        # 해당 필터 기반으로 ldap 에서 유저를 검색합니다.
        # uid=Stripped-User-Name 존재시 Stripped-User-Name, 없을시 User-Name
		filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"

		sasl {
		}
	}
...

sites-available/default 설정파일 :

authorize {
...
-# -ldap 
+ -ldap 
	#  ldap bind user 로 옥타를 통해 인증하기 위해 다음을 추가합니다
	#  If you're using Active Directory and PAP, then uncomment
	#  the following lines, and the "Auth-Type LDAP" section below.
	#
	#  This will let you do PAP authentication to AD.
	#
-	#if ((ok || updated) && User-Password && !control:Auth-Type) {
-	#	update control {
-	#		&Auth-Type := ldap
-	#	}
-	#}
+	if ((ok || updated) && User-Password && !control:Auth-Type) {
+		update control {
+			&Auth-Type := ldap
+		}
+	}
...

#  Post-Authentication
#  Once we KNOW that the user has been authenticated, there are
#  additional steps we can take.
post-auth {
+ # 인증성공시, Reply-Message 를 기반으로 VLAN 할당에 필요한 Attribute 을 추가합니다
+ if (reply:Reply-Message == "test_div") {
+     update reply {
+         Tunnel-Type := "VLAN"
+         Tunnel-Medium-Type := "IEEE-802"
+         Tunnel-Private-Group-ID := "17"
+     }
+ }

...

✅ radclient 를 통한 테스트

$ radiusd -X : 라디우스 서버 디버그모드 실행


# radclient [options] server[:port] <command> [<secret>]
#시크릿은 테스트용, raddb/clients.conf client 안에 secret 명시가능
$ radclient -f test -t 20 127.0.0.1:1812 auth testing123

-----
test 파일(supplicant 의 Auth-Request) : 
User-Name=test@test.com
User-Password="QWEqwe123@"

User-Password="testPassword,push" 쓰면 okta verify push mfa 도 가능함

결과 :

result

  • devision attr 에 따라 Tunnel-Private-Group-Id 할당 성공

2-1. FreeRadius LDAP 설정 - EAP-TTLS

맥은 raw PAP 방식을 지원하지 않으므로, EAP-TTLS 로 PAP 요청을 감싸야 합니다.

필요 추가설정 :

  1. EAP-TTLS 는 인증을 수행할 supplicant 에 Radius 서버의 Root CA certificate 이 필요합니다.

    freeradius/certs/README.md : The `openssl` command will be run against the sample configuration files included here, and will make a self-signed certificate authority (i.e. root CA), and a server certificate. This "root CA" should be installed on any client machine needing to do EAP-TLS, PEAP, or EAP-TTLS.

  2. 라디우스 서버에서 LDAP으로의 실제 인증시도는 EAP-TTLS 의 inner tunnel (PAP) 에서 이루어지기 때문에,
    sites-available/default 가 아니라 sites-available/inner-tunnel 에서
    post-auth 를 수행하여야 합니다.

    AAA struct

Jamf Profile (for cert) :

테스트 supplicant 로 맥을 사용하기 때문에,
맥에 802.1x Configuration Profile 을 배포하기 위해 jamf 를 사용합니다.
freeradius 의 CA 인증서는 /etc/freeradius/certs/ca.* 에 있습니다.

ca.cnf : ca 비밀번호 확인

[ req ]
prompt			= no
distinguished_name	= certificate_authority
default_bits		= 2048
input_password		= whatever
output_password		= whatever
x509_extensions		= v3_ca

jamf configuration profile 생성, certificates 에 /etc/freeradius/certs/ca.der 를 추가하고,
비밀번호를 ca.cnf 의 값대로 설정합니다. jamf1

Network 에서 802.1x 인증시 사용할 프로토콜을 EAP-TTLS, Inner Authentication 은 PAP로 설정합니다. jamf2

이후 scope 를 테스트 기기로 하여 configuration profile 을 배포합니다.

sites-available/default 설정파일 :

...

#  Post-Authentication
#  Once we KNOW that the user has been authenticated, there are
#  additional steps we can take.
post-auth {
  # 인증성공시, Reply-Message 를 기반으로 VLAN 할당에 필요한 Attribute 을 추가합니다
- if (reply:Reply-Message == "test_div") {
-     update reply {
-         Tunnel-Type := "VLAN"
-         Tunnel-Medium-Type := "IEEE-802"
-         Tunnel-Private-Group-ID := "17"
-     }
- }

...

1-1 번 테스트에서 추가한 부분을 삭제합니다.

sites-available/inner-tunnel 설정파일 :

authorize {
...
-# -ldap 
+ -ldap 
	#  ldap bind user 로 옥타를 통해 인증하기 위해 다음을 추가합니다
	#  If you're using Active Directory and PAP, then uncomment
	#  the following lines, and the "Auth-Type LDAP" section below.
	#
	#  This will let you do PAP authentication to AD.
	#
-	#if ((ok || updated) && User-Password && !control:Auth-Type) {
-	#	update control {
-	#		&Auth-Type := ldap
-	#	}
-	#}
+	if ((ok || updated) && User-Password && !control:Auth-Type) {
+		update control {
+			&Auth-Type := ldap
+		}
+	}
...

#  Post-Authentication
#  Once we KNOW that the user has been authenticated, there are
#  additional steps we can take.
post-auth {
+ # 인증성공시, Reply-Message 를 기반으로 VLAN 할당에 필요한 Attribute 을 추가합니다
+ if (reply:Reply-Message == "test_div") {
+     update reply {
+         Tunnel-Type := "VLAN"
+         Tunnel-Medium-Type := "IEEE-802"
+         Tunnel-Private-Group-ID := "17"
+     }
+ }

	######  중요! inner-tunnel 의 reply 를 outer 로 업데이트하여,
    ######  최종적으로 supplicant 에 전달하기 위해 다음이 반드시 필요합니다.
	#  Instead of "use_tunneled_reply", change this "if (0)" to an
	#  "if (1)".
	#
-	if (0) {
+	if (1) {
		#
		#  These attributes are for the inner-tunnel only,
		#  and MUST NOT be copied to the outer reply.
		#
		update reply {
			User-Name !* ANY
			Message-Authenticator !* ANY
			EAP-Message !* ANY
			Proxy-State !* ANY
			MS-MPPE-Encryption-Types !* ANY
			MS-MPPE-Encryption-Policy !* ANY
			MS-MPPE-Send-Key !* ANY
			MS-MPPE-Recv-Key !* ANY
		}

		#
		#  Copy the inner reply attributes to the outer
		#  session-state list.  The post-auth policy will take
		#  care of copying the outer session-state list to the
		#  outer reply.
		#
		update {
			&outer.session-state: += &reply:
		}
	}

...

✅ Mac Supplicant 에서의 테스트

result-freeradlog

  • OKTA 테스트 계정 test@test.com 으로 인증을 수행 성공

FreeRadius Log

result-switch1

  • post-auth if문 대로 VLAN 변경에 필요한 attribute 이 전달됨

스위치에서 VLAN 변경 확인

result-switch2 해당 인터페이스의 vlan 이 17로 성공적으로 변경되었음을 알 수 있습니다~!