... how to set static ip addresses to my OpenVPN clients in TUN mode.
For example, I would like to set the 10.8.0.2 ip to client01, 10.8.0.4 to client02 and 10.8.0.6 to client 03.
I have a router RT-AC68U and I have only one Openvpn server active in tun mode. I use the only .ovpn file that the system generates for all my clients. So, I have a single certificate and key that I am using for all my clients. I think I need this dynamic VPN Client Connect script. How it works? What do I have to do?
I have only one Openvpn server active in tun mode. I use the only .ovpn file that the system generates for all my clients. So, I have a single certificate and key that I am using for all my clients. I think I need this dynamic VPN Client Connect script. How it works? What do I have to do?
So cut'n'paste into the appropriate file and make it executable etc.
e.g. This extreme example shows that whilst the standard 10.8.0.0/24 pool will be used for all the other VPN clients, you can actually use the values as shown!
Code:
# Identify client
case "$username" in
"SGS5")
echo "ifconfig-push 10.12.34.56 255.255.255.0" >>$1 # Give my phone a static virtual I/P address and gateway!!!
# Don't forget 'route 10.12.34.0 255.255.255.0 vpn_gateway' in the GUI
You can read the appropriate OpenVPN manual if you are unsure how the 'ifconfig-push' directive is to be used:
#!/bin/sh
VER="v3.02"
# This VPN Server custom script will create a CCD/tmp_file based on 'User names' when they share the same 'Common name' e.g. 'client'
#
# i.e. CCD file '/jffs/configs/openvpn/ccd1/client' is inappropriate for multiple concurrent clients
# (see https://github.com/RMerl/asuswrt-merlin/wiki/Configuring-OpenVPN)
#
# Add CCD custom directives to
#
# '/jffs/configs/openvpn/ccd?/client_xxxxxx' - where xxxxxx is the Username used to authenticate using the User P/W
# and '?' is the VPN server instance '1' or '2'
#
# e.g. staticip 123
# or
# ifconfig-push 10.8.0.123 255.255.255.0
#
# also add the following in the VPN Server Custom Configuration
#
# # Customise clients - assign I/P address; share remote subnet etc:
# client-connect /jffs/scripts/VPNClientConnect.sh
# script-security 2 This will be auto included by RMerlin firmware (see /etc/openvpn/server1/config.ovpn)
# ONLY true if '/jffs/scripts/openvpn-event' exists!!!!!
#
#
#
#=====================================Main============================================================
#
# Function Parse(String delimiter(s) variable_names)
Parse() {
#
# Parse "Word1,Word2|Word3" ",|" VAR1 VAR2 REST
# (Effectivley executes VAR1="Word1";VAR2="Word2";REST="Word3")
local string IFS
TEXT="$1"
IFS="$2"
shift 2
read -r -- "$@" <<EOF
$TEXT
EOF
}
Get_CCD_Filename () {
for THISARG in $@
do
if [ -f $THISARG ];then
local FN=$THISARG
fi
done
echo $FN
return 0
}
#===============================================MAIN==========================================================================
VPN_SERVER_ID=${dev:4:1} # Identify current VPN Server instance
logger -st "($(basename $0))" $$ $VER "VPN Server" $VPN_SERVER_ID "Client user '"$username"' CCD configuration starting...." [$@]
POOL_SUBNET=${ifconfig_pool_remote_ip%.*} # Normally 10.8.0.xxx -> 10.8.0 (or 10.16.0.xxx -> 10.16.8)
# Configure the VPN Client CCD/file dynamically
STATUS="OK" # Let's be positive! ;-)
CCD_FILE=$(Get_CCD_Filename $@)
#logger -st "($(basename $0))" $$ "***DEBUG processing CCD_FILE='"$CCD_FILE"'"
if [ "$common_name" == "client" ];then
CCD_USER_ARGS="/jffs/configs/openvpn/ccd${VPN_SERVER_ID}/client_$username"
else
CCD_USER_ARGS="/jffs/configs/openvpn/ccd${VPN_SERVER_ID}/$common_name"
fi
if [ -f "$CCD_USER_ARGS" ];then
logger -st "($(basename $0))" $$ "Processing file '"$CCD_USER_ARGS"'"
while read LINE
do
#logger -st "($(basename $0))" $$ "***DEBUG LINE=>$LINE<"
case "$LINE" in
*ifconfig-push*|*staticip*)
Parse "$LINE" " " KEYWORD IP_ADDR SUBNET_MASK
# Let's give the lazy/numpty user a helping hand!
# e.g. 'staticip 88' -> 'ifconfig-push ${POOL_SUBNET}.88 255.255.255.0'
#
if [ "$KEYWORD" == "staticip" ];then
KEYWORD="ifconfig-push" # Use the real OpenVPN statement rather than our 'staticip' codeword
logger -st "($(basename $0))" $$ "Psuedo command 'staticip' converted to 'ifconfig-push'!"
fi
if [ -z "$SUBNET_MASK" ];then
SUBNET_MASK="255.255.255.0" # Use the default subnet mask if ommitted!
fi
if [ $(echo "$IP_ADDR" | grep -o "\." | wc -l) -eq 0 ];then # Only a number specified ?
echo -e $KEYWORD ${POOL_SUBNET}.$IP_ADDR $SUBNET_MASK >> $CCD_FILE
else
echo -e $KEYWORD $IP_ADDR $SUBNET_MASK >> $CCD_FILE # Use full I/P address as supplied
fi
;;
*)
echo -e "$LINE" >> $CCD_FILE # Assume user has specified correct syntax!!!
;;
esac
done < $CCD_USER_ARGS
fi
if [ "$STATUS" == "OK" ];then
logger -st "($(basename $0))" $$ "VPN Server" $VPN_SERVER_ID "Client user '"$username"' CCD config:" `cat $CCD_FILE`
else
echo -e "\a"
logger -st "($(basename $0))" $$ "**WARNING VPN Server" $VPN_SERVER_ID "Client user '"$username"' not defined - dynamic CCD config skipped."
fi
#Send_email [file | "A_single_line_text_message_in_quotes_to_be_emailed" ] [email_method]
TEMPFILE="/tmp/VPNClientConnect"${username}".txt"
echo "Common Name: $common_name" >>$TEMPFILE
echo "Username: $username" >>$TEMPFILE
echo "I/P: $trusted_ip" >>$TEMPFILE
echo "PORT:$trusted_port" >>$TEMPFILE
echo "MTU: $tun_mtu" >>$TEMPFILE
#Send_email $TEMPFILE
logger -st "($(basename $0))" $$ "VPN Server" $VPN_SERVER_ID "Client CCD configuration ended."
exit 0
Ensure the script is executable as per the Wiki etc.
Once the script has been created, you now need to tell the VPN Server instance which 'clients' are to have a static I/P assigned.....
Step 1. Simply enter two lines (into the VPN Server Custom Configuration) as shown below...clearly the comment line is optional! - and should be omitted if available NVRAM is low.
Step 2. Create the appropriate individual CCD parameter files
e.g. for VPN Server 1
4 named users (client01,client02,client03 and iPhone6) when they authenticate by name will have static I/P addresses (.2, .4, .6 and .66) assigned from the current VPN IP server pool (usually 10.8.0 or 10.16.0 for VPN Server1/Server2)
/jffs/configs/openvpn/ccd1/client_client01
Code:
staticip 2
/jffs/configs/openvpn/ccd1/client_client02
Code:
staticip 4
/jffs/configs/openvpn/ccd1/client_client03
Code:
staticip 6
/jffs/configs/openvpn/ccd1/client_iPhone6
Code:
staticip 66
NOTE: 'staticip' xxx is not a valid OpenVPN directive. My script simply makes it easier to specify a static I/P whilst allowing the script to dynamically (@runtime) determine the correct I/P prefix for the current VPN Server client pool.
However you may explicitly specify the full OpenVPN directive if you already know the current VPN Server client pool prefix.
e.g. for the iPhone6 user connecting to VPN Server 1
Code:
ifconfig-push 10.8.0.66 255.255.255.0
Code:
/jffs/configs/openvpn/ccd1/client_SGS5
# Custom CCD directives for single common name 'client' user SGS5
# Assign static I/P xxx.xxx.xxx.88 (Normally xxx.xxx.xxx=10.8.0 for VPN Server 1, and xxx.xxx.xxx=10.16.0 for VPN Server 2)
# 10.8.88.NOTE: 'staticip' is NOT a valid OpenVPN directive, but it is an easy to remember psuedo command short-cut!
staticip 88
(VPNClientConnect.sh): 23908 v3.01 VPN Server 1 Client user 'SGS5' CCD configuration starting.... [/tmp/openvpn_cc_53c68cfa75999c55604c7867c683d8a9.tmp]
(VPNClientConnect.sh): 23908 Processing Client_username file '/jffs/configs/openvpn/ccd1/client_SGS5'
(VPNClientConnect.sh): 23908 Psuedo command 'staticip' converted to 'ifconfig-push'!
(VPNClientConnect.sh): 23908 VPN Server 1 Client user 'SGS5' CCD config: # Custom CCD directives for single common name 'client' user SGS5 # Assign static I/P xxx.xxx.xxx.88 (Normally xxx.xxx.xxx=10.8.0 for VPN Server 1, and xxx.xxx.xxx=10.16.0 for VPN Server 2) # 10.8.88.NOTE: 'staticip' is NOT a valid OpenVPN directive, but it is an easy to remember psuedo command short-cut! ifconfig-push 10.8.88.88 255.255.255.0
(VPNClientConnect.sh): 23908 VPN Server 1 Client CCD configuration ended.
openvpn[2726]: client/XXX:XXX:XXX:XXX OPTIONS IMPORT: reading client specific options from: /tmp/openvpn_cc_53c68cfa75999c55604c7867c683d8a9.tmp
openvpn[2726]: client/XXX:XXX:XXX:XXX MULTI: Learn: 10.8.88.88 -> client/XXX:XXX:XXX:XXX
openvpn[2726]: client/XXX:XXX:XXX:XXX MULTI: primary virtual IP for client/XXX:XXX:XXX:XXX: 10.8.88.88
openvpn[2726]: client/XXX:XXX:XXX:XXX PUSH: Received control message: 'PUSH_REQUEST'
openvpn[2726]: client/XXX:XXX:XXX:XXX SENT CONTROL [client]: 'PUSH_REPLY,route 10.88.8.0 255.255.255.0 vpn_gateway 500,dhcp-option DNS 10.88.8.1,route-gateway 10.8.88.1,topology subnet,ping 15,ping-restart 60,ifconfig 10.8.88.88 255.255.255.0,peer-id 0,cipher AES-128-GCM' (status=1)
P.S. This is not guaranteed to be foolproof - there is no checking that the static I/P has NOT already been assigned (i.e. already in use) from the I/P pool to a no-name user!
#!/bin/sh
VER="v3.01"
# This VPN Server custom script will create a CCD/tmp_file based on 'User names' when they share the same 'Common name' e.g. 'client'
#
# i.e. CCD file '/jffs/configs/openvpn/ccd1/client' is inappropriate for multiple concurrent clients
# (see https://github.com/RMerl/asuswrt-merlin/wiki/Configuring-OpenVPN)
#
# Add CCD custom directives to
#
# '/jffs/configs/openvpn/ccd?/client_xxxxxx' - where xxxxxx is the Username used to authenticate using the User P/W
# and '?' is the VPN server instance '1' or '2'
#
# e.g. staticip 123
# or
# ifconfig-push 10.8.0.123 255.255.255.0
#
# also add the following in the VPN Server Custom Configuration
#
# # Customise clients - assign I/P address; share remote subnet etc:
# client-connect /jffs/scripts/VPNClientConnect.sh
# script-security 2 This will be auto included by RMerlin firmware (see /etc/openvpn/server1/config.ovpn)
# ONLY true if '/jffs/scripts/openvpn-event' exists!!!!!
#
#
#
#=====================================Main============================================================
#
# Function Parse(String delimiter(s) variable_names)
Parse() {
#
# Parse "Word1,Word2|Word3" ",|" VAR1 VAR2 REST
# (Effectivley executes VAR1="Word1";VAR2="Word2";REST="Word3")
local string IFS
TEXT="$1"
IFS="$2"
shift 2
read -r -- "$@" <<EOF
$TEXT
EOF
}
Get_CCD_Filename () {
for THISARG in $@
do
if [ -f $THISARG ];then
local FN=$THISARG
fi
done
echo $FN
return 0
}
#===============================================MAIN==========================================================================
VPN_SERVER_ID=${dev:4:1} # Identify current VPN Server instance
logger -st "($(basename $0))" $$ $VER "VPN Server" $VPN_SERVER_ID "Client user '"$username"' CCD configuration starting...." [$@]
POOL_SUBNET=${ifconfig_pool_remote_ip%.*} # Normally 10.8.0.xxx -> 10.8.0 (or 10.16.0.xxx -> 10.16.8)
# Configure the VPN Client CCD/file dynamically
STATUS="OK" # Let's be positive! ;-)
CCD_FILE=$(Get_CCD_Filename $@)
#logger -st "($(basename $0))" $$ "***DEBUG processing CCD_FILE='"$CCD_FILE"'"
CCD_USER_ARGS="/jffs/configs/openvpn/ccd${VPN_SERVER_ID}/client_$username"
if [ -f "$CCD_USER_ARGS" ];then
logger -st "($(basename $0))" $$ "Processing Client_username file '"$CCD_USER_ARGS"'"
while read LINE
do
#logger -st "($(basename $0))" $$ "***DEBUG LINE=>$LINE<"
case "$LINE" in
*ifconfig-push*|*staticip*)
Parse "$LINE" " " KEYWORD IP_ADDR SUBNET_MASK
# Let's give the lazy/numpty user a helping hand!
# e.g. 'staticip 88' -> 'ifconfig-push ${POOL_SUBNET}.88 255.255.255.0'
#
if [ "$KEYWORD" == "staticip" ];then
KEYWORD="ifconfig-push" # Use the real OpenVPN statement rather than our 'staticip' codeword
logger -st "($(basename $0))" $$ "Psuedo command 'staticip' converted to 'ifconfig-push'!"
fi
if [ -z "$SUBNET_MASK" ];then
SUBNET_MASK="255.255.255.0" # Use the default subnet mask if ommitted!
fi
if [ $(echo "$IP_ADDR" | grep -o "\." | wc -l) -eq 0 ];then # Only a number specified ?
echo -e $KEYWORD ${POOL_SUBNET}.$IP_ADDR $SUBNET_MASK >> $CCD_FILE
else
echo -e $KEYWORD $IP_ADDR $SUBNET_MASK >> $CCD_FILE # Use full I/P address as supplied
fi
;;
*)
echo -e "$LINE" >> $CCD_FILE # Assume user has specified correct syntax!!!
;;
esac
done < $CCD_USER_ARGS
fi
if [ "$STATUS" == "OK" ];then
logger -st "($(basename $0))" $$ "VPN Server" $VPN_SERVER_ID "Client user '"$username"' CCD config:" `cat $CCD_FILE`
else
echo -e "\a"
logger -st "($(basename $0))" $$ "**WARNING VPN Server" $VPN_SERVER_ID "Client user '"$username"' not defined - dynamic CCD config skipped."
fi
#Send_email [file | "A_single_line_text_message_in_quotes_to_be_emailed" ] [email_method]
TEMPFILE="/tmp/VPNClientConnect"${username}".txt"
echo "Common Name: $common_name" >>$TEMPFILE
echo "Username: $username" >>$TEMPFILE
echo "I/P: $trusted_ip" >>$TEMPFILE
echo "PORT:$trusted_port" >>$TEMPFILE
echo "MTU: $tun_mtu" >>$TEMPFILE
#Send_email $TEMPFILE
logger -st "($(basename $0))" $$ "VPN Server" $VPN_SERVER_ID "Client CCD configuration ended."
exit 0
Ensure the script is executable as per the Wiki etc.
Once the script has been created, you now need to tell the VPN Server instance which 'clients' are to have a static I/P assigned.....
Step 1. Simply enter two lines (into the VPN Server Custom Configuration) as shown below...clearly the comment line is optional! - and should be omitted if available NVRAM is low.
Step 2. Create the appropriate individual CCD parameter files
e.g. for VPN Server 1
4 named users (client01,client02,client03 and iPhone6) when they authenticate by name will have static I/P addresses (.2, .4, .6 and .66) assigned from the current VPN IP server pool (usually 10.8.0 or 10.16.0 for VPN Server1/Server2)
/jffs/configs/openvpn/ccd1/client_client01
Code:
staticip 2
/jffs/configs/openvpn/ccd1/client_client02
Code:
staticip 4
/jffs/configs/openvpn/ccd1/client_client03
Code:
staticip 6
/jffs/configs/openvpn/ccd1/client_iPhone6
Code:
staticip 66
NOTE: 'staticip' xxx is not a valid OpenVPN directive. My script simply makes it easier to specify a static I/P whilst allowing the script to dynamically (@runtime) determine the correct I/P prefix for the current VPN Server client pool.
However you may explicitly specify the full OpenVPN directive if you already know the current VPN Server client pool prefix.
e.g. for the iPhone6 user connecting to VPN Server 1
Code:
ifconfig-push 10.8.0.66 255.255.255.0
Code:
/jffs/configs/openvpn/ccd1/client_SGS5
# Custom CCD directives for single common name 'client' user SGS5
# Assign static I/P xxx.xxx.xxx.88 (Normally xxx.xxx.xxx=10.8.0 for VPN Server 1, and xxx.xxx.xxx=10.16.0 for VPN Server 2)
# 10.8.88.NOTE: 'staticip' is NOT a valid OpenVPN directive, but it is an easy to remember psuedo command short-cut!
staticip 88
(VPNClientConnect.sh): 23908 v3.01 VPN Server 1 Client user 'SGS5' CCD configuration starting.... [/tmp/openvpn_cc_53c68cfa75999c55604c7867c683d8a9.tmp]
(VPNClientConnect.sh): 23908 Processing Client_username file '/jffs/configs/openvpn/ccd1/client_SGS5'
(VPNClientConnect.sh): 23908 Psuedo command 'staticip' converted to 'ifconfig-push'!
(VPNClientConnect.sh): 23908 VPN Server 1 Client user 'SGS5' CCD config: # Custom CCD directives for single common name 'client' user SGS5 # Assign static I/P xxx.xxx.xxx.88 (Normally xxx.xxx.xxx=10.8.0 for VPN Server 1, and xxx.xxx.xxx=10.16.0 for VPN Server 2) # 10.8.88.NOTE: 'staticip' is NOT a valid OpenVPN directive, but it is an easy to remember psuedo command short-cut! ifconfig-push 10.8.88.88 255.255.255.0
(VPNClientConnect.sh): 23908 VPN Server 1 Client CCD configuration ended.
openvpn[2726]: client/XXX:XXX:XXX:XXX OPTIONS IMPORT: reading client specific options from: /tmp/openvpn_cc_53c68cfa75999c55604c7867c683d8a9.tmp
openvpn[2726]: client/XXX:XXX:XXX:XXX MULTI: Learn: 10.8.88.88 -> client/XXX:XXX:XXX:XXX
openvpn[2726]: client/XXX:XXX:XXX:XXX MULTI: primary virtual IP for client/XXX:XXX:XXX:XXX: 10.8.88.88
openvpn[2726]: client/XXX:XXX:XXX:XXX PUSH: Received control message: 'PUSH_REQUEST'
openvpn[2726]: client/XXX:XXX:XXX:XXX SENT CONTROL [client]: 'PUSH_REPLY,route 10.88.8.0 255.255.255.0 vpn_gateway 500,dhcp-option DNS 10.88.8.1,route-gateway 10.8.88.1,topology subnet,ping 15,ping-restart 60,ifconfig 10.8.88.88 255.255.255.0,peer-id 0,cipher AES-128-GCM' (status=1)
P.S. This is not guaranteed to be foolproof - there is no checking that the static I/P has NOT already been assigned (i.e. already in use) from the I/P pool to a no-name user!
Hi, the script doesn't seem to be working anymore. Did anything change in the latest Merlin OpenVPN implementation?
I added manually the "script-security 2" entry. And the VPNClientConnect.sh has the correct permission and execution mode set.
When a client is connecting I get this warning which seems to tell that the script is unable to be executed correctly. :
WARNING: Failed running command (--client-connect): could not execute external program
The "VPN Server ... Client user ... CCD configuration starting...." is never shown in the log, so I guess the script never manages to be executed. But the path in the Advanced VPN config is 100% correct. And I'm able to manually run the VPNClientConnect.sh file (Though it seems to hang without a OPENVPN file specified).
Any clue or idea on what could be going on here and how to fix it? Thanks.
Hi, the script doesn't seem to be working anymore. Did anything change in the latest Merlin OpenVPN implementation?
I added manually the "script-security 2" entry. And the VPNClientConnect.sh has the correct permission and execution mode set.
When a client is connecting I get this warning which seems to tell that the script is unable to be executed correctly. :
WARNING: Failed running command (--client-connect): could not execute external program
The "VPN Server ... Client user ... CCD configuration starting...." is never shown in the log, so I guess the script never manages to be executed. But the path in the Advanced VPN config is 100% correct. And I'm able to manually run the VPNClientConnect.sh file (Though it seems to hang without a OPENVPN file specified).
Any clue or idea on what could be going on here and how to fix it? Thanks.
I believe there are some minor changes and whilst the contents of my VPNClientConnect.shv4.70 contains more psuedo OpenVPN command features than the original script I posted it still works with the following three directives in the Custom Configuration GUI
e.g. for OpenVPN Server 2
Code:
# Override 'ccd/client' v384.4 now allows directives to be prefixed with '--' ?
--client-config-dir /jffs/configs/openvpn/ccd2
# Rather than use CN=client, more flexible to use a script for username!
--client-connect /jffs/scripts/VPNClientConnect.sh
--client-disconnect /jffs/scripts/VPNClientDisconnect.sh
I'm trying to achieve the same as OP. I have 1 VPN server and two clients. I want one of the clients to get a static VPN IP, I don't care about the other.
The clients have different common names, and a different password, but they use the same .ovpn file to connect to the server.
I thought that adding a client1 file in the ccd folder would do the trick, but it clearly doesn't as my client sometimes gets another IP.
I would like not to depend on your script, Martineau, because I don't really understand it. How am I supposed to have a different key and cert for each of my clients?
I'm trying to achieve the same as OP. I have 1 VPN server and two clients. I want one of the clients to get a static VPN IP, I don't care about the other.
The clients have different common names, and a different password, but they use the same .ovpn file to connect to the server.
I thought that adding a client1 file in the ccd folder would do the trick, but it clearly doesn't as my client sometimes gets another IP.
I would like not to depend on your script, Martineau, because I don't really understand it. How am I supposed to have a different key and cert for each of my clients?
Each common name should be associated w/ a unique client cert and key. And you do that at the time you create the certs and keys using easy-rsa. For simplicity sake, many ppl just create one client cert and key, specify the directive duplicate-cn, and reuse it. But once you need to distinguish one client from another, you need generate a cert and key for each client, and each with its own unique common name. And that also means each client will have different .ovpn config files since the client certs and keys differ.
Each common name should be associated w/ a unique client cert and key. And you do that at the time you create the certs and keys using easy-rsa. For simplicity sake, many ppl just create one client cert and key, specify the directive duplicate-cn, and reuse it. But once you need to distinguish one client from another, you need generate a cert and key for each client, and each with its own unique common name. And that also means each client will have different .ovpn config files since the client certs and keys differ.
@Martineau One thing I want to add is that there is no need for a script to distinguish between clients. You can just add "username-as-common-name" to the server config file and usernames will automatically be recognized as common names.
SNBForums is a community for anyone who wants to learn about or discuss the latest in wireless routers, network storage and the ins and outs of building and maintaining a small network.
If you'd like to post a question, simply register and have at it!
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.