Security
In this chapter will cover many security tips and servers best-practice routines. You can use the chapter as a reference for most common cases.
The content in this chapter doesn't have to be related to each other, and you can just read the parts you think relevant for your use-case.
Up to date
It may sound obvious, but it's important to make sure all your software and tools are up-to-date.
Latest version
Here you can read an example of why chasing the latest version at all costs is so vital.
In short, the reason is that upgrading regularly from one version to the following one is much easier than run a huge upgrade every few versions.
Staying up-to-date is necessary to avoid any exploits or zero-day vulnerabilities. When you're in charge of a deployed Appwrite server, make sure you're regularly checking for updates:
- Operating system
- Security patches
- Docker
- Appwrite
- Databases
In case you're using Linux - Ubuntu - for example, make sure to use the LTS - Long Term Support - version.
Blocking Console access?
You can block direct access to your Appwrite console by blocking the https://appwrite/console/*
endpoint.
But you need to know that it won't actually block the endpoint access as the console uses the Realtime & CLI endpoints via the https://appwrite/v1/*
endpoint, which can't be blocked as it's vital for all other endpoints.
For brute force attacks, Appwrite is taking care of it, and it won't matter if the request is done from the web console or via any REST/HTTP client.
Limit API keys ability and expiration.
There's a famous principle when it comes to security, it's called the least privilege principle. Meaning giving the fewest privileges possible while maintaining functionality.
When generating API keys within Appwrite, or any fine-grained tokens for that matter, grant the fewest permissions possible. If the API token is needed to execute a function, it doesn't make sense to grant it the Database one as well.
It is also best practice to set expiration dates for tokens, as some time you may have left active token that is not in use anymore.
Best practice
DDoS protection (Cloudflare)
You can use a DNS service like Cloudflare to proxy your request to your servers. Doing so will
- Hide your server IP from the world, make it harder to get attacked.
- Blocks all common ports as
22
for SSH - Give you the ability to block specific countries.
You're also getting a fast TTL for changing your DNS records.
And all of this is completely free.
Firewall
In any case, even if you're using Cloudflare, you would want to utilize a Firewall in front of your server.
There are two types of Firewalls
- Software - Using common Linux tools like
ufw
,csf
andiptables
to block access to specific ports. - Hardware - Using your IaaS - Infrastructure as a Service - provider. This Firewall type is supported by all major providers including Linode, DigitalOcean, Hetzner and the Big-3.
When possible, I'm rooting toward the Hardware
based solution. It's also recommended to block the ssh
port to your specific IPs only.
SSH Settings
When accessing your Appwrite server, it's best to make sure you're blocking the SSH password authentication method, and changing the default 22 SSH port.
You can do so by editing the /etc/ssh/sshd_config
file and changing these values
# Changing the port to 2221 (for example)
#Port 22
Port 2221
# Disable password login
PasswordAuthentication yes
PasswordAuthentication no
Then restart the SSH service
systemctl restart ssh
It might be overwhelmed, but you can use Cloudflare zero-trust tunnels to use SSH in your browser. The advantage of using a Cloudflare tunnel is that you can use many SSO providers as AzureAD, Google, Google Workspace and more to access you server terminal.
Cloudflare zero-trust is completely free up to 50 users, this will sufficient the most use case.
Check the fail2ban utility used for block machines that cause multiple authentication errors.
You can also avoid using the root account altogether by implementing sudo
in the following paragraph 👇.
Sudo
I was asked what is the use of sudo
if you can use it to run any commands? that's a good question.
Most of the time the sudo
settings are way too open, this is due to many reasons, some time is part of your IaaS Image for your Linux distro.
When you'll sudo
more strictly, you'll get the following advantages:
Requiring password
When running command with sudo
the user will be prompt to enter is password, after that the user can run as many sudo
command as needed without having the need to renter the password.
So even if someone gained access to your private key and was able to log in to your server, they are still going to know your password for running any sudo
command.
After entering the password you can keep run sudo
command with a 15-minutes default timeout, meaning after 15 minutes of idle mode, you'll need to renter the password.
Limiting commands When setting the sudo
command for a given user it's best to use the least privilege principle, giving the user sudo
access only to the needed commands nothing more.
Setting sudo
Run this command to create user named dockeruser
adduser dockeruser
Set a strong password for the user and complete the process.
Now, while running as the root user, run one of these two commands to edit the sudoers
file.
# This command will open the sudoers using the system default editor.
visudo
# Or
nano /etc/sudoers
Add these two lines at the end of the file to give the user a sudo
access for docker
binary with 5-minutes timeout.
dockeruser ALL=(ALL) /usr/bin/docker
Defaults:dockeruser timestamp_timeout=5
Now you can set an SSH connection for that user and use it for most management actions.
When needed, you can add more binary files for the user to run as root by separating them with a comma ,
like so:
dockeruser ALL=(ALL) /usr/bin/docker, /usr/bin/which
If you don't like the sudo
way you can check how to run Docker in Rootless mode, which doesn't supports Docker-Swarm Overlay networks.
VPC
When your Appwrite infrastructure is composed of more than one server, it's best to use VPC - Virtual Private Cloud.
You can place multiple servers into one VPC which gives you the ability to share the same network subnet between all the servers, letting you access servers by using their local IP.
This way you can have a Database server that can be connected only from servers within the VPC with no outside connection allowed.
You can do that by adding a rule to the IaaS provider Firewall letting this server be contacted only from servers from the same VPC.
Uptime
You can use on of the plenty available uptime services out there. I'm recommending you check Uptime Kuma, a fancy self-hosted monitoring tool.
One of the advantages of Uptime Kuma is the multiple checking methods, including one for checking Docker containers.
Monitor login attempts
You can check the login attempts in the auth.log
file by running
grep "Failed" /var/log/auth.log
You don't need to do it so often, but it will give you a quick overview of the server SSH attacking measures.
Antivirus
You can install the clamav
antivirus in your server in general by running.
apt install clamav clamav-daemon -y
The only way viruses can enter your self-hosted Appwrite is from user-uploaded files in the storage module. That's why it's vital to make sure you're protected against any viruses.
Both Docker volumes and the decentralized approach for your volumes will save the file in plain sight.
To mitigate this issue, it will be best to use an external storage service like S3 for the user-uploaded media.
Mini tip
Hide server information
Appwrite is already hiding your server details, but just in case it might be good to change the hostname
of your server for something that don't identify your server OS type & version.
In case your hostname something like this eu-4gb-ubuntu-2024
you might want to change it by running:
hostname NEW_HOST_NAME
Worst case
It's very unpleasant to find out your Appwrite is not working, try to be prepared for the worst case,
It's best to have a plan, and practice it, for what you do when:
Crisis | Possible solutions |
---|---|
Server is not responding | Checking the server in your IaaS provider Booting another server using recent snapshot and the daily DB backup. |
Hacked | Restore another server using recent snapshot and the daily DB backup Check to see if you can spot the server weakness. |
Corrupted Database | Restore the DB from the latest DB backup |
Missing files (volumes) | Restore from backup Use external provider. |
In the end, vulnerabilities can't be planned, and your job as a Appwrite DevOps is to try to mitigate the worst cases as fast as possible.
Backups
Backup is an important aspect of your self-hosted Appwrite.
When approaching backups, it's best to make as automatically as possible, so you can set & forget.
In this example of backup.sh
script you can see that I'm running backup on the database and saving the file in my Google Drive using rclone
#!/bin/sh
cd /root/appwrite/
# Database backup
/usr/local/bin/docker-compose -f /root/appwrite/docker-compose.yml exec mariadb sh -c 'exec mysqldump --all-databases --add-drop-database -u"$MYSQL_USER" -p"$MYSQL_PASSWORD"' >dump.sql
# Setting variables
name=$(date +"%m_%d_%Y_%H_%M")
fullname="backup_${name}.zip"
# Creating zip file
zip -r $fullname dump.sql docker-compose.yml .env
# Coping the file to Google Drive
rclone copy $fullname "gdr:/"
# Deleting the backup and dump.sql file
rm $fullname dump.sql
Then I've added the backup to Linux cronjob by running
crontab -e
Then, I've added this line
0 8 * * * /root/appwrite/backup.sh
Which sets the task to run any morning at 08:00AM at server time.