Checkpoint SNX adventure: isolating the proprietary in it own box April 26, 2021 on Savely Krasovsky's blog

My company uses Checkpoint products as a VPN solution. In case of Windows and macOS there is Checkpoint Endpoint Security. In case of Linux we have to use whatever called Checkpoint SSL Network Extender or just Checkpoint SNX.

As far as I got, it encapsulates traffic into HTTPS connection between you and VPN gateway and provides TUN interface. Sounds okay on paper, right?

But as you know, proprietary software has something in common: the quality of Linux support generally sucks. Checkpoint SNX is not an exception.

Just read their support page. The distros they consider relevant in 2021: Ubuntu 17.04, Fedora 18, openSUSE 12.2. Okay, cool.

The shitty quality of Linux solution itself wasn’t the main reason to isolate it. Gateways like this are pushing settings from the server including routes, usually there is no way to somehow reconfigure this.

In my case, the company decided that split tunneling isn’t secure. It means while I am connected and trying to reach corporate resources, I cannot reach the Internet without corporate proxy and vice versa.

So I had an idea: it would be nice to just share Checkpoint VPN connection with other computers.

Particularly I wanted to:

  1. Completely isolate Checkpoint SNX inside Linux VM.
  2. Use WireGuard tunnels to share VPN connection.
  3. At client-side use only one simple route: 10.0.0.0/8.

Why WireGuard? I could just use IP forwarding and NAT tweaks, but it would only work within a particular LAN and I would have to create and delete route and change DNS servers every time manually.

Let’s get started.

Install Checkpoint SNX

You can download SNX installer from your company’s VPN portal:

$ curl https://example.org/SNX/INSTALL/snx_install.sh -o snx_install.sh
# OR
$ curl https://example.org/sslvpn/SNX/INSTALL/snx_install.sh -o snx_install.sh

There is also a way to install the latest version from Checkpoint site, but I don’t recommend it, your company could use different version.

While the first part of script is just a bash script, the second part contains an archive with the binaries. The binaries are x86 (not x86-64), so you need to enable multiarch and install dependencies.

# ldd /usr/bin/snx
    ...
    libX11.so.6 => /usr/lib32/libX11.so.6
    libpam.so.0 => /usr/lib32/libpam.so.0 (0xf7d3d000)
    libstdc++.so.5 => /usr/lib32/libstdc++.so.5 (0xf7c5c000)
    ...

So you need to install three libraries: libX11, libpam, libstdc++. In various distros you need to use various names, but you can easily google it.

Finally, just install it:

$ chmod +x snx_install.sh
$ sudo ./snx_install.sh

Back to your browser and try to connect using the portal. A while back you needed a browser that supports NPAPI to launch Java-applet, but currently it seems like Checkpoint SNX uses the new mechanism. That’s why most probably a webpage will ask you to install whatever called cshell, but not csh, you will have to download another script cshell_install.sh.

Important

Before installation install OpenJRE, in my case only the latest 15.0 worked well.

$ chmod +x cshell_install.sh
$ sudo ./cshell_install.sh

In case of any errors probably you have a wrong OpenJRE version. Fortunately you can test it even after failed installation process:

$ java -jar /usr/bin/cshell/CShell.jar

Note

Scrips are idempotent, so you can run them indefinitely until getting a fix

After successful installation of snx and cshell there is another step: this jar-file runs HTTPS-server at localhost:14186, but installer fails to make certificate trusted. There are two ways to fix it:

Now try to reconnect again. Everything should now work.

WireGuard tunnel

I don’t want to tell here how WireGuard works. I will just provide an example of working configs:

Peer A (VM with SNX):

[Interface]
PrivateKey = ... # ABC
ListenPort = 51820
Address = 192.168.128.1/24

# Host PC
[Peer]
PublicKey = ... # XYZ' public key
AllowedIPs = 192.168.128.2/32

# You add here another peers:
#
# Mobile phone
# [Peer]
# PublicKey = DEF
# AllowedIPs = 192.168.128.3/32

Peer B (PC with which I want to share):

[Interface]
PrivateKey = ... # XYZ
Address = 192.168.128.2/32
DNS = DNS1, DNS2 # found out you VPN DNS server and hard code them here

[Peer]
PublicKey = ... # ABC's public key
AllowedIPs = 10.0.0.0/8
Endpoint = 192.168.1.8:51820 # Use an IP of your bridged VM

On both sides enable peers. You can test tunnel simply pinging thought it:

# from device to VM with SNX
$ ping 192.168.128.2

Firewall

Of course without proper firewall setup, an SNX connection won’t share. I will use modern nftables.

/etc/nftables.conf:

#!/usr/bin/nft -f
# ipv4/ipv6 Simple & Safe Firewall
# you can find examples in /usr/share/nftables/

flush ruleset

table inet filter {
	chain input {
		type filter hook input priority 0; policy drop;

		# allow established/related connections
		ct state {established, related} accept

		# early drop of invalid connections
		ct state invalid drop

		# allow from loopback
		iifname lo accept

		# allow icmp
		ip protocol icmp accept
		meta l4proto ipv6-icmp accept

		# allow wireguard
		udp dport 51820 accept

		# everything else
		reject with icmpx type port-unreachable
	}

	chain forward {
		type filter hook forward priority 0; policy drop;

		ct state {established, related} accept
		ct state invalid drop

		# allow forwarding from wireguard interface to snx one
		iifname wg0 oifname tunsnx ct state new accept
	}

	chain output {
		type filter hook output priority 0; policy accept;
	}
}

table inet nat {
	chain postrouting {
		type nat hook postrouting priority srcnat;
	}
}

Note

Don’t forget to enable IP forwarding:

net.ipv4.ip_forward=1

net.ipv6.conf.all.forwarding=1

To share SNX connection you will have to configure SNAT dynamically after connection (and disconnection). I will use systemd oneshot-service to do this.

/etc/systemd/system/snx-firewall.service:

[Unit]
Description=Checkpoint SNX firewall tweaker
BindsTo=sys-subsystem-net-devices-tunsnx.device
After=sys-subsystem-net-devices-tunsnx.device

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/root/bin/snx_add_rule.sh
ExecStop=/root/bin/snx_flush_chain.sh

[Install]
WantedBy=sys-subsystem-net-devices-tunsnx.device

The mix of BindsTo and RemainsAfterExit allows us to trigger ExecStart and ExecStop at the right time, when connection starts and when connection stops.

As you could see above, service also requires two scripts:

/root/bin/snx_add_rule.sh:

#!/bin/bash

GATEWAY=$(ip -o -4 addr show tunsnx | awk '{printf $4}')
nft add rule inet nat postrouting oifname "tunsnx" ip daddr 10.0.0.0/8 snat ip to $GATEWAY
echo "nftables rule added"

/root/bin/snx_flush_chain.sh:

#!/bin/bash

nft flush chain inet nat postrouting
echo "nftables flushed"

This is where all the magic is going on. Simple masquerading doesn’t help in this case. Reconnect you SNX and try to open corporate resource. Works? Cool!

Feel free to leave comments below the post.

UPDATE: I can also recommend removing default cshell autostart (from ~/.config/autostart/cshell.desktop) because it’s not reliable and setup user systemd service:

[Unit]
Description=Checkpoint SNX CShell
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
Environment=DISPLAY=:0
WorkingDirectory=/usr/bin/cshell
# Should work for JRE 16+
ExecStart=/usr/bin/java --add-exports java.base/sun.net.spi=ALL-UNNAMED -jar CShell.jar /tmp/cshell.fifo
ExecStop=/bin/kill -15 $MAINPID
SuccessExitStatus=143
Restart=on-failure

[Install]
WantedBy=default.target

UPDATE 2: Rui Ribeiro from comments suggested much nicer method using chroot, check it!