Jump to content

OpenVPN and scripting fun...


Recommended Posts


I've been searching around on this board try to find topics that treat OpenVPN scripts (+scripts +openvpn) and using specific openvpn options like +"client-connect" +openvpn with current dat and older option.

I have not being able to find specific script info on this forum regarding OpenVPN....

I have been capable of setting up a multi-client server and i'm also capable of connecting from any client to the server, using ca cert and ta certification and user based certificates (clientnames).

The clients have to log in silently through the network's firewall which always works. (In some cases i have to add a sneak route to the true gateway of the local network to get internet access, some of our client machines get an internal lan zone only).

The only problem is: if someone has physical access to those clients and manages to steal the keys and certificates, the person can happily connect to my VPN server and act like a kid in an non-supervised candy-store.

Logging on could be done using user and password authentication, but also those credits have to be stored in files that can be taken as well so that is not really an option.

Luckily you have some stages in which you can execute own scripts to do some checking. (server side or client side)

So in this particular situation i had a basic idea of checking the client's remote ip using a script to check out the environment variable "ifconfig_remote" and match it with a list to be sure the keys are submitted from the spot where i have left them.

But this is where the trouble actually starts.

I know you can script this using the client-connect option, however these scripting features are so poorly documented (because you can arbritrary methods to perform actions). I can find some scripts that give me some rough insides on how to deal with OpenVPN, but getting them executed is a real tar-pit.

Even when i set the script-security level to 2, it still does not seem to execute my scripts.

Just a simple .CMD script returning "exit -1" on one single line to test a logon interruption was for me the thing to test "if" my script actually is being started. But perhaps i am looking into the wrong direction.

At least the logging level (set to 7) does not give any indication whatsoever that it tries to look for a specific script or execute them.

So the two basic questions:

-how do i get these scripts executed (where should they reside in the config folder or wherever)

-And can i start up user specific scripts by adding the client-connect directive directly into a server-side ccd configuration?

I know you can do powerful things with scripting, just the interaction between OpenVPN and the script is not completely clear.

Perhaps we could use this topic to post several scripting solutions to work with OpenVPN and have at least one place where we can find this useful information.

Thanks in advance!

Link to comment
Share on other sites

I figured this one out myself.....

I found out that my programs got executed, however the returnvalue never got back due to a bug in the programming language used.

The following programming snippet has been written in XBasic (The open source variant) that you can download from xbasic.org. It supports Windows and Linux, however the official build doesn't work on the current Linux distributions anylonger, you can get an unofficial build that does support the newer versions in the file-section on the Xbasic mailinglist on the Yahoogroups (you have to register as member of the group in order to get this build).

Anyway, here is a code-snippet that allows you to use OpenVPN in two ways without having to open up two adapters or two tunnels.

You have person A, machine B and hacker C. The code-snippet works in two ways, it reads a list of trusted ip-addresses (i know that's a bit too simple, but i have no idea how to submit machine specific data yet) and in a further scenario, a username and password have to be submitted.

Machine B, does not authenticate itself using user-name and password. I can make it do that, but it is pointless if hacker C is able to fetch all this data from the harddrive in a sneak moment and then goes experimenting at home with the stuff.

I know Machine B's IP address which i register in a table of trusted ip's.

Person A is working on a laptop and working from various sources, so his IP address will be different all the time. I can't anticipate on the ip-addresses, so this person has to log on using username and password. That ain't any more than logical as well.

If hacker C does steal the keys and the configuration file and decides to try logging on at home, he has a problem since his ip address is unknown and therefore OpenVPN responds with the fact that his user-authentication went wrong.

That is what the below code simply does. You need to create a simple text-file containing trusted ip-addresses and perhaps a list with untrusted ip-addresses, just to test it out. The user-database is a very simple array stored database having a 16-byte username container and a 16-byte password container in the same type. The amount of records is being calculated by dividing the database size by 32. Very simple and very fragile, there is no integrity-check whatsover and everything is read plain-text but this is just a simple example.

I don't think the XBasic language is too hard to interpret and convert into your own scripting language :)

You can quickly compile it in Windows, you do need to import kernel32 API lib as the Xbasic runtime does submit the return value wrong. (it always quits with value 0 regardless what you fill into the QUIT() parentheses)

' ####################
' #####  PROLOG  #####
' ####################
PROGRAM	"progname"  ' 1-8 char program/file name without .x or any .extent
VERSION	"0.0000"    ' version number - increment before saving altered program

IMPORT	"xst"   ' Standard library : required by most programs
IMPORT	"kernel32"


USER #Users[]
' ######################
' #####  Entry ()  #####
' ######################
' This script is used to check user authentication of a person logging on.
' However some locations you might want to use unattended login on.
' That is a bit a problem if you want to require interactive login for person A
' and unattended login for machine B. So here's the deal:
' You can register trusted ip addresses in a list.
' This list will be read, if the ip is listed, no interactive logon is required.
' However if the ip is not listed, then the user needs to verify his login interactively.
' You need the auth-user-pass-optional on the server-side for this to work on 
' the machines that do unattended login. You need auth-user-pass directive for
' other users that want to log in as well.
FUNCTION  Entry ()
	XstGetEnvironmentVariable      ("untrusted_ip", @value$)
	XstGetEnvironmentVariable      ("ERRLOG", @dvalue$)
	XstLoadStringArray("trusted_ip_clients.txt", @trustedlist$[])
	XstLoadStringArray("untrusted_ip_clients.txt", @untrustedlist$[])
	XstGetCommandLineArguments     (@argc, @argv$[])

'Process contents of loaded files and read environment variables.
	PRINT "All arguments collected"
	FOR X = 0 TO UBOUND(trustedlist$[])
		IF value$ == trustedlist$[X] THEN
			PRINT "Found trusted ip is logging on..."

	found = 0

	FOR X = 0 TO UBOUND(untrustedlist$[])
		IF value$ == untrustedlist$[X] THEN
			PRINT "Untrusted ip found"
			found = 1
	IFZ found THEN
		PRINT "Log unmatched trusted ip as Untrusted"
		REDIM untrustedlist$[UBOUND(untrustedlist$[])+1]
		untrustedlist$[UBOUND(untrustedlist$[])] = value$
	PRINT "Initializing password check"

	REDIM log$[argc]
	FOR X = 0 TO argc
		log$[X] = "Argument ("+STRING(X)+"):"+ argv$[X]
		PRINT log$[X]
	pass_file$ = argv$[1]
  PRINT "Read user credentials:"; pass_file$
	XstLoadStringArray(pass_file$, @pass$[])

 	REDIM log$[UBOUND(log$[])+1]
	log$[UBOUND(log$[])] = "ERRLOG variable:" + dvalue$

	PRINT "Credentials read."
	offset = UBOUND(log$[])+1

	REDIM log$[UBOUND(log$[])+UBOUND(pass$[])]
	FOR X = 0 TO UBOUND(pass$[])
		log$[offset+X] = "Remote User/pass ("+STRING(X)+"):"+pass$[X]
		PRINT log$[offset+X]

	PRINT "Read user-database..."
	FileHandle = OPEN ("userdb.db", $$RD OR $$RWSHARE)
	IF FileHandle <> -1 THEN
		records = (LOF(FileHandle)/32)
		REDIM log$[UBOUND(log$[])+1]
		log$[UBOUND(log$[])] = "User records:" + STRING(records)
		REDIM #Users[records-1]
		READ [FileHandle], #Users[]
		CLOSE (FileHandle)
		REDIM log$[UBOUND(log$[])+1]
		log$[UBOUND(log$[])] = "User database could not be loaded. Terminating authentication process."
	PRINT "Userdatabase read completed."

	offset = UBOUND(log$[])+1
	REDIM log$[UBOUND(log$[])+UBOUND(dbval$[])]
	FOR X = 0 TO UBOUND(dbval$[])
		log$[offset+X] = "Local User/pass ("+STRING(X)+"):"+argv$[X]

	PRINT "Checking authentication."
  subname$ = pass$[0]
  subpass$ = pass$[1]
  subname$ = TRIM$(subname$)
  subpass$ = TRIM$(subpass$)

	FOR X = 0 TO UBOUND(#Users[])
		dbname$ = #Users[X].NAME
		dbpass$ = #Users[X].PASSWORD
		dbname$ = TRIM$(dbname$)
		dbpass$ = TRIM$(dbpass$)
		IF (dbname$ == subname$) && (dbpass$ == subpass$) THEN
			REDIM log$[UBOUND(log$[])+1]
			log$[UBOUND(log$[])] = "User/Pass both match, login succeeded"

	REDIM log$[UBOUND(log$[])+1]
	log$[UBOUND(log$[])] = "User/Pass mismatch, login failed"
	ExitProcess(1) 'QUIT(1) ' Unsuccessful, no logging on allowed.


What i consider to do next, is to make the machine execute a client-side script and let it submit some machine specific key as username / password combination and decrypt it based on the CA.key or the secret.key. (they both exist on server and client). This will require a one-time generation on the machine side to set it up and to be able to register the machine key in the database as well.

Now the hacker has an extra problem, he not only has to guess a user/password combination, he has to encrypt it first as well....

So i was wondering if someone else has some nice idea to add or alter this one with....

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...