이미 Okta를 IdP로 사용하고 있다면, RADIUS Agent를 통해 SSH 접속에 Okta MFA를 그대로 활용할 수 있다. 별도 RADIUS 서버를 구축할 필요 없이 Agent 하나만 Docker로 올리면 된다. 이 글에서는 Okta 콘솔 설정부터 Docker 구성, PAM 연동, 점진적 전환 전략까지 한 편에 정리한다.
왜 Okta + RADIUS인가
1편에서 SSH MFA 방식을 분류했고, 2편에서는 Google Authenticator, 3편에서는 Duo Security를 다뤘다. 각각 좋은 선택이지만, 이미 조직에서 Okta를 IdP로 사용하고 있다면 이야기가 달라진다.
- 사용자들이 이미 Okta Verify를 등록해둔 상태다. 추가 앱 설치나 등록이 불필요하다.
- 사용자 추가/삭제, MFA 정책, 접근 제어를 Okta 콘솔 하나에서 관리할 수 있다.
- 퇴사자 처리 시 Okta에서 계정을 비활성화하면 모든 서버 접근이 동시에 차단된다.
- 서버가 늘어나도 PAM 설정만 추가하면 된다. 서버마다 별도 모듈이나 API 키를 배포할 필요가 없다.
Duo와의 핵심 차이는 구조에 있다. Duo는 서버마다 전용 PAM 모듈이 Duo Cloud를 직접 호출하지만, Okta RADIUS 방식은 중간에 Agent가 인증을 중계한다. 서버 쪽에는 범용 pam_radius_auth 모듈만 있으면 되고, IdP와의 연동은 Agent가 담당한다.
Duo 방식: [서버 A] → Duo Cloud
[서버 B] → Duo Cloud ← 서버마다 API 키 필요
[서버 C] → Duo Cloud
RADIUS 방식: [서버 A] ─┐
[서버 B] ─┼→ [Agent] → Okta Cloud
[서버 C] ─┘ ← 서버에는 PAM 설정만
서버가 늘어날수록 RADIUS 방식의 관리 효율이 좋아진다.
전체 구조
[사용자 PC] → SSH → [리눅스 서버]
│ pam_radius_auth
│ UDP 1812
↓
[Okta RADIUS Agent] ← Docker 컨테이너
│ HTTPS (TCP 443)
↓
[Okta Cloud]
│
↓
[핸드폰 - Okta Verify 푸시 알림]
구성 요소는 세 가지다.
| 구성 요소 | 역할 | 위치 |
|---|---|---|
| pam_radius_auth | SSH 인증 요청을 RADIUS로 전달 | 리눅스 서버 |
| Okta RADIUS Agent | RADIUS ↔ Okta Cloud 중계 | Docker 컨테이너 |
| Okta Cloud | 비밀번호 검증 + MFA 수행 | Okta SaaS |
RADIUS Agent는 별도 서버에 둘 수도 있고, SSH 서버 자체에 Docker로 올릴 수도 있다. 서버 1대라면 같은 서버에 올리는 것이 가장 간단하다. 서버가 여러 대로 늘어나면 Agent를 별도 서버로 분리하는 것을 권장한다.
Part 1. Okta 콘솔 설정
Step 1. RADIUS Application 생성
- Okta Admin Console 접속
- Applications → Browse App Catalog
RADIUS Application검색 → Add Integration- Application label 입력 (예:
Linux SSH MFA) - Save
주의: “Create a new app integration” 버튼이 아니라 **”Browse App Catalog”**에서 검색해야 한다. RADIUS Application은 Okta가 미리 만들어둔 카탈로그 앱이다.
Step 2. Sign On 설정
생성된 RADIUS App → Sign On 탭에서:
| 항목 | 설정값 | 비고 |
|---|---|---|
| Authentication | 체크 | Okta가 비밀번호 검증 수행 |
| UDP Port | 1812 | 기본 RADIUS 포트 |
| Secret Key | (강력한 랜덤 문자열) | 최대 16자, PAM 설정과 동일해야 함 |
| Application username format | Email prefix | user@company.com → user로 매칭 |
Application username format은 반드시 확인해야 한다. 기본값인 Okta username은 이메일 형식(user@company.com)을 그대로 사용하므로, 리눅스 사용자명(user)과 불일치하여 인증이 실패한다. Email prefix로 설정하면 @ 앞부분만 사용한다.
Step 3. 사용자/그룹 할당
RADIUS App → Assignments 탭에서 SSH 접속을 허용할 사용자 또는 그룹을 할당한다.
여기에 할당되지 않은 사용자는 RADIUS 인증 자체가 거부된다. 접근 제어가 Okta 콘솔에서 이루어지므로, 서버별로 접근 권한을 관리할 필요가 없다.
Step 4. MFA 정책 확인
Security → Authentication Policies에서 RADIUS App에 적용할 정책을 확인한다. Okta Verify Push가 필수로 설정되어 있는지 확인한다.
Step 5. RADIUS Agent 패키지 다운로드
- Settings → Downloads
- Okta RADIUS Server Agent 항목에서 Linux
.deb패키지 다운로드 - SHA-512 해시값을 기록해둔다 (무결성 검증용)
Part 2. Docker로 RADIUS Agent 구성
Step 6. 디렉토리 구조 생성
Agent를 올릴 서버에서 작업한다.
mkdir -p ~/okta-radius-agent/{package,config,logs}
cd ~/okta-radius-agent
Step 5에서 다운로드한 .deb 파일을 package/ 디렉토리에 복사한다.
# 예시
scp OktaRadiusAgentSetup-x.xx.x.deb user@server:~/okta-radius-agent/package/
# 무결성 검증
sha512sum package/OktaRadiusAgentSetup-*.deb
Step 7. Dockerfile 작성
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y \
curl ca-certificates libicu70 sudo procps net-tools \
&& rm -rf /var/lib/apt/lists/*
COPY package/OktaRadiusAgentSetup-*.deb /tmp/okta-radius-agent.deb
# dpkg --unpack: 패키지 풀기만 수행 (대화형 post-install 스크립트 건너뜀)
RUN dpkg --unpack /tmp/okta-radius-agent.deb && rm -f /tmp/okta-radius-agent.deb
EXPOSE 1812/udp
WORKDIR /opt/okta/ragent
CMD ["/opt/okta/ragent/scripts/okta_radius_agent.sh"]
dpkg -i 대신 dpkg --unpack을 사용하는 이유가 있다. dpkg -i는 설치 후 대화형 설정 스크립트(configure.sh)를 자동 실행하는데, Docker 빌드 중에는 대화형 입력을 받을 수 없어 빌드가 멈춘다. --unpack으로 파일만 풀어놓고, 초기 설정은 이후 별도로 진행한다.
Step 8. docker-compose.yml 작성
services:
okta-radius-agent:
build:
context: .
dockerfile: Dockerfile
container_name: okta-radius-agent
restart: unless-stopped
ports:
- "${RADIUS_PORT:-1812}:1812/udp"
volumes:
- ./config:/opt/okta/ragent/user/config/radius
- ./logs:/opt/okta/ragent/logs
environment:
- TZ=${TZ:-Asia/Seoul}
healthcheck:
test: ["CMD", "ss", "-uln", "|", "grep", "1812"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "5"
볼륨 마운트가 핵심이다. config/에 Okta 연결 설정이 저장되므로, 컨테이너를 재생성해도 재등록 없이 기존 설정을 그대로 사용할 수 있다.
Step 9. 환경변수 파일
cat > .env <<EOF
RADIUS_PORT=1812
TZ=Asia/Seoul
EOF
Step 10. 이미지 빌드
docker compose build
Step 11. 초기 설정 (최초 1회)
Agent를 Okta에 등록하는 과정이다. 대화형으로 진행되므로 별도 컨테이너에서 실행한다.
# 설정용 컨테이너 실행
docker run -it --name okta-setup okta-radius-agent-okta-radius-agent bash
컨테이너 안에서:
/opt/okta/ragent/scripts/configure.sh
| 프롬프트 | 입력 |
|---|---|
| Okta 도메인 | https://your-org.okta.com |
| 프록시 | No (Enter) |
| 인증 | 표시되는 URL을 브라우저에서 열어 Okta 관리자로 인증 |
인증 URL은 시간 제한이 있으므로, 표시되면 바로 브라우저에서 접속하여 인증한다.
등록이 완료되면 컨테이너에서 나가지 않은 상태로 다른 터미널에서 설정 파일을 복사한다.
# 다른 터미널에서
cd ~/okta-radius-agent
docker cp okta-setup:/opt/okta/ragent/user/config/radius/ ./config/
mv config/radius/* config/ && rm -rf config/radius/
# 암호화 키 파일 복사 (필수)
docker cp okta-setup:/var/lib/ragent/additional-config.properties ./config/
additional-config.properties 파일을 빠뜨리면 Agent가 시작되지 않는다. 이 파일에 Okta 연결 토큰의 암호화 키가 들어있다.
# 설정 파일 확인
ls config/
# config.properties - Okta 연결 정보
# additional-config.properties - 암호화 키
# commons-logging.properties - 로깅 설정
# log4j2.xml - 로깅 설정
# 설정 컨테이너 정리
docker rm okta-setup
이 과정은 최초 1회만 필요하다. 이후 컨테이너 재생성, 업데이트, 재시작 시에는 볼륨에 저장된 설정을 자동으로 사용한다.
Step 12. Agent 컨테이너 실행
docker compose up -d
Step 13. 동작 확인
# 컨테이너 상태
docker compose ps
# 로그 확인 (정상이면 아래와 같은 메시지)
docker compose logs -f
# Okta RADIUS Agent Version: x.xx.x
# Listening on port 1812
# established connection with your-org.okta.com:443
Listening on port 1812과 established connection이 보이면 정상이다.
Part 3. 리눅스 서버 PAM 연동
Agent가 정상 동작하면, MFA를 적용할 리눅스 서버에서 작업한다. Agent와 같은 서버라면 그대로 이어서 진행한다.
Step 14. PAM RADIUS 모듈 설치
# Ubuntu/Debian
sudo apt install libpam-radius-auth
# RHEL/CentOS
sudo yum install pam_radius
Step 15. RADIUS 클라이언트 설정
sudo vi /etc/pam_radius_auth.conf # Ubuntu/Debian
# 또는
sudo vi /etc/pam_radius.conf # RHEL/CentOS
# server[:port] shared_secret timeout (s)
127.0.0.1 YourSecretKey 60
127.0.0.1: Agent가 같은 서버에 있으면 localhost. 별도 서버라면 해당 IP.YourSecretKey: Step 2에서 설정한 Okta RADIUS App의 Secret Key와 동일해야 함.60: 타임아웃. 푸시 알림 대기 시간을 고려하여 60초 권장.
sudo chmod 600 /etc/pam_radius_auth.conf
Step 16. PAM SSH 설정
⚠️ 필수: 반드시 별도 SSH 세션을 유지한 채 작업한다.
sudo vi /etc/pam.d/sshd
가장 단순한 형태:
auth required pam_radius_auth.so
그룹 기반 분기: 기존 인증과 공존
기존 사용자는 종래 방식(비밀번호, SMS OTP 등)을 유지하면서, 특정 그룹 사용자만 Okta MFA를 적용하고 싶을 때가 있다. PAM의 pam_succeed_if 모듈로 분기를 만들 수 있다.
# Okta MFA 대상 그룹 생성
sudo groupadd okta-mfa
sudo usermod -aG okta-mfa username
PAM 설정:
auth [success=1 default=ignore] pam_succeed_if.so user notingroup okta-mfa
auth required pam_radius_auth.so
auth [success=1 default=ignore] pam_succeed_if.so user ingroup okta-mfa
auth substack password-auth
이 설정의 분기 동작:
| 순서 | 설정 | okta-mfa 사용자 | 일반 사용자 |
|---|---|---|---|
| 1 | user notingroup okta-mfa | 실패 → 다음으로 | 성공 → 1줄 건너뜀 |
| 2 | pam_radius_auth | 실행 (Okta 인증) | 건너뜀 |
| 3 | user ingroup okta-mfa | 성공 → 1줄 건너뜀 | 실패 → 다음으로 |
| 4 | password-auth | 건너뜀 | 실행 (기존 인증) |
okta-mfa 그룹 사용자는 Okta로 인증하고, 나머지는 기존 방식을 그대로 사용한다. 전환이 완료되면 분기를 제거하고 auth required pam_radius_auth.so 한 줄로 정리한다.
Step 17. SSH 데몬 설정
sudo vi /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
KbdInteractiveAuthentication yes
UsePAM yes
Step 18. SSH 재시작 및 테스트
sudo systemctl restart sshd
새 터미널에서 접속:
$ ssh user@server
Password: ← Okta 비밀번호 입력
Enter a passcode to continue.
To verify with something else enter:
'1' for Okta Verify Push.
Enter '0' to abort.
1 ← '1' 입력 → 푸시 알림 발송
← 핸드폰에서 Okta Verify 승인
user@server:~$ ← 로그인 성공
Okta Verify TOTP 코드를 직접 입력할 수도 있다.
Enter a passcode to continue.
...
582693 ← Okta Verify 앱의 6자리 코드
user@server:~$ ← 로그인 성공
이전 방식들과의 비교
| 항목 | Google Authenticator | Duo | Okta RADIUS |
|---|---|---|---|
| 사용자 등록 | 서버마다 개별 | Duo 콘솔 | Okta 콘솔 (기존 등록 활용) |
| 서버 추가 시 | 서버별 사용자 등록 | 서버별 모듈+API 키 | PAM 설정만 추가 |
| 중간 인프라 | 없음 | 없음 | Agent 1대 |
| IdP 통합 | ❌ | Duo 단독 | ✅ 기존 Okta 통합 |
| 퇴사자 처리 | 서버마다 수동 | Duo 콘솔 | Okta에서 비활성화 → 전체 차단 |
| 접근 제어 정책 | 없음 | Duo 정책 | Okta 정책 (조건부 접근 등) |
| 비용 | 무료 | 10명↓ 무료 | Okta MFA 라이선스 |
Okta를 이미 쓰고 있다면 추가 라이선스 비용 없이(기존 MFA 라이선스 활용) 가장 관리 효율이 좋은 방식이다. Okta를 쓰지 않는다면 비용 대비 효과를 따져봐야 한다.
Docker 운영
Agent 업데이트
cd ~/okta-radius-agent
# 1. 새 .deb 패키지를 package/에 복사
# 2. 이미지 재빌드
docker compose build --no-cache
# 3. 컨테이너 교체 (설정은 볼륨에 보존됨, 재등록 불필요)
docker compose down && docker compose up -d
# 4. 동작 확인
docker compose logs -f
백업
설정 파일과 Compose 파일만 백업하면 된다. 이것만 있으면 어디서든 복구할 수 있다.
tar czf okta-radius-backup-$(date +%Y%m%d).tar.gz \
docker-compose.yml Dockerfile .env config/
서버 추가
새 리눅스 서버에 MFA를 추가할 때는 Part 3(Step 14~18)만 반복하면 된다. Agent 서버나 Okta 콘솔에서 추가 작업은 불필요하다.
1. libpam-radius-auth 설치
2. pam_radius_auth.conf에 Agent 서버 IP + Secret Key 설정
3. /etc/pam.d/sshd에 pam_radius_auth 추가
4. sshd_config 확인
5. sshd 재시작
6. 테스트
Agent 서버 분리 (서버 확장 시)
서버가 늘어나면 Agent를 별도 서버로 분리하는 것을 권장한다. 변경사항은 각 리눅스 서버의 pam_radius_auth.conf에서 Agent IP를 127.0.0.1에서 Agent 서버 IP로 바꾸는 것뿐이다.
# 변경 전 (같은 서버)
127.0.0.1 YourSecretKey 60
# 변경 후 (별도 서버)
10.0.1.50 YourSecretKey 60
Agent 서버의 방화벽에서 UDP 1812를 허용해야 한다.
# Agent 서버에서
sudo ufw allow from 10.0.1.0/24 to any port 1812 proto udp
고가용성이 필요하면 Agent를 2대 이상 운영하고, PAM 설정에 둘 다 지정한다.
10.0.1.50 YourSecretKey 60 # Primary
10.0.1.51 YourSecretKey 60 # Secondary
PAM이 자동으로 Primary 실패 시 Secondary로 시도한다.
점진적 전환 전략
기존 인증 방식에서 Okta MFA로 전환할 때는 단계적으로 진행하는 것이 안전하다.
1단계: 테스트 사용자만 적용
okta-mfa 그룹에 관리자/테스트 계정만 추가하고, 그룹 기반 분기 PAM 설정을 적용한다. 나머지 사용자는 기존 방식으로 로그인한다.
2단계: 사용자 점진적 추가
Okta Verify 등록이 완료된 사용자부터 okta-mfa 그룹에 추가한다.
sudo usermod -aG okta-mfa username
그룹 추가만으로 즉시 Okta MFA가 적용된다. 문제가 있으면 그룹에서 제거하여 즉시 롤백할 수 있다.
sudo gpasswd -d username okta-mfa
3단계: 전체 전환 완료
모든 사용자가 okta-mfa 그룹으로 이동하면, 그룹 분기를 제거하고 단순화한다.
auth required pam_radius_auth.so
기존 인증 방식(SMS OTP 등)을 비활성화하고, Okta MFA만 남긴다.
트러블슈팅
MFA 프롬프트가 안 뜸
sshd_config에서 ChallengeResponseAuthentication yes와 KbdInteractiveAuthentication yes를 확인한다.
Access-Reject / 인증 실패
- Okta RADIUS App에 사용자가 할당되어 있는지 확인
- Application username format이
Email prefix인지 확인. 변경 후 사용자를 재할당해야 반영되는 경우가 있다. - Secret Key가 Okta App과 PAM 설정에서 일치하는지 확인
Agent 컨테이너 로그에서 확인
docker compose logs -f
# 정상
# send response: Access-Challenge ← MFA 요청
# send response: Access-Accept ← 인증 성공
# 실패
# send response: Access-Reject ← 인증 거부
RADIUS server failed to respond
리눅스 서버에서 Agent로 UDP 1812 통신이 가능한지 확인한다.
# 패킷 확인
tcpdump -i any -n udp port 1812
SSH 접속이 완전히 차단됨
콘솔로 접속한 뒤 PAM 설정에서 pam_radius_auth 줄을 주석 처리하고 sshd를 재시작한다.
정리
| 항목 | 내용 |
|---|---|
| 소요 시간 | 1~2시간 (초기 설정) |
| 비용 | Okta MFA 라이선스 (기존 보유 시 추가 비용 없음) |
| 별도 서버 | 불필요 (Docker로 같은 서버에 구성 가능) |
| 필요 통신 | Agent → Okta Cloud (HTTPS), 서버 → Agent (UDP 1812) |
| 인증 방식 | Okta Verify Push, TOTP, SMS, 음성 통화 등 |
| 적합 환경 | Okta 사용 조직, 서버 확장 예정, 중앙 관리 필요 |
| 핵심 장점 | 서버 추가 시 PAM 설정만, 기존 Okta 인프라 활용 |
이 시리즈에서 다룬 세 가지 방식을 다시 정리하면:
| 상황 | 추천 |
|---|---|
| 서버 1~2대, 무료로 빠르게 | 2편: Google Authenticator |
| 소규모 팀, 푸시 알림, 추가 인프라 없이 | 3편: Duo Security |
| IdP 사용 조직, 서버 확장, 중앙 관리 | 이 글: Okta + RADIUS |
자신의 환경에 맞는 방식을 선택하면 된다. 어떤 방식을 고르든, PAM 위에서 동작하는 구조는 동일하므로 나중에 방식을 전환하는 것도 어렵지 않다.