There is a simple rule for hardening a system: You can not hack what is not there.
With this in mind, removing as much software from a system as possible makes it simpler to understand and then control.
For this guide you will need a DNS name. You can get one for free from deSEC or register your own.
Why do this extra work?
Because QNAP has a terrible track record on security. They trade more features for understandability. Violating the KISS principle or Economy of mechanism a key part of system security.
Some of the last attacks were using the default webserver to access privileged services. Disabling it closes that attack vector.
Setup NAS
Assign a static IP
Assign a static IPv4 and a IPv6 address to all the adapters or bridges you use. Even if you use private IPs only, the DHCP server can forget about your current lease and assign the NAS a new IP and then a lot of stuff breaks.
Side note: It is good practice to have your static (server) assignments outside of your dynamic range.
Example:
- Router: 10.0.0.1/24 ; fd02:cafe::1/64
- Dynamic Range: 10.0.0.100 - 10.0.0.200 ; fd02:cafe::d:0 - fd02:cafe::d:ff
- Static Range: 10.0.0.10 - 10.0.0.99 ; fd02:cafe::10 - fd02:cafe::ff
Or use a globally unique IPv6 range from ISP or your datacenter (eg. ipv6onlyhosting) and never worry about overlapping ip ranges again when connecting networks (with VPN).
Remove or Stop already installed apps
Go to App Center
then remove everything you don't need except:
- QuFirewall (install it if you don't have it)
- License Center (sadly)
- Malware Remover
- Disable periodic scans
- Set to auto update
- Enable scan on update
- Network & Virtual Switch
- Notification Center
- QTS SSL Certifiacte
- QuLog Center
- Resource Monitor
Most people will also keep * Container Station (docker with docker-compose) * Virtualization Station (kvm)
Everything else can go or be replaced by containers like replace Video Station with jellyfin in a container
Minimal Configuration
Go to ControlPanel
-> System
and set the following settings:
- General Settings
- Enable secure connection (HTTPS)
- TLS version:
1.2 and later
- Enable strong cipher suites
- Port number:
443
Enable SSH Access on QNAP
Login with an admin account to your NAS
Then open ControlPanel
-> Network & File Services
-> Telnet / SSH
Now enable:
- Allow SSH connection (Only administrators can login remotely.)
- Port number: 22
- Enable SFTP
Open Firewall (a bit)
Here we need two firewalls to work in agreement.
Router or Edge Firewall
This is the firewall or router between your NAS and the internet. If you don't have one already, you should get one now!
Hardware
Using an old PC with a PCIe Ethernet card is an okay option.
If you can get an APU2 or a Turris or a mikrotik, even better.
Software
opnsense (OSS) or pfsense (mostly OSS) or RouterOS from MicroTik (pay one time) are popular choices these days.
Rules
- Forbid all incoming traffic as the last rule.
- Then add an exception for HTTPS or port
tcp/443
- For the duration of confirming the certificate add an exception for HTTP or port
tcp/80
Both rules are
IN
rules that allow from- any source from IPv4 or IPv6 with
- source port range 1025..65535
- optional: rate limit new connections to 20 per 90 seconds
On Device Firewall QuFirewall
You can get the QuFirewall in the App Center
for free.
Open the QuFirewall application and Add a new profile.
Then add all the IPv4 and IPv6 ranges that are allowed to use the NAS es services.
- Include Samba (the windows shares) on port 445 (probably only to LAN IPs)
- HTTP on port 80 (from the everywhere
0.0.0.0/0
and::/0
) - HTTPS on port 443 (also from everywhere
- And all the ports you expose from containers
Important: KVM virtual machines with bridged interfaces need their own firewalls
Compile the binary
/// LICENSE: AGPLv3
/// Copyright (C) 2022 Stefan Schindler
/// https://estada.ch/2022/2/7/how-to-secure-a-minimal-qnap-installation-with-letsencrypt/
package main
import (
"fmt"
"net/http"
"os"
)
/// get: `go tool dist list`
/// current: `go env GOOS GOARCH`
///
/// build: `go build -tags netgo ./http_well_known.go`
///
/// serve /mnt/HDA_ROOT/.config/QcloudSSLCertificate/cert/.well-known for letsencrypt
func main() {
argsWithoutProg := os.Args[1:]
bind_addr := ":80"
if len(argsWithoutProg) == 1 {
bind_addr = argsWithoutProg[0]
}
fmt.Printf("binding to \"%s\"\n", bind_addr)
os.Chdir("/mnt/HDA_ROOT/.config/QcloudSSLCertificate/cert/.well-known")
newDir, _ := os.Getwd()
fmt.Printf("Current Working Direcotry: %s\n", newDir)
http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) {
fmt.Printf("%s: %s\n", r.Method, r.URL)
fmt.Fprintf(w, "Welcome to my website!\n")
})
fs := http.FileServer(http.Dir("./"))
shifted_fs := http.StripPrefix("/.well-known/", fs)
// http.Handle("/.well-known/", shifted_fs)
// Docs: https://pkg.go.dev/net/http
http.HandleFunc("/.well-known/", func (w http.ResponseWriter, r *http.Request) {
fmt.Printf("%s: %s\n", r.Method, /*r.RequestURI*/ r.URL)
shifted_fs.ServeHTTP(w, r)
})
res := http.ListenAndServe(bind_addr, nil)
fmt.Printf("%s\n", res)
}
Save this source code in your local user home under ~/go/bin
in http_well_known.go
, you may have to create the directories.
Then compile it with this command:
go build -tags netgo ./http_well_known.go
Transfer the binary
Note the :
at the end is mandatory.
scp http_well_known admin@qnas.estada.ch:
Get/Renew the certificate
First login with ssh on your machine:
ssh admin@qnas.estada.ch
Qnap hides the fact that you can have an interactive shell in the options.
The 8th invisible option is the sh
shell.
Press 8
and then confirm with y + Enter
Now run the server with ./http_well_known
it will output any access that takes place so you have a log if you are attacked meanwhile.
Temporarily open all the firewalls
Don't forget to open the all firewalls between the internet and the NAS to allow:
- IPv4 and IPv6
- Source ports 1025..65535
- Target ports 80, 443
Start the process in the WebUI
Go to ControlPanel
-> System
-> Security
-> SSL Certifiacte & private Key
.
Then either:
- Press
Certifiacte Renewal
if the system is configured already - Press
Replace Certifiacte
if you want to add/change the TLS common/domain name- Select
Get from letsencrypt
in the dropdown - Enter the domain name, eMail and all alternative names
- Select
Now you should see the network verify the challenge and verify the certificate:
# ./http_well_known
binding to ":80"
Current Working Direcotry: /mnt/HDA_ROOT/.config/QcloudSSLCertificate/cert/.well-known
GET: /.well-known/acme-challenge/q70a7XfBD6VVcno666666d2pD9abcdefICb3QH3Lfvw
GET: /.well-known/acme-challenge/q70a7XfBD6VVcno666666d2pD9abcdefICb3QH3Lfvw
GET: /.well-known/acme-challenge/q70a7XfBD6VVcno666666d2pD9abcdefICb3QH3Lfvw
GET: /.well-known/acme-challenge/q70a7XfBD6VVcno666666d2pD9abcdefICb3QH3Lfvw
GET: /.well-known/acme-challenge/q70a7XfBD6VVcno666666d2pD9abcdefICb3QH3Lfvw
After the new certificate is installed you can exit the http_well_known
process by entering Ctrl + C
and then Ctrl + D
.
Cleanup
Under ControlPanel
-> System
-> General Settings
-> System Administration
enable:
- Force secure connection (HTTPS) only
Close all the firewalls
- Close the QuFirewall
- Close the firewall between the internet and the LAN