WARNING! NONE OF THE PERFORMED CONFIGURATIONS CONSIDER SPECIFIC PROJECTS' SECURITY REQUIREMENTS. USE THIS ARTICLE AS A SECONDARY
SOURCE OF KNOWLEDGE ONLY AND HARDEN YOUR SYSTEMS AS APPROPRIATE.
*upd 12/26/2024: Dev Community formatting has gone nuts, I will eventually rewrite all or some of my blog posts on my personal website. Stay tuned to get the link.
Contents
Preface
If you are an active computer user, you probably often get a lot of new files from the outside world. The environment is harsh, and it's quite easy to infect a device with malware. To reduce the chances of being infected, it's a good practice to scan everything with special software. For small files that don't contain any confidential info, services like VirusTotal can be utilized. For other data, a standalone or a Live CD-based antivirus application should be preferred.
One of the most popular Free and Open Source applications of this kind is ClamAV by Cisco Talos. The tool is the de facto standard antivirus for UNIX-like systems (both on servers and clients).
To be able to find threats, antivirus software needs a special database of virus signatures. These signatures are continuously created by large teams of security specialists.
Why set up a private mirror?
Well, there might be lots of reasons. The most significant one coming straight to mind is to reduce network traffic and to increase the database update speed for clients.
The decision to host a private virus signature database mirror for ClamAV at my Homelab emerged because I use the tool a lot, and it's not available in my region.
I encourage you to try out this small project even for your Homelab, because it is really easy!
Setting up a server
What tools are used
Since the aforementioned Homelab is based on the Proxmox VE hypervisor, everything is set up there, in a container.
In this scenario,
freshclam
is used for fetching database files to a mirror server. It's not possible to serve CDIFF files using this method. Consider cvdupdate if you need to reduce network bandwidth consumption even more during clients' database updates.
What | Tool |
---|---|
Hypervisor | Proxmox VE 7.3-4 |
Guest | Fedora Linux 37 (Container Image) |
Antivirus (mirror) | ClamAV 0.103.7 |
Anonymous communication | Tor 0.4.7.13 |
Creating an LXC container on Proxmox
-
Select your storage pool for VM disks and container templates, go to
CT Templates
and click on theTemplates
button. -
Find the latest Fedora version in the list and download it.
Click on the
Create CT
button at the top of the Proxmox dashboard and follow the wizard. It's ok to designate 1 CPU core to the container, but it needs sufficient RAM to update databases. Set the limit to something around 4096 MiB.-
Don't forget to set an option to start the container at boot.
Start up the container and connect to its console.
A local Tor proxy (SOCKS5) with obfs4 bridges
In countries where Tor is censored, bridges can be used.
Some operating systems' repositories don't provide pre-built packages for obfs4, but the tool can be easily compiled from source code. If you are interested in a quick guide on how to compile obfs4, let me know!
Install Tor
Thankfully, Fedora has everything we need in its repositories <3.
# dnf install tor obfs4
Configure Tor
-
Locate the
obfs4proxy
binary
$ which obfs4proxy
Example
[deathroll@ClamAV-DB ~]$ which obfs4proxy /usr/bin/obfs4proxy [deathroll@ClamAV-DB ~]$
-
Open the
/etc/tor/torrc
config file with an editor of choice. Mine is vim.
# vim /etc/tor/torrc
-
Next, add the following two lines:
ClientTransportPlugin obfs4 exec /usr/bin/obfs4proxy UseBridges 1
👆 Replace the path after
exec
with what you got in step 1. -
Now, get some bridges!
Go to https://bridges.torproject.org/options/, selectobfs4
, and click onGet Bridges
.In order to get a list of bridges, complete a captcha. When the list is displayed, just copy it as-is.
-
Paste what you've just copied to the
torrc
file and prefix each line withBridge
. Here's how it will look in the file (the output was modified due to privacy concerns):
[deathroll@ClamAV-DB ~]$ grep -i ^bridge /etc/tor/torrc Bridge obfs4 <some.ip.address.here:port> <ALONGSTRINGOFSOMEJIBBERISH> cert=<ONEMORELONGSTRINGOFSOMEJIBBERISH> iat-mode=<number> Bridge obfs4 <some.ip.address.here:port> <ALONGSTRINGOFSOMEJIBBERISH> cert=<ONEMORELONGSTRINGOFSOMEJIBBERISH> iat-mode=<number> [deathroll@ClamAV-DB ~]$
-
After saving and closing the config file, perform a test connection by executing
tor
astoranon
.
$ sudo -u toranon tor
-
In case of success, kill the process with
Ctrl + C
, and enable and start the service.
# systemctl enable --now tor
-
Check the status with the following command:
# systemctl status tor
ClamAV with automatic database updates
Install ClamAV
# dnf install clamav clamd clamav-update
Configure freshclam
-
Open the
/etc/freshclam.conf
file and add the following:
HTTPProxyServer socks5://127.0.0.1 HTTPProxyPort 9050
-
Enable database compression (optional).
CompressLocalDatabase yes
-
Also, you can enable logging (optional). For more, read freshclam.conf(5).
-
Add this to the config:
UpdateLogFile /var/log/freshclam.log LogTime yes LogSyslog yes
-
Create the log file.
# touch /var/log/freshclam.log
-
And change its owner to
clamupdate
.
# chown clamupdate: /var/log/freshclam.log
-
-
After that, locate the
clamav-freshclam
service file.
$ find /etc/systemd/ -name '*clam*'
Example
[root@ClamAV-DB deathroll]# find /etc/systemd/ -name '*clam*' /etc/systemd/system/multi-user.target.wants/clamav-freshclam.service [root@ClamAV-DB deathroll]#
-
Open the file you've just found and append
tor.service
to the lines that begin withWants
andAfter
so they look like this:
Wants=network-online.target tor.service After=network-online.target tor.service
-
Reload the systemd manager configuration.
# systemctl daemon-reload
-
Enable and start the
clamav-freshclam
service.
# systemctl enable --now clamav-freshclam
-
Check the status with the following command:
# systemctl status clamav-freshclam
Web server with HTTPS
In this example, we use NGINX, but you're free to experiment.
Prepare a directory for serving
-
Create a new directory. For me it is
/srv/cvd
.
# mkdir -p /srv/cvd
-
Find where the database is located.
# find / -name clamav -xtype d |& grep -iv permission
👆
-xtype d
makesfind
search for directories only.|&
pipes bothstdout
andstderr
to grep. Here we are just filtering the output to remove read errors—don't worry, they are insignificant.In my case, the dir is
/var/lib/clamav
.Example
[deathroll@ClamAV-DB nginx]$ sudo find / -name clamav -xtype d |& grep -iv permission /usr/share/licenses/clamav /var/lib/clamav [deathroll@ClamAV-DB nginx]$
Without grep filtering
[deathroll@ClamAV-DB nginx]$ sudo find / -name clamav -xtype d find: ‘/proc/tty/driver’: Permission denied /usr/share/licenses/clamav /var/lib/clamav find: ‘/dev/.lxc/sys/kernel’: Permission denied find: ‘/dev/.lxc/sys/power’: Permission denied find: ‘/dev/.lxc/sys/class’: Permission denied find: ‘/dev/.lxc/sys/devices’: Permission denied find: ‘/dev/.lxc/sys/dev’: Permission denied ---OUTPUT TRIMMED---
-
Edit
/etc/fstab
to set a read-only bind mount from the database dir to the newly created one.
/var/lib/clamav/ /srv/cvd none bind,ro 0 0
-
Reload the systemd manager configuration.
# systemctl daemon-reload
-
Try to mount
/srv/cvd
.
# mount /srv/cvd
-
Check the mountpoint.
$ findmnt /srv/cvd
Example
[deathroll@ClamAV-DB nginx]$ findmnt /srv/cvd TARGET SOURCE FSTYPE OPTIONS /srv/cvd NAS/Virtualization/subvol-1000-disk-0[/var/lib/clamav] zfs ro,noatime,xattr,posixacl [deathroll@ClamAV-DB nginx]$
Install NGINX
# dnf install nginx
Copy certificates
In order to be able to use HTTPS, it's required to have valid certificates. Refer to this article to learn how to create a trusted self-signed certificate.
-
Create a directory for certificates in a preferred location.
# mkdir /etc/ssl/www
Copy a chain of trust file and a key file to the newly created directory.
Configure NGINX
In this example, it's assumed that a DNS A record exists for the container's static IP address. The record name is
cvd.deathroll.internal
. It's also possible to implement HTTPS without DNS, though not recommended.The configuration is split up into multiple files for ease of administration, but this is not required.
-
Create a file with shared SSL/TLS configuration for server blocks. Choose a filename suitable for you.
# vim /etc/nginx/ssl_params.conf
-
Add at least two things: locations for the chain of trust and key files.
ssl_certificate "/etc/ssl/www/fullchain.pem"; ssl_certificate_key "/etc/ssl/www/deathroll-internal-key.pem";
-
Change any other options at your discretion. For example:
ssl_prefer_server_ciphers on; ssl_protocols TLSv1.2 TLSv1.3;
-
-
Change the default server block in the
/etc/nginx/nginx.conf
file to the following:
server { listen 80; server_name _; # A catch-all server name for handling requests to any host. location / { return 301 https://$host$request_uri; # Redirect to HTTPS. } }
-
Create a file for a <domain> HTTPS server block in
/etc/nginx/conf.d
.
vim /etc/nginx/conf.d/cvd.deathroll.internal.conf
And populate it with contents similar to these:
server { listen 443 ssl; server_name cvd.deathroll.internal; root /srv/cvd; # This is the bind mount directory you have created earlier. include /etc/nginx/ssl_params.conf; # Include shared TLS/SSL parameters. location / { autoindex on; # Enable directory listing. } }
-
Create a file with a server block for denying requests to any host. For example,
/etc/nginx/conf.d/deny-conn-by-ip.conf
. Add something like this:
server { listen 443 default_server; # Make this server default for port 443 to be able to catch unwanted requests. server_name _; include /etc/nginx/ssl_params.conf; location / { return 403; } }
👆 From what I could infer from inspecting packets, observing NGINX behavior, and reading its documentation, when NGINX receives a request, it checks the
Host
HTTP header to decide what virtual host should process the request.The catch-all (
_
) server for port 443 has adefault_server
parameter, so HTTPS requests to anyHost
will be processed by this virtual host. If this parameter is omitted, any other virtual server for this port may become the default server, and hence requests won't be denied. -
Reload NGINX config.
# systemctl reload nginx
-
Check NGINX status.
# systemctl status nginx
Enabling firewall
In this example, firewalld is used.
Install firewalld
# dnf install firewalld
Configure firewalld
-
Enable and start the service
# systemctl enable --now firewalld
-
Add ports. If you use SSH, add it as well.
# firewall-cmd --permanent --add-port={80,443}/tcp
-
Reload firewalld
# firewall-cmd --complete-reload
Configuring clients
Disable database compression (optional) and add a private mirror in the freshclam.conf
file, so these options look something like this:
deathroll@fdesk:~ % egrep -i '^(private|compress)' /usr/local/etc/freshclam.conf
CompressLocalDatabase no
PrivateMirror https://cvd.deathroll.internal
deathroll@fdesk:~ %
The result
After the setup is complete, what we have is a Fedora 37 container with a local Tor-based SOCKS5 proxy that is used for updating the ClamAV database. On top of this, we have an NGINX web server that acts as a private mirror that allows clients to list and download database files for ClamAV. This web server redirects all requests to HTTPS and denies requests to any host different from the server's FQDN.
Top comments (0)