[OpenVPN] ID / PW 로 인증하기
OpenVPN 은 기본적으로 사용자별로 인증서를 생성해서 연결하도록 하고 있다.
그러다보니 설정파일에 ca, tls-auth, cert, key 4개의 파일이 추가로 따라다니게 되는데 관리하기 조금 번거로운 면이 있다.
물론 각각 ovpn 파일안에 임베딩해버릴 수 있지만...
아이디, 비밀번호로 로그인하는 방법이 있길래 진행해보았다.
먼저 OpenVPN 서버 설정에서 아래 두 문구를 추가한다.
client-cert-not-required
auth-user-pass-verify "/etc/openvpn/userauth/verify.sh" via-file
첫번째 줄은 클라이언트 인증서가 필요없게 하는거고, 두번째줄은 비밀번호를 체크할 스크립트를 지정한다.
저 파일은 내가 만들어줘야하며, 경로는 어디두던 크게 상관없음.
다만 user nobody / group nogroup 설정을 했을 경우 OpenVPN 데몬이 nobody / nogroup 권한으로 작동하기 때문에, 권한에 유의해주면 된다. 읽기 권한이나 실행 권한 등이 없으면 비밀번호 체크를 할 수 없다.
비밀번호 체크 과정에 root 권한이 필요하다면 권한을 낮추는 해당 설정을 제거해주자.
스크립트 경로 뒤의 via-file 은 OpenVPN 에서 사용자가 입력한 아이디와 비밀번호를 넘겨줄 방식을 정한다.
via-file 로 할 경우 임시 파일을 생성하면서
username
password
이렇게 두 줄로 된 파일을 생성한다.
(via-env 로 할 경우 환경변수에 넣어준다는데, 사용자명은 들어오는데 비밀번호는 어디있는지 모르겠더라...)
해당 스크립트에서 아이디와 비밀번호를 읽어서, 성공했다면 exit 0 을, 실패했다면 에러코드와 함께 프로그램이 종료되면 된다.
이제 비밀번호 체크를 위한 스크립트를 만들자.
방식은 다양하니 직접 만들면 되겠지만 내 스크립트를 참고 삼아 올려둠.
아래 URL 에 들어가보면 다른 스크립트들도 있다.
나는 user.pass 파일에
사용자명=SHA1비밀번호
사용자명=SHA1비밀번호
꼴로 저장을 해두고, 이를 검사하는 방식으로 작성하였다.
verify.sh 코드는 이렇게.
#!/bin/bash
passfile="/etc/openvpn/userauth/user.pass"
logfile="/var/log/openvpn/userauth.log"
username=`head -n 1 $1`
password=`head -n 2 $1 | tail -1`
hashed=`echo -n $password | sha1sum | awk '{print $1}'`
userpass=`cat $passfile | grep $username= | awk -F= '{print $2}'`
if [ "$userpass" = "$hashed" ]; then
echo "`date +'%Y-%m-%d %H:%M:%S'` - auth success: $username" >> $logfile
# echo "ok"
exit 0
fi
echo "`date +'%Y-%m-%d %H:%M:%S'` - auth fail: $username" >> $logfile
#echo "not ok"
exit 1
그리고 client 설정 파일에는 cert 와 key 를 없애고, auth-user-pass 옵션을 추가해주면 된다.
----------
이 다음으로, Local 사용자 계정으로 로그인하는걸 시도해보았다.
보통 shadow 파일을 읽어서 비교를 많이 하던데, 나는 PAM Authentication 을 이용해보았다.
PAM 인증을 할때 root 이어야만 제대로 작동하는 것 같으니, 서버 설정에서 user nobody 옵션은 주석처리 해주자. (혹은 스티키 비트를 넣어주거나..)
PAM 인증을 하는 c 프로그램을 제작하였고, 로그를 남기기 위해 간단한 쉘 스크립트를 같이 이용하였음.
verify.sh
#!/bin/bash
pam_auth="/etc/openvpn/userauth/pam_auth"
logfile="/var/log/openvpn/userauth.log"
pam_result=`$pam_auth $1`
ret=$?
echo "`date +'%Y-%m-%d %H:%M:%S'` $pam_result" >> $logfile
exit $ret
pam_auth.c ( https://gist.github.com/iolate/a58b73a023b35d5f181814de2f4ffccd )
// gcc -o pam_auth pam_auth.c -lpam
#include <security/pam_appl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int custom_converation(int num_msg, const struct pam_message** msg, struct pam_response** resp, void* appdata_ptr) {
// Provide password for the PAM conversation response that was passed into appdata_ptr
struct pam_response* reply = (struct pam_response* )malloc(sizeof(struct pam_response));
reply[0].resp = (char*)appdata_ptr;
reply[0].resp_retcode = 0;
*resp = reply;
return PAM_SUCCESS;
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s [filepath]\n", argv[0]);
exit(1);
}
FILE* fp;
char* username = NULL;
char* password = NULL;
size_t len = 0;
ssize_t read;
fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "%s: Cannot open '%s'\n", argv[0], argv[1]);
return 1;
}
read = getline(&username, &len, fp);
if (read == -1) {
fclose(fp);
return 1;
}
username[strlen(username)-1] = '\0'; // remove LF
read = getline(&password, &len, fp);
if (read == -1) {
fclose(fp);
return 1;
}
password[strlen(password)-1] = '\0'; // remove LF
fclose(fp);
// PAM Authentication
struct pam_conv conv = {custom_converation, password};
pam_handle_t* pamh = NULL;
int retval = pam_start("whoami", username, &conv, &pamh);
if (retval == PAM_SUCCESS)
retval = pam_authenticate(pamh, 0); // is user really user?
//if (retval == PAM_SUCCESS)
// retval = pam_acct_mgmt(pamh, 0); // permitted access?
if (retval == PAM_SUCCESS) {
fprintf(stdout, "Authenticated - %s\n", username);
} else {
fprintf(stdout, "Not Authenticated - %s\n", username);
}
pam_end(pamh, 0);
return retval;
}
pam_auth 를 컴파일하기 위해선 pam development 패키지가 필요하니 설치하고 컴파일 해주자.
Ubuntu 기준 아래의 방법으로 진행하면 됨.
sudo apt install libpam0g-dev
gcc -o pam_auth pam_auth.c -lpam
verify.sh 를 간단히 수정하면, 두가지 방식을 동시에 사용하는 것도 가능하다.
참고
https://forums.openvpn.net/viewtopic.php?t=24907
https://medium.com/@nqbao/openvpn-auth-user-pass-verify-example-8d99023f08f7