Eduroam

RadSecProxy

  • RADIUS 프로토콜을 TLS 기반으로 확장한 RadSec 서비스를 제공하는 프록시
  • RADIUS 요청을 암호화하여 안전하게 전송할 수 있게 해주는 역할
  • 무선 AP 등에서 발생하는 RADIUS 요청을 Radsec을 통해 안전하게 전달하는 중개 서버 역할을 하며, RADIUS 인증 트래픽의 보안 강화에 사용

FreeRADIUS

  • RADIUS 프로토콜을 구현한 오픈소스 AAA 서버로 네트워크 접속 제어를 위한 사용자 인증 및 권한 부여, 계정 정보 관리
  • 주로 VPN, 무선 네트워크, 스위치 등 클라이언트 인증을 담당하며 다양한 인증 메커니즘과 외부 데이터베이스, LDAP 연동 기능 등을 지원

RadSecProxy와 FreeRADIUS의 차이점

  • RadSecProxy는 RADIUS 프로토콜을 TLS로 감싸서 보안 전송을 지원하는 프록시 서버이며, RADIUS 요청을 중계하고 암호화 함
  • FreeRADIUS는 RADIUS 서버로서 사용자 인증과 권한 부여, 계정 관리 기능을 수행하는 인증 서버
  • RadSecProxy는 RADIUS 프로토콜을 TLS로 감싸서 보안 전송을 지원하는 프록시 서버이며, FreeRADIUS는 실제 인증 서버스와 사용자 관리 기능을 제공하는 서버

Clickhouse

  • 컬럼 지향의 고성능 데이터베이스 관리 시스템으로서, 대용량 데이터에 대해 빠른 분석과 실시간 쿼리를 지원
  • 초당 수억 건의 데이터를 효율적으로 처리할 수 있어 실시간 분석과 통계 모니터링 등 대규모 데이터 처리에 최적화

Logstash

  • 오픈소스 데이터 수집 및 처리 엔진
  • 다양한 데이터 소스를 수집하여 실시간으로 필터링, 변환, 정규화 등의 가공 작업을 수행
  • 입력, 필터, 출력 플러그인 구조를 갖추고 있어 다양한 데이터 유형을 처리하고, 최종적으로 저장소 혹은 다른 시스템으로 전달하는 역할

Filebeat

  • Elastic Stack의 경량 로그 수집기
  • 서버나 시스템에서 생성되는 로그 파일을 모니터링하여 실시간으로 로그 데이터를 수집하고 이를 중앙 시스템에 전송하는 역할
  • 운영 중인 여러 서버에서 발생하는 로그 파일을 읽어들여 Logstash나 Elasticsearch 같은 로그 처리 및 저장 시스템으로 전달

Filebeat, Logstash, Clickhouse의 관계

  • Filebeat는 분산된 서버에서 로그를 경량 수집해 Logstash로 전송 ( 데이터 초기 수집)
  • Logstash는 수집된 로그를 필터링, 가공하여 분석에 적합한 형태로 변환 후 저장소로 보냄 (데이터 전처리 및 파이프라인 역할)
  • Clickhouse는 Logstash가 보낸 데이터를 고성능으로 저장하고 대용량 실시간 분석 수행 (저장 및 고속 분석 역할)

인증서 소유

  • RadSecProxy가 RADIUS 인증 요청을 다른 서버로 프록시할 때, 인증서 검사 및 TLS 연결 설정은 인증 요청을 받는 서버에서 관리된다.
  • 즉, 프록시 요청을 보내는 서버는 다른 서버의 인증서를 직접 보유할 필요가 없다.


인증서 생성

# 루트 인증서
# ca.key
openssl genpkey -algorithm RSA -out ca.key -pkeyopt rsa_keygen_bits:2048

# ca.pem
openssl req -new -x509 -days 365 -key ca.key -out ca.pem -subj "/C=KR/ST=chanegeme/L=changeme/O=changeme/OU=changeme/CN=changeme"

# 서버 인증서
# server.key
openssl genpkey -algorithm RSA -out server.key -pkeyopt rsa_keygen_bits:2048

# server.csr
openssl req -new -key server.key -out server.csr -subj "/C=KR/ST=changeme/L=chanegme/O=changeme/OU=changeme/CN=server.domain.or.ip"

# CA로 CSR 서명
openssl x509 -req -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out server.pem -days 365 -sha256


Eduroam 인증서 만료시 발생 로그

Mon Nov 17 08:05:56 2025 : ERROR: (0) ERROR: (TLS) RADIUS/TLS - Alert read:fatal:certificate expired
Mon Nov 17 08:05:56 2025 : Info:  ... shutting down socket auth from client (0.0.0.0, 44409) -> (*, 2083, virtual-server=default)
Mon Nov 17 08:05:58 2025 : Info:  ... adding new socket auth from client (0.0.0.0, 38253) -> (*, 2083, virtual-server=default)
Mon Nov 17 08:05:58 2025 : ERROR: (0) ERROR: (TLS) RADIUS/TLS - Alert read:fatal:certificate expired
Mon Nov 17 08:05:58 2025 : Info:  ... shutting down socket auth from client (0.0.0.0, 38253) -> (*, 2083, virtual-server=default)
Mon Nov 17 08:06:00 2025 : Info:  ... adding new socket auth from client (0.0.0.0, 34935) -> (*, 2083, virtual-server=default)
Mon Nov 17 08:06:00 2025 : ERROR: (0) ERROR: (TLS) RADIUS/TLS - Alert read:fatal:certificate expired
Mon Nov 17 08:06:00 2025 : Info:  ... shutting down socket auth from client (0.0.0.0, 34935) -> (*, 2083, virtual-server=default)
Mon Nov 17 08:06:04 2025 : Info:  ... adding new socket auth from client (0.0.0.0, 37521) -> (*, 2083, virtual-server=default)
Mon Nov 17 08:06:04 2025 : ERROR: (0) ERROR: (TLS) RADIUS/TLS - Alert read:fatal:certificate expired
Mon Nov 17 08:06:04 2025 : Info:  ... shutting down socket auth from client (0.0.0.0, 37521) -> (*, 2083, virtual-server=default)


RadSecProxy Config

#radsecproxy.conf

tls default {
    CACertificateFile /your/path/ca.pem
    CertificateFile /your/path/server.pem
    CertificateKeyFile /your/path/server.key
}

# 2. 로컬 FreeRADIUS 정의 (RadSecProxy가 받은 요청을 넘겨줄 곳)
client local-radius {
    host 127.0.0.1`
    type udp
    secret changeme			
    fticksVISCOUNTRY KR			# F-ticks Country Code
    fticksVISINST    Server1	# F-ticks Your Private Name      
}

client nro-client {
    host Your NRO Server IP
    type tls
    secret changeme
    fticksVISCOUNTRY KR		# F-ticks Country Code
    fticksVISINST    NRO	# F-ticks Your Private Name
}

client test-monitor {
    host Your Monitoring Server IP
    type udp
    secret changeme
    fticksVISCOUNTRY KR			# F-ticks Country Code
    fticksVISINST    MONITOR	# F-ticks Your Private Name
}

server nro-server {
    host Yout NRO Server IP
    port 2083
    type tls
    tls default
    secret changeme
}


server local-radius {
    host 127.0.0.1
    port 1812
    type udp
    secret changeme
}


# 5. 라우팅 (Realm) 설정

realm Your realm {
    server local-radius
}

realm * {
    server nro-server
}

# 포트 개방
ListenTLS *:2083
ListenUDP *:18121

#F-Ticks
FTicksReporting  Basic
FTicksPrefix     F-TICKS/eduroam/1.0
FTicksKey        C9GovB1Lt1mjYxrp
FTicksMAC        VendorKeyHashed
FTicksSyslogFacility = LOG_LOCAL1


SQL Config

## mods-available/sql -- SQL modules

sql {
	
	dialect = "mysql"

	driver = "rlm_sql_mysql"

	sqlite {

		filename = "/tmp/freeradius.db"

		busy_timeout = 200

		bootstrap = "${modconfdir}/${..:name}/main/sqlite/schema.sql"
	}

	mysql {

		warnings = auto
	}

	postgresql {

		send_application_name = yes

	}

	mongo {

		appname = "freeradius"

		tls {
			certificate_file = /path/to/file
			certificate_password = "password"
			ca_file = /path/to/file
			ca_dir = /path/to/directory
			crl_file = /path/to/file
			weak_cert_validation = false
			allow_invalid_hostname = false
		}
	}

	server = "localhost"
	port = 3306
	login = "radius"
	password = "changeme"

	radius_db = "radius"

	acct_table1 = "radacct"
	acct_table2 = "radacct"

	postauth_table = "radpostauth"

	authcheck_table = "radcheck"
	groupcheck_table = "radgroupcheck"

	authreply_table = "radreply"
	groupreply_table = "radgroupreply"

	usergroup_table = "radusergroup"

	delete_stale_sessions = yes

	pool {

		start = ${thread[pool].start_servers}

		min = ${thread[pool].min_spare_servers}

		max = ${thread[pool].max_servers}

		spare = ${thread[pool].max_spare_servers}

		uses = 0

		retry_delay = 30

		lifetime = 0

		idle_timeout = 60

		max_retries = 5
	}


	client_table = "nas"

	group_attribute = "SQL-Group"


	$INCLUDE ${modconfdir}/${.:name}/main/${dialect}/queries.conf
}


Clickhouse Logstash Config

input {
  beats {
    port => 5044
  }
}

filter {
  #  Syslog 헤더 분리
  grok {
    match => { "message" => "%{TIMESTAMP_ISO8601:syslog_timestamp} %{HOSTNAME:syslog_host} %{DATA:syslog_program}: %{GREEDYDATA:fticks_data}" }
  }

  #  F-TICKS 로그만 처리
  if [fticks_data] =~ /F-TICKS\/eduroam/ {
    
    dissect {
      mapping => {
        "fticks_data" => "F-TICKS/%{federation}/%{version}#%{kv_data}"
      }
    }

    kv {
      source => "kv_data"
      field_split => "#"
      value_split => "="
      trim_key => "#"
    }

    mutate {
      rename => {
        "REALM" => "realm"
        "VISCOUNTRY" => "viscountry"
        "VISINST" => "visinst"
        "CSI" => "csi"
        "RESULT" => "result"
      }
    }

    date {
      match => [ "syslog_timestamp", "ISO8601" ]
      target => "@timestamp"
    }
    
    # ClickHouse용 포맷 문자열로 변환하여 'timestamp_str' 필드에 저장
    ruby {
      code => "event.set('timestamp_str', event.get('@timestamp').time.localtime.strftime('%Y-%m-%d %H:%M:%S'))"
    }

    mutate {
      add_tag => ["fticks_success"]
      copy => { "message" => "original_message" }
      remove_field => ["kv_data", "fticks_data", "syslog_program", "syslog_host", "message", "syslog_timestamp"]
    }
  } 
  else {
    drop { }
  }
}

output {
  if "fticks_success" in [tags] {
    clickhouse {
      http_hosts => ["http://127.0.0.1:8123"]
      table => "fticks"
      
      mutations => {
        "timestamp" => "timestamp_str"
        "federation" => "federation"
        "version" => "version"
        "realm" => "realm"
        "viscountry" => "viscountry"
        "visinst" => "visinst"
        "csi" => "csi"
        "result" => "result"
        "original_message" => "original_message"
      }
    }
  }
}


Test-Command

#basic
echo "User-Name=testuser@kisti.re.kr,User-Password=changeme" | radclient -x 127.0.0.1:18121 auth changeme
#include MAC
echo "User-Name=testuser@kisti.re.kr,User-Password=changeme,,Calling-Station-Id=02-00-00-00-00-01" | radclient -x 127.0.0.1:18121 auth changeme