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

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







The article has a lot of extra information, but without this knowledge I would not be able to solve the problems that I suddenly had with the vpn setting. I think that anyone who tries to apply this manual will have problems that I did not have, and I hope that this extra information will help to solve these problems on my own.







Most of the commands used in the manual need to be executed through sudo, which is removed for brevity. Keep in mind.







Most ip addresses were severely obfuscated, so if you see an address like 435.435.435.435, there should be some normal ip specific to your case.







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







Cisco connect



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







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







Openconnect



In theory, in ubunt there is a special graphical interface for openconnect but it did not work for me. Maybe it's for the best.







In ubunt 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 the fictitious VPN address \

poxvuibr - fictitious username







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







Certificate



With high probability, nothing will start, and the openconnect exhaust 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.







Then the server sent us a certificate, according to which it can be determined that the connection is made to the server of the native corporation, and not to the malicious fraudster, and this certificate is unknown to the system. And so she cannot check if the real server is 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 to us directly from what openconnect printed. Here 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 it’s working now, then you can go to the end. But Ubunta personally 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 not be possible to go there. Addresses like jira.evilcorp.com will not resolve at all.







What happened here is not clear to me. 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 magically resolve and it will be possible to go around them, that is, what DNS looks for to resolve the addresses looks in /etc/resolv.conf, and not somewhere else.







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







The result is two problems









What I will do now I’ll tell you, but first a little automation.







Automatic entry of the fixed part of the password



By the current moment, you most likely have already entered the password at least five times and this procedure has already pretty tired you. Firstly, because the password is long, and secondly, because when entering, you need to keep within a fixed time period







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







Suppose that the fixed part of the password is fixedPassword, and part of the Google Authenticator 567 987. The whole password of openconnect can be passed through 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 command entered and change only part of Google Authenticator there.







Corporate VPN does not allow Internet access.



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







It is necessary to organize somehow, so that when you need to go to the resource from the internal network, Linux go to vpn, and when you need to go to the hub, go to the Internet.







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







Apparently specially 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 have to dance, but a shaman is not necessary.







Split traffic with vpn-slice



Firstly, 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 regular python program, so there should be no difficulties. I set using virtualenv.







And then you need to apply the utility, using the --script key indicating openconnect that instead of the standard script you need to use vpn-slice







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





In --script, a line is passed with the command that needs 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 be visited in vpn. Here, I mean that if the address starts with 192.168.430, then the resource with this address must be searched inside vpn







Now the situation should be almost normal. Nearly. Now you can log in to the hub and you can log in to the internal corporate resource by ip, but you cannot log into 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 ip changes. Linux is now able to go to the Internet or to the corporate network, depending on ip. But non-corporate DNS is still used to determine the address.







The problem can still appear in this form - everything is fine at work, and at home you can access internal corporate resources only by ip. This is because when you are connected to corporate Wi-Fi, the corporate DNS is also used, and in it the symbolic addresses from the VPN resolve, although it is still impossible to 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 in hosts. After turning off the VPN, these addresses will be deleted from hosts. To do this, 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 the DNS that the VPN gave



If there are few addresses within the network, then the approach with automatically modifying 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 this is the name of one of the test stands.







But now that we have a little understanding of why this need can be eliminated.







If, after raising the VPN, look in / etc / hosts, you can see, here is a line







192.168.430.534 dns0.tun0 # vpn-slice-tun0 AUTOCREATED

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







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







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







That is, you can make it so 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, the Ubunt uses NetworkManager, and the graphical interface for selecting, for example, a Wi-Fi connection is just the front to it.







We will need to climb in its configuration.







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


address = /. evilcorp.com/192.168.430.534

Pay attention to the point before evilcorp. It signals dnsmasq that all evilcorp.com subdomains must be searched 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 a VPN using a bunch of openconnect and vpn-slice, ip will be detected normally even if you do not add symbolic addresses to the arguments to vpnslice.







How to go through VPN to separate services



After it turned out 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, mail does not work. A familiar symptom, isn't it?







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







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







address = / mail.publicevilcorp.com / 192.168.430.534

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







And only then, when I delved into the situation and sorted out 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 via vpn







I use vpn-slice to go through the VPN to addresses that start with 192.168.430. And on the mail server, not only the symbolic address is not a subdomain of evilcorp, it also has an IP address that does not start with 192.168.430. And of course he doesn’t let anyone out of 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. Suppose the address of the mailer 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 a 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, rather than typing with your hands, but it’s still not enough pleasant. To facilitate the process, you can wrap the command in a script that will be located in PATH. And then you only need to enter the code obtained 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 ~ you can just write in the console







 connect_evil_corp 567987
      
      





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







Openconnect launch in background



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







 #!/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 not clear where the logs go. Logs are generally not really needed for us, but you never know. openconnect can redirect them to syslog, where they will be kept intact. 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 works somewhere in the background and does not bother anyone, but how to stop it is not clear. That is, you can, of course, filter out the ps exhaust with a grep and look for a process in the name of which is openconnect, but it is somehow dreary. Thanks to the authors who thought about this. Openconnect has the --pid-file key, with which you can 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 nail the process with the command







 kill $(cat ~/vpn-pid)
      
      





If there is no process, kill will swear, but it will not throw an error. If there is no file, then nothing bad 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, passing it the code from Google Authenticator. The console can then be nailed.







Without vpn-slice. Instead of an afterword



It was very difficult to understand how to live without vpn-slice. I had to read a lot and google. Fortunately, when I spent so much time with the 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



To put it simply, such a table in the first column of which contains where the address where Linux wants to go should begin, and in the second 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 see 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 to go in order to send a message to some address. The first is a description of where the address should begin. 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 mask of the ip address is. After dev is the name of the adapter to which the message should be sent.







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







 192.168.0.0/16 dev tun0 scope link
      
      





You can also look at the current state of the routing table using the route -n command (ip addresses are anonymously talented) This command gives the results in a different form and is generally deprecated, but its exhaust often comes across online manuals and you need to be able to read it.







Where the ip address for the route should start can be understood from the combination of the Destination and Genmask columns. Those parts of the ip address to which the numbers 255 correspond 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 a 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 begin with 192.168 will go along this route







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







Before enabling 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 so



 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 separately indicated.







Right here:







 0.0.0.0 0.0.0.0 0.0.0.0 U 0 0 0 tun0
      
      





Nearby, another path is immediately indicated that must be used if the address that Linux is trying to go through does not match any mask from the table.







 0.0.0.0 222.222.222.1 0.0.0.0 UG 600 0 0 wlp3s0
      
      





It has already been written here that in this case you need to go through the standard Wi-Fi adapter.







I believe that the path for the VPN is used because it is the first 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 it worked.







Routing requests to a mail server without vpn-slice



But I also have a mail server with the address 555.555.555.555, which you also need to go through vpn. The route to it must also be added by hand.







 ip route add 555.555.555.555 via dev tun0
      
      





And now all the rules. So you can do without vpn-slice, but you already need to know well what you are doing. I’m now thinking about adding the removal of the default route and adding the route for the mailer after connecting to vpn to the last line of the openconnect native script, just to make the number of moving parts in my bike smaller.







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








All Articles