Regalis Technologies log

Regalis Technologies

technical blog about GNU/Linux, C++, security, electronics

Sat 31 May 2025 in ssh by Regalis

How to update an offline GNU/Linux system using SSH?

Introduction

Throughout my many years in business, I have constantly encountered servers connected to the Internet... Although it is not necessary. Keeping your server offline is one of the most effective ways to avoid serious security issues such as data leaks and remote takeovers.

What is the most common reason for connecting servers to the Internet? Well, the most common reason I hear is "I have to update it somehow."

There are a few ways to update offline servers very conveniently. Today I'll show you how to do it very easily using ssh. It's not the optimal technique for many servers, but you might find it useful.

A more efficient method for updating multiple hosts is described in my other article about why you should keep most of your infrastructure offline.

Basic idea

Let's say we have the following infrastructure:

Offline Debian GNU/Linux behind the jumphost

In this configuration, your destination host (Debian GNU/Linux) has no access to the Internet, so you won't be able to run apt update && apt upgrade.

SSH reverse socks proxy

You can use your SSH client to act as a socks proxy and simply perform a remote port forwarding to this proxy. Remote port forwarding works in the way that connections to the given TCP port or Unix socket on the remote (server) host are to be forwarded to the local side. This way, your destination host will be able to use your machine to access the official Debian GNU/Linux repositories.

And you can do this by using a single SSH option: -R.

Offline Debian GNU/Linux behind the jumphost

As you can see, it works even if you have a jumphost on your way.

Verify your connection

Normally, if you just connect to your offline host and use curl, you should expect something like this:

1
2
3
4
user@laptop:~$ ssh -J jumphost debian.domain.com
# (...)
root@dom0:~# curl https://regalis.tech
curl: (6) Could not resolve host: regalis.tech

This will be exactly the same if you use -R9999:

1
2
3
4
user@laptop:~$ ssh -J jumphost debian.domain.com -R9999
# (...)
root@dom0:~# curl https://regalis.tech
curl: (6) Could not resolve host: regalis.tech

This is because you need to tell the curl to use your socks proxy, you can do this with a simple environment variable called ALL_PROXY:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
✅ root@dom0:~# ALL_PROXY=socks5h://localhost:9999 curl https://regalis.tech
<!DOCTYPE html>
<html lang="pl">
<head>
# (...) 

❌ root@dom0:# curl --socks5 localhost:9999 https://regalis.tech
curl: (6) Could not resolve host: regalis.tech


✅ root@dom0:# curl --socks5-hostname localhost:9999 https://regalis.tech
<!DOCTYPE html>
<html lang="pl">
<head>
# (...) 

Names resolution - socks5h:// vs socks5://

Did you notice socks5h:// or --socks5-hostname? Use this if you want your host/program to resolve hostnames using SOCKS Proxy instead of system-configured DNS servers. Since your system is offline, it won't be able to resolve domain names on its own.

Upgrade your system using apt

How to tell apt to use your reverse socks proxy?

1
2
3
4
5
6
7
✅ root@dom0:# apt -o 'Acquire::http::proxy=socks5h://localhost:9999' update
(...)

✅ root@dom0:~# apt -o 'Acquire::http::proxy=socks5h://localhost:9999' upgrade
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done

How to persist apt's proxy configuration?

Just put a line Acquire::http::proxy "socks5h://localhost:9999;" inside your configuration file, for example /etc/apt/apt.conf.d/99-ssh-socks-proxy.conf:

1
2
3
4
5
6
7
8
root@dom0:~# cat > /etc/apt/apt.conf.d/99-ssh-socks-proxy.conf <<< 'Acquire::http::proxy "socks5h://localhost:9999";'


✅ root@dom0:# apt update
(...)

✅ root@dom0:# apt upgrade
(...)

Filter your socks proxy connections

SSH client has an option called PermitRemoteOpen, you can use it to filter connections. For example, you can allow only deb.debian.org:443 and security.debian.org:443 using:

1
2
3
4
5
6
7
8
user@laptop:~$ ssh -J jumphost debian.domain.com -R9999 -o 'PermitRemoteOpen=deb.debian.org:443 security.debian.org:443'


❌ root@dom0:# curl --socks5 localhost:9999 https://regalis.tech
curl: (97) connection to proxy closed

✅ root@dom0:# apt update
(...)

As you can see, connection to regalis.tech:443 was blocked, but apt is still happy!

Of course, you can use your SSH config file (on the client side) to write down all these options and make a very easy to use shortcut. You can even update and upgrade remote, offline host with a single line, including jumping over the jumphost:

1
2
3
4
5
6
7
user@laptop:~$ ssh regalis-tech-dom0-with-rev-socks-proxy 'apt update && apt dist-upgrade'
(...)
Reading package lists...
Building dependency tree...
Reading state information...
Calculating upgrade...
(...)

I described the ssh configuration in a previous post.

Summary

SSH is an incredibly powerful tool, and many administrators are not fully aware of its capabilities. A key takeaway from this post should also be security - please pay attention to the following points:

And as always, remember: When it comes to cybersecurity - no magic security tool, no matter how or by whom it's marketed, can replace good design, knowledge, and experience. Know your tools!

Happy hacking! (and remove your PuTTY 😉)