OpenVPN 클라이언트의 IP 고정 할당하기
openvpn을 사용하여 인프라에 접근하는 시스템을 구축하였을 경우, 유저가 할당받는 ip를 고정해야 할 수 있다. 예를 들어 리눅스 서버에 접속한 IP를 확인해야 할 때 클라이언트들의 IP가 고정되어 있지 않다면, 로그를 보더라도 어떤 유저가 ssh를 통해서 접근하였는지 구별할 수가 없기 때문이다. 이런 경우를 어떻게 해결할 수 있는지 알아보자.
ifconfig-push
--ifconfig-push local remote-netmask [alias]
이 옵션은 유저에게 ip를 push하는 역할을 한다. 설정은 서버 설정에서 진행하지만 실행은 openvpn 클라이언트 사이드에서 진행된다. 그래서 이 명령 뒤에 따르는 local
과 remote-netmask
는 클라이언트 입장에서 필요한 내용을 입력해 주어야 한다.
ifconfig-push의 실행
그러면 ifconfig-push
를 실행하는 시점은 어떤 것이 있을지를 알아보면 그것은 다음 내용이 순서대로 진행된다.
1 -- Use --client-connect script generated file for static IP (first choice).
2 -- Use --client-config-dir file for static IP (next choice).
3 -- Use --ifconfig-pool allocation for dynamic IP (last choice).
여기서는 --ifconfig-pool
을 제외한 1,2번에 대해서만 기록해 본다.
client-connect cmd
클라이언트와의 연결이 이루어지는 시점에 특정 명령을 실행할 수 있다. 이를 통해 ifconfig-push
명령을 실행하게 하면 되는데, 예를 들어 접속하는 유저에게 무조건 192.168.255.6의 ip를 할당하여야 하는 경우, 다음과 같은 설정을 만들 수 있다.
$ cat openvpn.conf
# 서버사이드 설정
...
client-connect /etc/openvpn/get_ip.sh
script-security 2
...
$ cat /etc/openvpn/get_ip.sh
#!/bin/bash
ifconfig-push 192.168.255.6 255.255.255.0
openvpn이 외부 스크립트를 실행하기 위해서 --script-security
2를 추가한 것을 확인할 수 있다. 참고로 --script-security
의 경우 레벨에 따라 다음과 같은 실행이 가능해진다.
0 -- 외부 프로그램을 전혀 사용하지 않는다.
1 -- (Default) 기본 제공 실행파일, 즉 ifconfig, ip, route, netsh등의 스크립트 이외의 것들은 사용하지 않는다.
2 -- 1과 더불어 유저 스크립트도 실행 가능해진다.
3 -- 환경 변수에 담긴 password등까지도 사용 가능해진다.(안전하지 않음)
이와는 반대로, 연결이 끊어지는 시점에 명령을 실행할 수도 있는데 이는 --client-disconnect
를 사용한다.
client-config-dir dir
두 번째 방법은 ccd
설정을 이용하는 것이다. 설정 파일에 ccd 경로를 명시하면, 클리이언트와의 연결이 이루어질 때 클라이언트의 common name과 동일한 이름의 파일을 디렉토리 ccd
에서 찾는다. 찾은 경우 해당 파일의 내용을 실행하는데, 이를 통하여 고정 IP를 할당할 수 있다.
$ cat openvpn.conf
# 서버사이드 설정
...
client-config-dir ccd
...
위 client-config-dir
의 인자 ccd
는 디렉토리 이름이므로 ccd
라는 디렉토리를 만들어서 그 안에 클라이언트의 common name과 같은 파일을 만들어 둔다.
$ ls -la ccd/
total 32
drwxr-xr-x 2 root root 4096 Oct 15 11:21 .
drwxr-xr-x 6 root root 4096 Oct 15 11:35 ..
-rw-r--r-- 1 root root 44 Jul 19 2016 cublr
-rw-r--r-- 1 root root 44 Jun 30 06:09 cublr-home
$ cat ccd/cublr
ifconfig-push 192.168.0.254 255.255.255.0
만약 cublr
라는 유저가 접속하는 경우 ccd/cublr
에 명시된 ifconfig-push 192.168.0.254 255.255.255.0
가 클라이언트 사이드에서 실행된다. 딱히 뭔가를 수정할 필요도 없이 사용 가능한, 1번에 비해 상당히 우아한 방법이라고 할 수 있겠다.
예제: 고정 IP 할당
여기서는 client-connect
를 통해 고정 IP를 할당하는 방법에 대해서 적는다. 사실 ccd
를 사용하는 경우 딱히 적을만한 내용이 없이 매우 간단하다. 거기다 유저의 수가 많을 경우 파일들을 계속 지우거나 생성하여야 하므로 매우 귀찮기 때문에, 개인적으로도 이런 요구가 있을 경우는 client-connect
를 더 선호하는 편이다.
client-connect
를 사용하는 경우 진행되는 순서는 다음과 같다.
- 유저와의 연결이 이루어짐(client-connect)
- 서버에서는 접속 시도한 유저의 common name을 사용하여 그 유저가 사용하여야 할 fixed ip를 확인
- 클라이언트로
ifconfig-push
를 하도록 명령 전송
설정
openvpn 서버 사이드 설정에 다음 내용을 추가한다.
# 서버사이드 설정
client-connect /etc/openvpn/get_ip.sh
Fixed IP List 작성
유저와 그 유저의 IP를 기록한 파일이 필요하다. 예를 들어 다음과 같은 내용이면 충분하다. 필요에 맞게 적당히 만들면 된다.
CN IP NETMASK 이름 용도
...
cublr 192.168.12.42 192.168.12.41 cublr 개발
cublr-home 192.168.12.46 192.168.12.45 cublr 긴급대응
cublr-notebook 192.168.12.50 192.168.12.49 cublr 긴급대응
cublr-phone 192.168.12.54 192.168.12.53 cublr 긴급대응
...
각 클라이언트는 /30
의 서브넷을 할당받도록 지정하였다. 이는 Windows와의 호환성을 위해 이렇게 할당한다. 이 내용의 파일을 /etc/openvpn/iplist.txt
로 저장하였다.
스크립트 작성
#!/bin/bash
GREPED=$(grep -P "^$common_name " iplist.txt)
IP=$(echo $GREPED iplist.txt | awk '{print $2}')
NETMASK=$(echo $GREPED iplist.txt | awk '{print $3}')
echo "ifconfig-push $IP $NETMASK" > $1
스크립트의 내용은 별 거 없는데, $common_name
을 iplist.txt
에서 찾아서 ifconfig-push
명령의 파라미터를 만드는 것이다. $common_name
은 연결이 이루어진 시점에 자동으로 환경 변수로 전달되는데, 중요한 것들만 추리면 다음과 같다. 모든 환경변수의 내용은 man openvpn
을 참고하자.
- common name: 연결된 유저의 X509 common name을 말한다. 쉽게 말하면 키 이름.
- dev: tun/tap 등의 디바이스 이름.
- username: 유저가 인증에 사용한 아이디. 예를 들어 openvpn+pam 인증을 붙였을 경우, 이를 스크립트에 사용할 수 있다.
- password: 유저가 인증에 사용한 패스워드. 이름부터가 왠지 보안적으로다가 사용하면 안될 것 같아 보인다.
client-connect
의 장점은 스크립트를 실행한다는 것에 있다. 지금은 간단한 txt
파일과 이를 파싱하도록 grep
을 사용하여 고정된 얻도록 하였는데, 조금 더 발전된 형태를 생각해 보면 python3
와 mysql
/sqlite
등을 통해 조금 더 우아하고 멋진 테이블 관리를 할 수 있을 것이다.
사용
이제 유저가 접속할 경우 get_ip.sh
가 실행되어 해당 유저의 올바른 IP/NETMASK를 얻을 수 있다. 새로운 유저가 추가되면 iplist.txt에 해당 유저 정보를 추가하여 사용하면 된다.