How to connect to a corporate VPN on Linux using openconnect and vpn-slice

Want to use Linux at work, but your corporate VPN doesn't? Then this article may help, although it is not accurate. I want to warn you in advance that I do not understand network administration issues well, so it is possible that I did everything wrong. On the other hand, it is possible that I will be able to write a manual in such a way that it will be understandable to ordinary people, so I advise you to try it.

There is a lot of unnecessary information in the article, but without this knowledge I would not have been able to solve the problems that I suddenly had with the vpn setup. I think that anyone who tries to use this guide will have problems that I did not have, and I hope that this extra information will help you solve these problems yourself.

Most of the commands used in the manual need to be run via sudo, which has been removed for brevity. Keep in mind.

Most ip addresses have undergone severe obfuscation, so if you see an address like 435.435.435.435, there must be some normal ip specific to your case.

I have Ubuntu 18.04, but I think with a few changes the guide can be applied to other distributions. However, in this text, Linux == Ubuntu.

Cisco Connect

Those who are on Windows or MacOS can connect to our corporate VPN via Cisco Connect, which needs to specify the gateway address and enter a password each time it connects, consisting of a fixed part and a code generated by Google Authenticator.

In the case of Linux, it was not possible to start Cisco Connect, but it turned out to google the recommendation to use openconnect, made specifically to replace Cisco Connect.

open connect

In theory, Ubuntu has a special graphical interface for openconnect, but it didn’t work for me. Maybe it's for the best.

In Ubuntu, openconnect is installed from the package manager.

apt install openconnect

Immediately after installation, you can try to connect to the VPN

openconnect --user poxvuibr vpn.evilcorp.com

vpn.evilcorp.com is a fictitious VPN address
poxvuibr - fictitious username

openconnect will ask you to enter a password, which, I remind you, consists of a fixed part and a code from Google Authenticator, and then it will try to connect to the vpn. If it worked out, congratulations, you can safely skip the middle in which there is a lot of pain and go to the point about the work of openconnect in the background. If it doesn't work, then you can continue. Although if it worked out when connecting, for example, from a guest Wi-Fi at work, then it’s possible to rejoice and it’s too early, you should try to repeat the procedure from home.

Certificate

With a high probability, nothing will start, and the openconnect output will look something like this:

POST https://vpn.evilcorp.com/
Connected to 777.777.777.777:443
SSL negotiation with vpn.evilcorp.com
Server certificate verify failed: signer not found

Certificate from VPN server "vpn.evilcorp.com" failed verification.
Reason: signer not found
To trust this server in future, perhaps add this to your command line:
    --servercert sha256:4444444444444444444444444444444444444444444444444444444444444444
Enter 'yes' to accept, 'no' to abort; anything else to view: fgets (stdin): Operation now in progress

On the one hand, this is unpleasant, because there was no connection to the VPN, but on the other hand, how to fix this problem is basically clear.

Here the server sent us a certificate, by which we can determine that the connection is to the server of the native corporation, and not to an evil scammer, and this certificate is unknown to the system. And therefore it cannot check if the server is real or not. And so, just in case, it stops working.

In order for openconnect to still connect to the server, you need to explicitly tell it which certificate should come from the VPN server using the --servercert key

And you can find out which certificate the server sent us directly from what openconnect printed. From this piece:

To trust this server in future, perhaps add this to your command line:
    --servercert sha256:4444444444444444444444444444444444444444444444444444444444444444
Enter 'yes' to accept, 'no' to abort; anything else to view: fgets (stdin): Operation now in progress

With this command, you can try to connect again

openconnect --servercert sha256:4444444444444444444444444444444444444444444444444444444444444444 --user poxvuibr vpn.evilcorp.com

Maybe now it's working, then you can go to the end. But personally, Ubuntu showed me a fig in this form

POST https://vpn.evilcorp.com/
Connected to 777.777.777.777:443
SSL negotiation with vpn.evilcorp.com
Server certificate verify failed: signer not found
Connected to HTTPS on vpn.evilcorp.com
XML POST enabled
Please enter your username and password.
POST https://vpn.evilcorp.com/
Got CONNECT response: HTTP/1.1 200 OK
CSTP connected. DPD 300, Keepalive 30
Set up DTLS failed; using SSL instead
Connected as 192.168.333.222, using SSL
NOSSSSSHHHHHHHDDDDD
3
NOSSSSSHHHHHHHDDDDD
3
RTNETLINK answers: File exists
/etc/resolvconf/update.d/libc: Warning: /etc/resolv.conf is not a symbolic link to /run/resolvconf/resolv.conf

/ Etc / resolv.conf

# Generated by NetworkManager
search gst.evilcorpguest.com
nameserver 127.0.0.53

/run/resolvconf/resolv.conf

# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
# 127.0.0.53 is the systemd-resolved stub resolver.
# run "systemd-resolve --status" to see details about the actual nameservers.

nameserver 192.168.430.534
nameserver 127.0.0.53
search evilcorp.com gst.publicevilcorp.com

habr.com will be resolved, but it will be impossible to go there. Addresses like jira.evilcorp.com are not resolved at all.

What happened here, I do not understand. But the experiment shows that if you add the line to /etc/resolv.conf

nameserver 192.168.430.534

then the addresses inside the VPN will start to magically resolve and it will be possible to walk on them, that is, what looks for which DNS to resolve addresses looks in /etc/resolv.conf, and not somewhere else.

You can make sure that there is a connection to the VPN and it works without editing /etc/resolv.conf, for this it is enough to enter in the browser not the symbolic name of the resource from the vpn, but its ip address

As a result, there are two problems

  • when connecting to a VPN, its dns is not picked up
  • all traffic goes through vpn, which does not allow you to go to the Internet

I’ll tell you what to do now, but first, a little automation.

Automatic entry of the fixed part of the password

By now, you have most likely already entered your password at least five times, and this procedure has already tired you pretty much. Firstly, because the password is long, and secondly, because when entering it, you need to meet a fixed time period

The final solution to the problem was not included in the article, but you can make sure that the fixed part of the password does not have to be entered many times.

Let's say the fixed part of the password is fixedPassword and the part is from Google Authenticator 567 987. The entire openconnect password can be passed via standard input using the --passwd-on-stdin argument.

echo "fixedPassword567987" | openconnect --servercert sha256:4444444444444444444444444444444444444444444444444444444444444444 --user poxvuibr vpn.evilcorp.com --passwd-on-stdin

Now you can constantly return to the last entered command and change only part of the Google Authenticator there.

Corporate VPN does not allow you to go to the Internet.

In general, it is not very inconvenient when you have to use a separate computer in order to go to habr. The lack of the ability to copy-paste with stackoverfow can generally paralyze the work, so something needs to be done.

You need to somehow organize it so that when you need to access a resource from the internal network, Linux goes to vpn, and when you need to go to Habr, you go to the Internet.

openconnect after starting and establishing a connection with vpn, executes a special script, which is located in /usr/share/vpnc-scripts/vpnc-script. Some variables are passed to the input script, and it makes the vpn setup. Unfortunately, I could not figure out how to separate traffic flows between corporate vpn and the rest of the Internet using a native script.

Apparently, specifically for people like me, the vpn-slice utility was developed, which allows you to direct traffic through two channels without dancing with a tambourine. Well, that is, you will have to dance, but you don’t have to be a shaman.

Slicing traffic with vpn-slice

First, vpn-slice will have to be installed, you will have to figure it out on your own. If there are questions in the comments, I will write a separate post about this. But this is a normal python program, so there shouldn't be any difficulties. I installed using virtualenv.

And then the utility must be applied, using the --script key, specifying openconnect that you need to use vpn-slice instead of the standard script

echo "fixedPassword567987" | openconnect --servercert sha256:4444444444444444444444444444444444444444444444444444444444444444 --user poxvuibr --passwd-on-stdin 
--script "./bin/vpn-slice 192.168.430.0/24  " vpn.evilcorp.com 

The --script is passed a string with the command to be called instead of the script. ./bin/vpn-slice - path to the vpn-slice executable file 192.168.430.0/24 - mask of addresses to go to vpn. Here, it means that if the address starts with 192.168.430, then the resource with this address must be searched inside the vpn

Now the situation should be almost normal. Almost. Now you can go to Habr and you can go to the internal corporate resource by ip, but you can’t go to the internal corporate resource by symbolic name. If you register the correspondence of the symbolic name and address in hosts, everything should work. And work until the ip changes. Linux is now able to go to the Internet or to the intranet, depending on ip. But the non-corporate DNS is still used to resolve the address.

The problem can still manifest itself in this form - everything is fine at work, but at home you can access internal corporate resources only by ip. This is because when you are connected to a corporate Wi-Fi, the corporate DNS is also used, and symbolic addresses from the VPN are resolved in it, despite the fact that you still cannot go to such an address without using a VPN.

Automatic modification of the hosts file

If you politely ask vpn-slice, then after raising the VPN, it can go to its DNS, find the ip addresses of the necessary resources there by their symbolic names and enter them into hosts. After VPN is turned off, these addresses will be removed from hosts. To do this, you need to pass symbolic names to vpn-slice as arguments. Like this.

echo "fixedPassword567987" | openconnect --servercert sha256:4444444444444444444444444444444444444444444444444444444444444444 --user poxvuibr --passwd-on-stdin
--script "./bin/vpn-slice 192.168.430.0/24  jira.vpn.evilcorp.com git.vpn.evilcorp.com " vpn.evilcorp.com 

Now everything should work both in the office and on the beach.

Search for addresses of all subdomains in DNS given by VPN

If there are few addresses within the network, then the approach with automatic modification of the hosts file is quite working. But if there are a lot of resources on the network, then you will constantly need to add lines like zoidberg.test.evilcorp.com to the script zoidberg is the name of one of the test benches.

But now, when we understand a little what's what this need can be eliminated.

If, after raising the VPN, look in /etc/hosts, you can see this line

192.168.430.534 dns0.tun0 # vpn-slice-tun0 AUTOCREATED

And a new line has been added to resolv.conf. In short, vpn-slice somehow determined where the dns server for the vpn is located.

Now we need to make sure that in order to find out the ip address of a domain name ending in evilcorp.com, Linux goes to the corporate dns, and if something else is needed, then to the default one.

I googled for quite some time and found out that such functionality is in ubuntu out of the box. This refers to the ability to use the local dns server dnsmasq for resolving names.

That is, you can make sure that Linux always goes to the local dns server for ip addresses, which, in turn, depending on the domain name, will look for ip on the corresponding external dns server.

To manage everything related to networks and network connections, Ubuntu uses NetworkManager, and the GUI for choosing, for example, wifi connections is just a front to it.

We will need to climb in its configuration.

  1. Create a file in /etc/NetworkManager/dnsmasq.d/evilcorp

address=/.evilcorp.com/192.168.430.534

Note the dot before evilcorp. It signals to dnsmasq that all evilcorp.com subdomains should be looked for in the corporate dns.

  1. Tell NetworkManager to use dnsmasq to resolve names

The network-manager configuration is located in /etc/NetworkManager/NetworkManager.conf You need to add there:

[main] dns=dnsmasq

  1. Restart NetworkManager

service network-manager restart

Now, after connecting to the VPN using the openconnect and vpn-slice bundle, ip will be determined normally, even if you do not add symbolic addresses to the arguments to vpnslice.

How to go through a VPN to individual services

After I managed to connect to vpn, I was very happy for two days, and then it turned out that if you connect to vpn not from the office network, then mail does not work. The symptom is familiar, isn't it?

Our mail is located in mail.publicevilcorp.com, which means it does not fall under the rule in dnsmasq and the mail server address is searched through the public DNS.

Well, the office still uses DNS, in which this address is. I mean, I thought so. In fact, after adding the line to dnsmasq

address=/mail.publicevilcorp.com/192.168.430.534

the situation has not changed. ip remains the same. I had to go to work.

And only later, when I delved into the situation and understood the problem a little, one smart person told me how to solve it. It was necessary to connect to the mail server not just like that, but through vpn

I'm using vpn-slice to surf the VPN to addresses that start with 192.168.430. And not only the symbolic address of the mail server is not a subdomain of evilcorp, it also has an ip address that does not start with 192.168.430. And of course, he does not let anyone in from the general network.

In order for Linux to go through the VPN and to the mail server, you need to add it to vpn-slice as well. Let's say the mailer address is 555.555.555.555

echo "fixedPassword567987" | openconnect --servercert sha256:4444444444444444444444444444444444444444444444444444444444444444 --user poxvuibr --passwd-on-stdin
--script "./bin/vpn-slice 555.555.555.555 192.168.430.0/24" vpn.evilcorp.com 

Script to raise VPN with one argument

All this, of course, is not very convenient. Yes, you can save the text to a file and copy-paste it to the console instead of typing it by hand, but it's still not pleasant enough. To facilitate the process, you can wrap the command in a script that will be located in PATH. And then you will only need to enter the code received from Google Authenticator

#!/bin/sh  
echo "fixedPassword$1" | openconnect --servercert sha256:4444444444444444444444444444444444444444444444444444444444444444 --user poxvuibr --passwd-on-stdin 
--script "./bin/vpn-slice 192.168.430.0/24  jira.vpn.evilcorp.com git.vpn.evilcorp.com " vpn.evilcorp.com 

If you put the script in connect~evilcorp~ then you can just write in the console

connect_evil_corp 567987

But now, for some reason, you still have to keep the console in which openconnect is running open

Running openconnect in the background

Fortunately, the authors of openconnect took care of us and added a special key -background to the program, which makes the program run in the background after launch. If you run it like this, then the console can be closed after launch

#!/bin/sh  
echo "fixedPassword$1" | openconnect --servercert sha256:4444444444444444444444444444444444444444444444444444444444444444 
--user poxvuibr 
--passwd-on-stdin 
--background 
--script "./bin/vpn-slice 192.168.430.0/24  jira.vpn.evilcorp.com git.vpn.evilcorp.com " vpn.evilcorp.com  

Now it's just not clear where the logs go. In general, we don’t really need logs, but you never know. openconnect can redirect them to syslog where they will be kept safe and sound. you need to add the --syslog key to the command

#!/bin/sh  
echo "fixedPassword$1" | openconnect --servercert sha256:4444444444444444444444444444444444444444444444444444444444444444 
--user poxvuibr 
--passwd-on-stdin 
--background 
--syslog 
--script "./bin/vpn-slice 192.168.430.0/24  jira.vpn.evilcorp.com git.vpn.evilcorp.com " vpn.evilcorp.com  

And so, it turns out that openconnect is running somewhere in the background and does not bother anyone, but it is not clear how to stop it. That is, you can, of course, filter the output of ps with grep and look for a process in the name of which there is openconnect, but this is somehow dreary. Thanks to the authors for thinking about this. openconnect has a --pid-file switch that can be used to instruct openconnect to write its process ID to a file.

#!/bin/sh  
echo "fixedPassword$1" | openconnect --servercert sha256:4444444444444444444444444444444444444444444444444444444444444444 
--user poxvuibr 
--passwd-on-stdin 
--background  
--syslog 
--script "./bin/vpn-slice 192.168.430.0/24  jira.vpn.evilcorp.com git.vpn.evilcorp.com " vpn.evilcorp.com  
--pid-file ~/vpn-pid

Now you can always kill the process with the command

kill $(cat ~/vpn-pid)

If there is no process, kill will swear, but will not throw an error. If the file does not exist, then nothing terrible will happen either, so you can safely kill the process in the first line of the script.

kill $(cat ~/vpn-pid)
#!/bin/sh  
echo "fixedPassword$1" | openconnect --servercert sha256:4444444444444444444444444444444444444444444444444444444444444444 
--user poxvuibr 
--passwd-on-stdin 
--background 
--syslog 
--script "./bin/vpn-slice 192.168.430.0/24  jira.vpn.evilcorp.com git.vpn.evilcorp.com " vpn.evilcorp.com  
--pid-file ~/vpn-pid

Now you can turn on the computer, open the console and run the command by passing it the code from Google Authenticator. The console can then be beaten.

Without vpn-slice. Instead of an afterword

It turned out to be very difficult to understand how to live without vpn-slice. I had to read and google a lot. Fortunately, after spending so much time with a problem, technical manuals and even man openconnect read like exciting novels.

As a result, I found out that vpn-slice, like the native script, modifies the routing table to separate networks.

Routing table

This, to put it simply, is such a table in the first column of which contains what the address to which Linux wants to go should begin with, and in the second column through which network adapter to go to this address. In fact, there are more columns, but this does not change the essence.

In order to view the routing table, you need to run the ip route command

default via 192.168.1.1 dev wlp3s0 proto dhcp metric 600 
192.168.430.0/24 dev tun0 scope link 
192.168.1.0/24 dev wlp3s0 proto kernel scope link src 192.168.1.534 metric 600 
192.168.430.534 dev tun0 scope link 

Here each line is responsible for where you need to go in order to send a message to some address. The first is a description of what the address should begin with. In order to understand how to determine that 192.168.0.0/16 means that the address must begin with 192.168, you need to google what the ip address mask is. After dev is the name of the adapter to send the message to.

Linux made a virtual adapter for VPN - tun0. For the traffic for all addresses starting with 192.168 to go through it, the line is responsible

192.168.0.0/16 dev tun0 scope link 

You can also look at the current state of the routing table using the command route (IP addresses are smartly anonymized) This command produces results in a different form and is generally deprecated, but its output is often found in manuals on the Internet and you need to be able to read it.

What the ip address for the route should start with can be understood from the combination of the Destination and Genmask columns. Those parts of the ip address that correspond to the numbers 255 in Genmask are taken into account, and those where there are 0 are not. That is, the combination of Destination 192.168.0.0 and Genmask 255.255.255.0 means that if the address starts with 192.168.0, then the request to it will go along this route. And if Destination is 192.168.0.0 but Genmask is 255.255.0.0, then requests to addresses that start with 192.168 will go along this route

In order to figure out what vpn-slice actually does, I decided to look at the states of the tables before and after

Before turning on the VPN it was like this

route -n 

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         222.222.222.1   0.0.0.0         UG    600    0        0 wlp3s0
222.222.222.0   0.0.0.0         255.255.255.0   U     600    0        0 wlp3s0
333.333.333.333 222.222.222.1   255.255.255.255 UGH   0      0        0 wlp3s0

After calling openconnect without vpn-slice it became like this

route -n

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         0.0.0.0         0.0.0.0         U     0      0        0 tun0
0.0.0.0         222.222.222.1   0.0.0.0         UG    600    0        0 wlp3s0
222.222.222.0   0.0.0.0         255.255.255.0   U     600    0        0 wlp3s0
333.333.333.333 222.222.222.1   255.255.255.255 UGH   0      0        0 wlp3s0
192.168.430.0   0.0.0.0         255.255.255.0   U     0      0        0 tun0
192.168.430.534 0.0.0.0         255.255.255.255 UH    0      0        0 tun0

And after calling openconnect in combination with vpn-slice like this

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         222.222.222.1   0.0.0.0         UG    600    0        0 wlp3s0
222.222.222.0   0.0.0.0         255.255.255.0   U     600    0        0 wlp3s0
333.333.333.333 222.222.222.1   255.255.255.255 UGH   0      0        0 wlp3s0
192.168.430.0   0.0.0.0         255.255.255.0   U     0      0        0 tun0
192.168.430.534 0.0.0.0         255.255.255.255 UH    0      0        0 tun0

It can be seen that if you do not use vpn-slice, then openconnect explicitly writes that you must go through vpn to all addresses, except for those separately specified.

Right here:

0.0.0.0         0.0.0.0         0.0.0.0         U     0      0        0 tun0

There, next to it, another path is immediately indicated, which must be used if the address that Linux is trying to go to does not match any of the masks from the table.

0.0.0.0         222.222.222.1   0.0.0.0         UG    600    0        0 wlp3s0

It is already written here that in this case it is necessary to go through a standard Wi-Fi adapter.

I believe the VPN path is used because it is the first one in the routing table.

And theoretically, if you remove this default path from the routing table, then in conjunction with dnsmasq openconnect should ensure normal operation.

I tried

route del default

And everything worked.

Routing requests to the mail server without vpn-slice

But I also have a mail server with the address 555.555.555.555, which also needs to be accessed via vpn. The route to it must also be added by hand.

ip route add 555.555.555.555 via dev tun0

And now everything is normal. So you can still do without vpn-slice, but you already need to know well what you are doing. Now I'm thinking about adding to the last line of the native openconnect script deleting the default route and adding a route for the mailer after connecting to the vpn, just so that there are fewer moving parts in my bike.

Probably, in order to understand how to set up a VPN, this afterword would be enough for someone. But while I was trying to understand what and how to do, I read a lot of such manuals that work for the author, but for some reason do not work for me and decided to add here all the pieces that I found. I would be very happy with something like that.

Source: habr.com

Add a comment