1069 lines
32 KiB
Markdown
1069 lines
32 KiB
Markdown
![]() |
---
|
||
|
title: 02 Server
|
||
|
description:
|
||
|
published: true
|
||
|
date: 2024-11-24T11:38:12.406Z
|
||
|
tags:
|
||
|
editor: markdown
|
||
|
dateCreated: 2023-04-28T08:02:53.316Z
|
||
|
---
|
||
|
|
||
|
# Getting back in with the installation USB
|
||
|
First we have to get back into the installation medium to install the network components
|
||
|
|
||
|
You just start from USB again, Check your network, Mount the needed drives and chroot into the system
|
||
|
|
||
|
You might think, Why did I make you go trough this trouble?
|
||
|
|
||
|
Well, This routine should be common knowledge for Arch users, If you ever break your system, you now know how to get back into it and hopefully fix your system.
|
||
|
|
||
|
So once again so you don't have to go back to the previous guide.
|
||
|
|
||
|
ping archlinux.org
|
||
|
|
||
|
OK? Ctrl + C
|
||
|
|
||
|
mount the needed drives
|
||
|
|
||
|
mount /dev/nvme0n1p1 /mnt
|
||
|
|
||
|
Also mount boot if you need it, mount the second partition to /mnt/boot
|
||
|
|
||
|
mount /dev/nvme0n1p2 /mnt/boot
|
||
|
|
||
|
|
||
|
And finally chroot into the system so you can change whatever you need to change
|
||
|
|
||
|
arch-chroot /mnt
|
||
|
|
||
|
You should be root inside your installed system
|
||
|
|
||
|
# Installing DHCP
|
||
|
For network capabilities we need a DHCP client so install it
|
||
|
|
||
|
pacman -S dhcpcd
|
||
|
|
||
|
That's it
|
||
|
|
||
|
# Reboot into your Installation
|
||
|
Run the following command to escape from the chroot
|
||
|
|
||
|
exit
|
||
|
|
||
|
Run the following command to restart
|
||
|
|
||
|
reboot
|
||
|
|
||
|
Remove the USB when your screen turns black and it should boot into a login prompt
|
||
|
|
||
|
# Configure network
|
||
|
Configuring the network is easy with dhcpcd but first we need to know what device we want to enable
|
||
|
|
||
|
ip address
|
||
|
|
||
|
It should show you a bunch of devices, Ignore lo, there can be multiple, for me it is enp4s0.
|
||
|
|
||
|
so I run the following command to activate the network
|
||
|
|
||
|
sudo systemctl enable --now dhcpcd@enp4s0
|
||
|
|
||
|
You can then test with ping if it works, if it works you can go on, if not you can disable it again with sudo systemctl disable --now dhcpcd@enp4s0 and try other network devices listed by the previous command.
|
||
|
|
||
|
# Point Domains to your server
|
||
|
If you have any domains, you first need to edit their DNS records to point to your Server.
|
||
|
|
||
|
First we need our public IP so run the following command
|
||
|
|
||
|
host myip.opendns.com resolver1.opendns.com
|
||
|
|
||
|
Next up you go to your domain registrar, I can recommend Dynadot.com if you don't have one
|
||
|
|
||
|
You simply login go to domain settings, look for the DNS records and let it point to your IP It should look something like this
|
||
|
|
||
|
```
|
||
|
A domain.tld PublicIP
|
||
|
A *.domain.tld PublicIP
|
||
|
```
|
||
|
|
||
|
The `A` refers to IPv4, If you need IPv6 you just add both records again this time with `AAAA`
|
||
|
|
||
|
`domain.tld` should be replaced with your custom domain, the `*.domain.tld` is for all subdomains.
|
||
|
|
||
|
If you have a TTL setting, just put it on the lowest possible value and don't forget to save.
|
||
|
|
||
|
# Set Static IP for Server
|
||
|
You need a Static IP on your internal network so you can open ports on that internal IP without the device switching internal IP randomly
|
||
|
|
||
|
For this you need to login into your router, This can be a pain, If you have no idea I recommend resetting it and use the default password to login.
|
||
|
|
||
|
In the router interface you have to look for Static IP or DHCP reservation or something like that.
|
||
|
|
||
|
run the following command and look at the hardware address or MAC/link/ether address for your network device it should look something like this `ab:cd:f0:12:34:56`
|
||
|
|
||
|
ip address
|
||
|
|
||
|
Now you need to combine the mac address of the server with a static IP given by you, It might be good to plan out your network create different vlans like 192.168.1.X for your servers 192.168.2.X for your trusted devices 192.168.3.X for your smarthome devices and 192.168.4.X for a free wifi guest network, This way you have a seperated network for your home and servers, you can block all smarthome devices from phoning home by just blocking the subnet with 1 rule and you can seperate guests from your own devices. we are going to assume you made a subnet 192.168.1.X for your servers, and this server is 192.168.1.10.
|
||
|
|
||
|
So Mac Adress `ab:cd:f0:12:34:56` needs to be assigned the local IP of `192.168.1.10`.
|
||
|
|
||
|
Make sure you save, Then reboot your server and it should move over to the assigned local IP.
|
||
|
|
||
|
# Open two ports to your server
|
||
|
For now we are only going to open the ports 80 and 443 for http and https respectively
|
||
|
|
||
|
Still in your router interface you have to look for "port forward", Don't worry about clicking trough the menus, as long as you don't press save anywhere you should be fine :)
|
||
|
|
||
|
If you found the port forwarding screen you need to open ports 80 and 443 over TCP to the local static IP of your server. So in this example case
|
||
|
|
||
|
`192.168.1.10 TCP 80`
|
||
|
`192.168.1.10 TCP 443`
|
||
|
|
||
|
Make sure you save and you don't need to reboot anything for this.
|
||
|
|
||
|
Be careful with opening ports here, I would recommend against opening the SSH or the FTP ports, Keep it local which keeps it somewhat safe :)
|
||
|
|
||
|
Also remember that you don't need to open ports for local services, like mysql or any of the proxy ports, You also don't have to open ports for outgoing connections, It is just for incoming connections.
|
||
|
|
||
|
# Firewall
|
||
|
IPtables is present in the kernel and installed by default We use IPtables because docker networks don't play nice with nftables.
|
||
|
|
||
|
The default settings are fine we are leaving 80 and 443 closed, because the services we are going to host will be running from their own network. It is recomended to read up on iptables. Have a look at them and see what docker and fail2ban is changing over time. Do not change things other than steps in this guide unless you know what you are doing. See page *02 Server Iptables*
|
||
|
|
||
|
# Enable auto login
|
||
|
To make sure everything is getting started automatically after a reboot you can use autologin
|
||
|
|
||
|
edit the getty service file with the following command
|
||
|
|
||
|
sudo vim /etc/systemd/system/getty.target.wants/getty@tty1.service
|
||
|
|
||
|
Look for the following line
|
||
|
|
||
|
```
|
||
|
ExecStart=./sbin/agetty -o '-p -- \\u' --noclear - $TERM
|
||
|
```
|
||
|
|
||
|
And change it into this
|
||
|
|
||
|
```
|
||
|
ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear --autologin USERNAME - $TERM
|
||
|
```
|
||
|
|
||
|
Save and exit
|
||
|
|
||
|
You can restart to ensure you are automatically logged in, It will still ask you for a password, but it should say Automatic Login after boot. If you want to log in with another user simply press enter (wrong password) and you can type another user when needed. press enter for your other user password and you are in!
|
||
|
|
||
|
# Install and Configure SSH
|
||
|
SSH or Secure SHell is a tool to access your server remotely, In our example we will only use it locally by simply not opening any ports in the router.
|
||
|
|
||
|
Simply install OpenSSH
|
||
|
|
||
|
sudo pacman -S openssh
|
||
|
|
||
|
We need to configure it properly so open the following file
|
||
|
|
||
|
sudo vim /etc/ssh/sshd_config
|
||
|
|
||
|
Uncomment LogLevel and change it to VERBOSE so we can use it with fail2ban
|
||
|
|
||
|
LogLevel VERBOSE
|
||
|
|
||
|
Now start and enable the service
|
||
|
|
||
|
sudo systemctl enable --now sshd
|
||
|
|
||
|
# Install Docker
|
||
|
Docker is the containerization software we are going to use, Every service will get its own container and network.
|
||
|
|
||
|
Install it with the following command
|
||
|
|
||
|
sudo pacman -S docker
|
||
|
|
||
|
now we just need to enable it with systemctl
|
||
|
|
||
|
sudo systemctl enable --now docker
|
||
|
|
||
|
Docker should be up and running.
|
||
|
|
||
|
# Install Docker Compose
|
||
|
Docker Compose is a front end for Docker, It allows us to set up containers and networks in an easy way.
|
||
|
|
||
|
Install it with the following command
|
||
|
|
||
|
sudo pacman -S docker-compose
|
||
|
|
||
|
Docker Compose parses yaml files, which are designed to be simple configuration files, spaces are everything here, so make sure you get the spacing right. It does allow us to set up containers quick and easy.
|
||
|
|
||
|
We configure 3 main things container details, volumes and networks
|
||
|
|
||
|
container or service details contain the name of the container, which image it should pull, when it should restart, etc.
|
||
|
|
||
|
volumes are like mounting points for persistant storage, For stuff like Configuration files, Databases, File storage, Stuff that must remain intact if you ever remove the container and rebuild it.
|
||
|
|
||
|
Networks are like virtual networks on the local network, they seperate the different services that don't need to talk to eachother. Services can be in multiple networks tho.
|
||
|
|
||
|
I hope the configuration files will make everything more clear, but this is everything you need to know for now.
|
||
|
|
||
|
# Shutdown your server
|
||
|
Aside from fail2ban, certbot, banner/motd and updating and the docker services themself your server is done. It is the perfect time to make some changes. mainly becuase the next sections require a lot of typing and the ability to copy/paste will be very welcome.
|
||
|
|
||
|
So shutdown your machine
|
||
|
|
||
|
sudo shutdown now
|
||
|
|
||
|
Remove the power cable and press the power button this will drain any power left in the machine. Now you can remove any GPU you needed for a video output, remove any peripherals like keyboard, monitors, and give your server a nice place in your house, Just connect the ethernet and power cable and simply press the power button once to start your server again.
|
||
|
|
||
|
Wait a minute and you should be able to login to your server from a different machine over your local network in our example the Local IP was 192.168.1.10 so our command will be the following
|
||
|
|
||
|
ssh username@192.168.1.10
|
||
|
|
||
|
Fill in your password associated with your username and press enter. If you run into problems here, double check the Firewall, SSH and static IP steps. Also make sure your home vlan has access to your server vlan if you chose to use different vlans.
|
||
|
|
||
|
# Banner and motd
|
||
|
Next up we are going to adjust the banner it will display on SSH password request
|
||
|
|
||
|
sudo vim /etc/issue
|
||
|
|
||
|
Add in the following content (you might need to \\ the \ ones)
|
||
|
|
||
|
```
|
||
|
_________
|
||
|
/ \
|
||
|
| Whatsup? |
|
||
|
\_______ /
|
||
|
\/
|
||
|
.--.
|
||
|
|o_o |
|
||
|
|:_/ |
|
||
|
// \ \
|
||
|
(| | )
|
||
|
/'\_ _/`\
|
||
|
\___)=(___/
|
||
|
```
|
||
|
|
||
|
Then we have the motd which will display after login
|
||
|
|
||
|
for this we will write a little script so it has up to date information
|
||
|
|
||
|
mkdir ~/Scripts
|
||
|
vim ~/Scripts/updatemotd.sh
|
||
|
|
||
|
Add in the following content
|
||
|
|
||
|
```
|
||
|
#!/bin/bash
|
||
|
|
||
|
printf " \e[34m/#\\ \e[0;0m_ \e[34m_ _
|
||
|
\e[34m/###\\ \e[0;0m __ _ _ __ ___| |__ \e[34m| (_)_ __ _ ___ __
|
||
|
\e[34m/#####\\ \e[0;0m/ _' | '__/ __| '_ \\ \e[34m| | | '_ \\| | | \\ \\/ /
|
||
|
\e[34m/##.-.##\\ \e[0;0m| (_| | | | (__| | | | \e[34m| | | | | | |_| |> <
|
||
|
\e[34m/##( )##\\ \e[0;0m\\__,_|_| \\___|_| |_| \e[34m|_|_|_| |_|\\__,_/_/\\_\\
|
||
|
\e[34m/#.-- --.#\\
|
||
|
\e[34m/' '\\
|
||
|
" > /etc/motd
|
||
|
printf "\e[34mMemory Usage: \e[0;0m" >> /etc/motd && free -m | grep Mem | awk '{ printf("%dMB/%dMB ", $3, $2) }' >> /etc/motd && free | grep Mem | awk '{ printf("%.2f%% used\n", $3*100/$2) }' >> /etc/motd
|
||
|
printf "\e[34mDisk Free: \e[0;0m" >> /etc/motd && df -h | awk '$NF=="/"{printf "root %s ", $4}' >> /etc/motd
|
||
|
printf "" && df -h | awk '$NF=="/data"{printf "data %s ", $4}' >> /etc/motd
|
||
|
printf "" && df -h | awk '$NF=="/music"{printf "data %s ", $4}' >> /etc/motd
|
||
|
printf "" && df -h | awk '$NF=="/video"{printf "data %s ", $4}' >> /etc/motd
|
||
|
printf "\n\e[34mLast Boot: \e[0;0m$(uptime)\n" >> /etc/motd
|
||
|
exit
|
||
|
```
|
||
|
|
||
|
Now we are going to create a Systemtimer which will run the script
|
||
|
|
||
|
sudo vim /etc/systemd/system/updatemotd.service
|
||
|
|
||
|
Add in the following (replace USSERNAME with your username!)
|
||
|
|
||
|
```
|
||
|
[Unit]
|
||
|
Description=Update the motd
|
||
|
Wants=updatemotd.timer
|
||
|
|
||
|
[Service]
|
||
|
Type=oneshot
|
||
|
ExecStart=/bin/bash /home/USERNAME/Scripts/updatemotd.sh
|
||
|
|
||
|
[Install]
|
||
|
WantedBy=multi-user.target
|
||
|
```
|
||
|
|
||
|
Now we need a timer file
|
||
|
|
||
|
sudo vim /etc/systemd/system/updatemotd.timer
|
||
|
|
||
|
Add in the following content
|
||
|
|
||
|
```
|
||
|
[Unit]
|
||
|
Description=Runs the update motd service every minute
|
||
|
Requires=updatemotd.service
|
||
|
|
||
|
[Timer]
|
||
|
Unit=updatemotd.service
|
||
|
OnBootSec=1min
|
||
|
OnUnitActiveSec=1min
|
||
|
|
||
|
[Install]
|
||
|
WantedBy=timers.target
|
||
|
```
|
||
|
|
||
|
And now we finally start the timer
|
||
|
|
||
|
sudo systemctl enable --now updatemotd.timer
|
||
|
|
||
|
You can exit the ssh and log back in to see the results
|
||
|
|
||
|
# Install and Configure Certbot
|
||
|
Certbot will create signed SSL certificates for you (For HTTPS websites)
|
||
|
|
||
|
We simply install it with the following command
|
||
|
|
||
|
sudo pacman -S certbot
|
||
|
|
||
|
First we need to open port 80 temporarily so that certbot can verify you own the domain.
|
||
|
|
||
|
sudo iptables -I INPUT -p tcp --dport 80 -j ACCEPT
|
||
|
|
||
|
Next we are going to request a certificate for every domain you pointed to this server.
|
||
|
|
||
|
sudo certbot certonly -d example.com
|
||
|
|
||
|
Just answer the questions and repeat the process for every other domain. Pick 1 if it asks for the authentication method.
|
||
|
|
||
|
Now we can restore the firewall settings again by running the following command
|
||
|
|
||
|
sudo iptables -D INPUT -p tcp -m tcp --dport 80 -j ACCEPT
|
||
|
|
||
|
Your certificates are only valid for 90 days, so we need to update them every once in a while, personally I just run a script every week that makes sure all certificates are valid
|
||
|
|
||
|
Create the script
|
||
|
|
||
|
vim ~/Scripts/renewcerts.sh
|
||
|
|
||
|
Add in the following content (replace USSERNAME with your username!)
|
||
|
|
||
|
```
|
||
|
#!/bin/bash
|
||
|
|
||
|
docker-compose -f /home/USERNAME/Docker/nginx/docker-compose.yml down
|
||
|
iptables -I INPUT -p tcp --dport 80 -j ACCEPT
|
||
|
certbot renew
|
||
|
iptables -D INPUT -p tcp -m tcp --dport 80 -j ACCEPT
|
||
|
docker-compose -f /home/USERNAME/Docker/nginx/docker-compose.yml up -d
|
||
|
```
|
||
|
|
||
|
Next we need a service which actually executes the script so create the service file
|
||
|
|
||
|
sudo vim /etc/systemd/system/renewcerts.service
|
||
|
|
||
|
Add in the following content (replace USSERNAME with your username!)
|
||
|
|
||
|
```
|
||
|
[Unit]
|
||
|
Description=Updates all expiring certificates
|
||
|
Wants=renewcerts.timer
|
||
|
|
||
|
[Service]
|
||
|
Type=oneshot
|
||
|
ExecStart=/bin/bash /home/USERNAME/Scripts/renewcerts.sh
|
||
|
|
||
|
[Install]
|
||
|
WantedBy=multi-user.target
|
||
|
```
|
||
|
|
||
|
Next we need a timer which actually calls the service so create the timer file
|
||
|
|
||
|
sudo vim /etc/systemd/system/renewcerts.timer
|
||
|
|
||
|
Add in the following content
|
||
|
|
||
|
```
|
||
|
[Unit]
|
||
|
Description=Runs the update certificates service every week
|
||
|
Requires=renewcerts.service
|
||
|
|
||
|
[Timer]
|
||
|
OnCalendar=Tue 04:00
|
||
|
Persistent=true
|
||
|
|
||
|
[Install]
|
||
|
WantedBy=timers.target
|
||
|
```
|
||
|
|
||
|
Finally start and enable the timer so it will actually run
|
||
|
|
||
|
sudo systemctl enable --now renewcerts.timer
|
||
|
|
||
|
# Install and Configure Fail2Ban
|
||
|
Fail2Ban will read your logs for failed login attempts or other fishy things, when it crosses a certain amount of attempts it will block the IP using the firewall.
|
||
|
|
||
|
So first we are going to install fail2ban
|
||
|
|
||
|
sudo pacman -S fail2ban
|
||
|
|
||
|
Next enable and start the service
|
||
|
|
||
|
sudo systemctl enable --now fail2ban
|
||
|
|
||
|
Create a file where we will store all of our configuration
|
||
|
|
||
|
sudo vim /etc/fail2ban/jail.local
|
||
|
|
||
|
Replace the whole contents with the following
|
||
|
|
||
|
```
|
||
|
[INCLUDES]
|
||
|
|
||
|
before = paths-arch.conf
|
||
|
|
||
|
[DEFAULT]
|
||
|
|
||
|
## Settings
|
||
|
ignoreip = 127.0.0.1/8 ::1 192.168.1.1/16 172.20.0.0/16
|
||
|
findtime = 1h
|
||
|
maxretry = 3
|
||
|
bantime = 1h
|
||
|
bantime.increment = true
|
||
|
bantime.rndtime = 3600
|
||
|
bantime.multipliers = 1 3 5 12 24 168 4200 999999999
|
||
|
bantime.overalljails = true
|
||
|
banaction = action
|
||
|
|
||
|
## SSH Jail
|
||
|
[sshd]
|
||
|
enabled = true
|
||
|
```
|
||
|
|
||
|
For now we only set some default settings you can always overwrite them later on
|
||
|
|
||
|
ignoreip is pretty self explanitory, You can add your own IPs
|
||
|
findtime is the period in which these attempts are are allowed
|
||
|
maxretry is the number of attempts to trigger a ban
|
||
|
bantime is pretty self explanitory
|
||
|
bantime.increment this increments the bantime
|
||
|
bantime.rndtime adds a random time to the bantime to fool scripts
|
||
|
bantime.multipliers sets a custom ban time for repeated offenders
|
||
|
bantime.overalljails makes fail2ban check accross all jails so 1 attempt at 3 services is a ban
|
||
|
banaction is which action to perform, we will write a custom action in the next section
|
||
|
Finally under `[sshd]` we enabled sshd, we have to enable every service we add.
|
||
|
We will add more later depending on the services you want to activate
|
||
|
|
||
|
Save and exit the file
|
||
|
|
||
|
Now we need to create a banaction so fail2ban knows what to do when it needs to ban someone.
|
||
|
|
||
|
sudo vim /etc/fail2ban/action.d/action.conf
|
||
|
|
||
|
Add in the following lines
|
||
|
|
||
|
```
|
||
|
[Definition]
|
||
|
actionban = iptables -I INPUT -s <ip> -j REJECT
|
||
|
iptables -I FORWARD -s <ip> -j REJECT
|
||
|
iptables -I DOCKER-USER -s <ip> -j REJECT
|
||
|
echo "<matches>" >> /var/log/fail2ban.log
|
||
|
|
||
|
actionunban = iptables -D INPUT -s <ip> -j REJECT
|
||
|
iptables -D FORWARD -s <ip> -j REJECT
|
||
|
iptables -D DOCKER-USER -s <ip> -j REJECT
|
||
|
```
|
||
|
|
||
|
Here you can see that everytime it needs to ban someone it will simply execute some iptables commands that reject requests for docker and the host system.
|
||
|
|
||
|
I also put in the echo so it prints the why in the fail2ban log it can be handy for debugging but you can remove it.
|
||
|
|
||
|
Don't forget to restart the service to make the changes take effect
|
||
|
|
||
|
sudo systemctl restart fail2ban
|
||
|
|
||
|
You can use fail2ban-client to unban people or view some statistics
|
||
|
|
||
|
sudo fail2ban-client
|
||
|
|
||
|
Lets add some Aliases so viewing the banned list and unbanning people gets easier
|
||
|
|
||
|
WIP!
|
||
|
|
||
|
# Install and Configure Nginx
|
||
|
eNGINe X is a very powerful webserver, It can do many things and is highly configurable.
|
||
|
|
||
|
We will use it as a reverse proxy to forward requests to the correct container.
|
||
|
|
||
|
Create some folders in your home directory
|
||
|
|
||
|
mkdir -p ~/Docker/nginx
|
||
|
|
||
|
Now create a docker compose file in that directory
|
||
|
|
||
|
vim ~/Docker/nginx/docker-compose.yml
|
||
|
|
||
|
Add in the following text
|
||
|
|
||
|
```
|
||
|
version: '3'
|
||
|
|
||
|
services:
|
||
|
nginx:
|
||
|
image: nginx:latest
|
||
|
container_name: nginx
|
||
|
restart: always
|
||
|
volumes:
|
||
|
- /data/nginx/config:/etc/nginx
|
||
|
- /data/nginx/log/error.log:/var/log/error.log
|
||
|
- /data/nginx/log/access.log:/var/log/access.log
|
||
|
- /etc/letsencrypt/:/etc/letsencrypt/
|
||
|
- /etc/localtime:/etc/localtime:ro
|
||
|
ports:
|
||
|
- 80:80
|
||
|
- 443:443
|
||
|
```
|
||
|
|
||
|
First we need to create a folder for the configuration
|
||
|
|
||
|
sudo mkdir -p /data/nginx/config
|
||
|
|
||
|
We need to add the nginx configuration file
|
||
|
|
||
|
sudo vim /data/nginx/config/nginx.conf
|
||
|
|
||
|
Add in the following text
|
||
|
|
||
|
```
|
||
|
# Global Settings
|
||
|
user nginx;
|
||
|
pid /var/run/nginx.pid;
|
||
|
worker_processes auto;
|
||
|
worker_rlimit_nofile 65535;
|
||
|
|
||
|
events {
|
||
|
multi_accept on;
|
||
|
worker_connections 1024;
|
||
|
}
|
||
|
|
||
|
|
||
|
# Web Traffic
|
||
|
http {
|
||
|
charset utf-8;
|
||
|
sendfile on;
|
||
|
tcp_nopush on;
|
||
|
tcp_nodelay on;
|
||
|
server_tokens off;
|
||
|
error_log /var/log/error.log warn;
|
||
|
access_log /var/log/access.log;
|
||
|
proxy_cache_path /etc/nginx/cache keys_zone=one:500m max_size=1000m;
|
||
|
types_hash_max_size 2048;
|
||
|
types_hash_bucket_size 64;
|
||
|
client_max_body_size 16M;
|
||
|
client_body_buffer_size 16M;
|
||
|
client_header_buffer_size 16M;
|
||
|
large_client_header_buffers 2 16M;
|
||
|
|
||
|
# MIME
|
||
|
default_type application/octet-stream;
|
||
|
|
||
|
# Limits
|
||
|
limit_req_log_level warn;
|
||
|
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/m;
|
||
|
|
||
|
# SSL
|
||
|
ssl_session_timeout 1d;
|
||
|
ssl_session_cache shared:SSL:10m;
|
||
|
ssl_session_tickets off;
|
||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||
|
ssl_stapling on;
|
||
|
ssl_stapling_verify on;
|
||
|
|
||
|
# Services
|
||
|
include /etc/nginx/services/*.active;
|
||
|
}
|
||
|
```
|
||
|
|
||
|
We need to create the 2 log files so docker compose won't create directories
|
||
|
|
||
|
sudo touch /data/nginx/log/access.log /data/nginx/log/error.log
|
||
|
|
||
|
We also nened to create 2 directories which will house our auth
|
||
|
|
||
|
sudo mkdir -p /data/nginx/config/auth /data/nginx/config/services
|
||
|
|
||
|
Nginx should be good now, lets start the container
|
||
|
|
||
|
sudo docker-compose -f ~/Docker/nginx/docker-compose.yml up -d
|
||
|
|
||
|
Now we only need to setup fail2ban for Nginx, so create the following file
|
||
|
|
||
|
sudo vim /etc/fail2ban/filter.d/nginxx.local
|
||
|
|
||
|
add in the following content
|
||
|
|
||
|
```
|
||
|
[INCLUDES]
|
||
|
before = common.conf
|
||
|
|
||
|
[Definition]
|
||
|
failregex = ^<HOST>.*"(GET|POST).*" (400|401|403|404|405|406|407|423|429) .*$
|
||
|
```
|
||
|
|
||
|
This will ban everyone getting any of the error codes in the failregex line.
|
||
|
|
||
|
Now we need to activate the filter in our main fail2ban configuration file
|
||
|
|
||
|
sudo vim /etc/fail2ban/jail.local
|
||
|
|
||
|
Add the following to the bottom
|
||
|
|
||
|
```
|
||
|
## Nginx
|
||
|
[nginxx]
|
||
|
enabled = true
|
||
|
logpath = /data/nginx/log/access.log
|
||
|
```
|
||
|
|
||
|
Restart fail2ban to make the changes take effect
|
||
|
|
||
|
sudo systemctl restart fail2ban
|
||
|
|
||
|
# Install and Configure MariaDB
|
||
|
MariaDB is a drop in replacement for MySQL, which is a database used by many services.
|
||
|
|
||
|
First we create some folders
|
||
|
|
||
|
mkdir -p ~/Docker/mariadb
|
||
|
|
||
|
First we will create a docker compose file
|
||
|
|
||
|
vim ~/Docker/mariadb/docker-compose.yml
|
||
|
|
||
|
Add in the following text
|
||
|
|
||
|
```
|
||
|
version: '3'
|
||
|
|
||
|
services:
|
||
|
mariadb:
|
||
|
image: mariadb:latest
|
||
|
container_name: mariadb
|
||
|
restart: always
|
||
|
command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW --skip-innodb-read-only-compressed
|
||
|
volumes:
|
||
|
- /data/mariadb/data:/var/lib/mysql
|
||
|
- /data/mariadb/config:/etc/mysql/conf.d
|
||
|
- /data/mariadb/logs:/var/log/mysql
|
||
|
- /etc/localtime:/etc/localtime:ro
|
||
|
|
||
|
environment:
|
||
|
- MYSQL_ROOT_PASSWORD=SETAMYSQLROOTPASSWORDHERE
|
||
|
```
|
||
|
|
||
|
Be sure to set your mysql root password
|
||
|
|
||
|
Lets start the container
|
||
|
|
||
|
sudo docker-compose -f ~/Docker/mariadb/docker-compose.yml up -d
|
||
|
|
||
|
Now we only need to initialize the database
|
||
|
|
||
|
sudo docker exec -it mariadb mariadb-secure-installation
|
||
|
|
||
|
Answer the first 3 questions with No and the rest with Yes
|
||
|
|
||
|
# Install and Configure Nextcloud
|
||
|
Nextcloud is a great application, It is great for storing and syncing data, storing your contacts, bookmarks, passwords, calendar, tasks. It also has a great RSS reader, full office suite and many many more. I truly can't live without it. And neither should you which is why I picked it as the example for this guide
|
||
|
|
||
|
First we need to create a network for the service.
|
||
|
|
||
|
sudo docker network create --subnet=172.20.30.0/24 nextcloud
|
||
|
|
||
|
Next we are going to create some folders
|
||
|
|
||
|
mkdir -p ~/Docker/nextcloud
|
||
|
|
||
|
Now we will create a docker compose file
|
||
|
|
||
|
vim ~/Docker/nextcloud/docker-compose.yml
|
||
|
|
||
|
Add in the following text
|
||
|
|
||
|
```
|
||
|
version: '3'
|
||
|
|
||
|
services:
|
||
|
nextcloud:
|
||
|
image: nextcloud
|
||
|
container_name: nextcloud
|
||
|
restart: always
|
||
|
volumes:
|
||
|
- /etc/localtime:/etc/localtime:ro
|
||
|
- /data/nextcloud:/var/www/html
|
||
|
networks:
|
||
|
nextcloud:
|
||
|
ipv4_address: 172.20.30.10
|
||
|
|
||
|
networks:
|
||
|
nextcloud:
|
||
|
external: true
|
||
|
name: nextcloud
|
||
|
ipam:
|
||
|
config:
|
||
|
- subnet: 172.20.30.0/24
|
||
|
```
|
||
|
|
||
|
Now we need to add a server block for nextcloud to the Nginx config file so create a file that will be included by the main nginx config file
|
||
|
|
||
|
And create the file
|
||
|
|
||
|
sudo vim /data/nginx/config/services/nextcloud.active
|
||
|
|
||
|
add in the following text
|
||
|
|
||
|
```
|
||
|
server {
|
||
|
server_name example.com;
|
||
|
listen 443 ssl;
|
||
|
|
||
|
# Settings
|
||
|
autoindex off;
|
||
|
client_max_body_size 5000M;
|
||
|
|
||
|
# Locations
|
||
|
location / {
|
||
|
proxy_pass http://nextcloud:80;
|
||
|
proxy_http_version 1.1;
|
||
|
proxy_cache_bypass $http_upgrade;
|
||
|
proxy_ssl_server_name on;
|
||
|
proxy_set_header Upgrade $http_upgrade;
|
||
|
proxy_set_header X-Real-IP $remote_addr;
|
||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||
|
proxy_set_header X-Forwarded-Host $host;
|
||
|
proxy_set_header X-Forwarded-Port $server_port;
|
||
|
proxy_set_header Connection "upgrade";
|
||
|
proxy_set_header Host $host;
|
||
|
proxy_connect_timeout 600m;
|
||
|
proxy_send_timeout 600m;
|
||
|
proxy_read_timeout 600m;
|
||
|
}
|
||
|
|
||
|
location /.well-known/carddav {
|
||
|
return 301 $scheme://$host/remote.php/dav;
|
||
|
}
|
||
|
|
||
|
location /.well-known/caldav {
|
||
|
return 301 $scheme://$host/remote.php/dav;
|
||
|
}
|
||
|
|
||
|
location ~ /\.(?!well-known) {
|
||
|
deny all;
|
||
|
}
|
||
|
|
||
|
location = /favicon.ico {
|
||
|
log_not_found off;
|
||
|
}
|
||
|
|
||
|
location = /robots.txt {
|
||
|
log_not_found off;
|
||
|
}
|
||
|
|
||
|
# GZip
|
||
|
gzip on;
|
||
|
gzip_vary on;
|
||
|
gzip_proxied any;
|
||
|
gzip_comp_level 6;
|
||
|
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
|
||
|
|
||
|
# Headers
|
||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||
|
add_header X-Content-Type-Options "nosniff" always;
|
||
|
add_header X-Frame-Options "SAMEORIGIN";
|
||
|
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||
|
add_header Content-Security-Policy "default-src 'self' http: https: ws: wss: data: blob: 'unsafe-inline'; frame-ancestors 'self';" always;
|
||
|
add_header Permissions-Policy "interest-cohort=()" always;
|
||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||
|
|
||
|
# SSL
|
||
|
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
|
||
|
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
|
||
|
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
|
||
|
}
|
||
|
|
||
|
# Redirect
|
||
|
server {
|
||
|
listen 80;
|
||
|
server_name example.com;
|
||
|
return 301 https://example.com$request_uri;
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Be sure to replace `example.com` 6 times
|
||
|
|
||
|
Now we need to add nextcloud to the mariadb and nginx networks, because it needs a database and a proxy.
|
||
|
|
||
|
open the nginx compose file
|
||
|
|
||
|
vim ~/Docker/nginx/docker-compose.yml
|
||
|
|
||
|
add the nextcloud network so it looks like this
|
||
|
|
||
|
```
|
||
|
version: '3'
|
||
|
|
||
|
services:
|
||
|
nginx:
|
||
|
image: nginx:latest
|
||
|
container_name: nginx
|
||
|
restart: always
|
||
|
volumes:
|
||
|
- /data/nginx/config:/etc/nginx
|
||
|
- /data/nginx/log/error.log:/var/log/error.log
|
||
|
- /data/nginx/log/access.log:/var/log/access.log
|
||
|
- /etc/letsencrypt/:/etc/letsencrypt/
|
||
|
- /etc/localtime:/etc/localtime:ro
|
||
|
ports:
|
||
|
- 80:80
|
||
|
- 443:443
|
||
|
- 8448:8448
|
||
|
networks:
|
||
|
nextcloud:
|
||
|
ipv4_address: 172.20.30.20
|
||
|
|
||
|
networks:
|
||
|
nextcloud:
|
||
|
external: true
|
||
|
name: nextcloud
|
||
|
```
|
||
|
|
||
|
open the mariadb compose file
|
||
|
|
||
|
vim ~/Docker/mariadb/docker-compose.yml
|
||
|
|
||
|
add the nextcloud network so it looks like this
|
||
|
|
||
|
```
|
||
|
version: '3'
|
||
|
|
||
|
services:
|
||
|
mariadb:
|
||
|
image: mariadb:latest
|
||
|
container_name: mariadb
|
||
|
restart: always
|
||
|
command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW --skip-innodb-read-only-compressed
|
||
|
volumes:
|
||
|
- /data/mariadb/data:/var/lib/mysql
|
||
|
- /data/mariadb/config:/etc/mysql/conf.d
|
||
|
- /data/mariadb/logs:/var/log/mysql
|
||
|
- /etc/localtime:/etc/localtime:ro
|
||
|
|
||
|
environment:
|
||
|
- MYSQL_ROOT_PASSWORD=SETAMYSQLROOTPASSWORDHERE
|
||
|
networks:
|
||
|
nextcloud:
|
||
|
ipv4_address: 172.20.30.30
|
||
|
|
||
|
networks:
|
||
|
nextcloud:
|
||
|
external: true
|
||
|
name: nextcloud
|
||
|
```
|
||
|
|
||
|
Now we are going to start the nextcloud container and restart the nginx and mariadb containers.
|
||
|
|
||
|
sudo docker-compose -f ~/Docker/nextcloud/docker-compose.yml up -d
|
||
|
sudo docker-compose -f ~/Docker/nginx/docker-compose.yml down && sudo docker-compose -f ~/Docker/nginx/docker-compose.yml up -d
|
||
|
sudo docker-compose -f ~/Docker/mariadb/docker-compose.yml down && sudo docker-compose -f ~/Docker/mariadb/docker-compose.yml up -d
|
||
|
|
||
|
Nextcloud should be accessable from your browser using the domain you chose.
|
||
|
If you have not setup your dns records but only adjusted your .active file for nginx you need to do 2 things first before you go on.
|
||
|
- make sure you have chosen a domain (nextcloud.example.com) and used it in your .active file for nginx.
|
||
|
- make sure you have valid certificates for this domain. if not go back to the certbot part so you can repeat those instructions for your nextcloud domain.
|
||
|
|
||
|
But first we need to create a database, a user and set the permissions.
|
||
|
|
||
|
You can get into the database with the following command (only if mariadb is running)
|
||
|
|
||
|
sudo docker exec -it mariadb mariadb -p
|
||
|
|
||
|
Enter the Mysql root password you provided during the creation of the mariadb container and you should be in.
|
||
|
|
||
|
Now run the following commands to create a database, create a user with privileges, and make them take effect.
|
||
|
|
||
|
create database nextcloud;
|
||
|
create user nextcloud@'172.20.30.10' identified by 'NEXTCLOUDDATABASEPASSWORD';
|
||
|
grant all privileges on nextcloud.* to nextcloud@'172.20.30.10';
|
||
|
flush privileges;
|
||
|
|
||
|
You can exit the mysql prompt with `exit;` and then pressing enter.
|
||
|
|
||
|
Now we just need to go to example.com and follow the steps
|
||
|
|
||
|
The Database is nextcloud, the user is nextcloud, The IP is 172.20.30.30:3306 and the password is what you gave it.
|
||
|
|
||
|
We also need to force HTTPS, else it will give problems since we are running from behind a proxy
|
||
|
|
||
|
sudo vim /data/nextcloud/config/config.php
|
||
|
|
||
|
Add in the following line preferrrably under the overwrite.cli.url line.
|
||
|
|
||
|
```
|
||
|
'overwriteprotocol' => 'https',
|
||
|
```
|
||
|
|
||
|
Nextcloud requires some tasks to be executed every 5 minutes, for that we are going to use systemd timers, like we did for certbot
|
||
|
|
||
|
Create a little script
|
||
|
|
||
|
vim ~/Scripts/nextcloudcron.sh
|
||
|
|
||
|
add in the following content
|
||
|
|
||
|
```
|
||
|
#!/bin/bash
|
||
|
|
||
|
docker exec -u 33 -t nextcloud php -f /var/www/html/cron.php
|
||
|
exit
|
||
|
```
|
||
|
|
||
|
Create a systemd service
|
||
|
|
||
|
sudo vim /etc/systemd/system/nextcloudcron.service
|
||
|
|
||
|
Add in the following content
|
||
|
|
||
|
```
|
||
|
[Unit]
|
||
|
Description=Runs Nextcloud Cron
|
||
|
Wants=nextcloudcron.timer
|
||
|
|
||
|
[Service]
|
||
|
Type=oneshot
|
||
|
ExecStart=/bin/bash /home/USERNAME/Scripts/nextcloudcron.sh
|
||
|
|
||
|
[Install]
|
||
|
WantedBy=multi-user.target
|
||
|
```
|
||
|
|
||
|
Create a timer file
|
||
|
|
||
|
sudo vim /etc/systemd/system/nextcloudcron.timer
|
||
|
|
||
|
and add in the following content
|
||
|
|
||
|
```
|
||
|
[Unit]
|
||
|
Description=Runs Nextcloud Cron
|
||
|
Requires=nextcloudcron.service
|
||
|
|
||
|
[Timer]
|
||
|
Unit=nextcloudcron.service
|
||
|
OnBootSec=5min
|
||
|
OnUnitActiveSec=5min
|
||
|
|
||
|
[Install]
|
||
|
WantedBy=timers.target
|
||
|
```
|
||
|
|
||
|
Finally start the timer
|
||
|
|
||
|
sudo systemctl enable --now nextcloudcron.timer
|
||
|
|
||
|
Nextcloud should be all good and ready to go, You can check the persistance by completely deleting all containers and all volumes, When you start it again all your stuff should still be there :)
|
||
|
|
||
|
Nextcloud has its own Brute force protection, but we still are going to add a fail2ban filter because we want attackers to be banned from all services and not just nextcloud.
|
||
|
|
||
|
So lets create a new nextcloud filter
|
||
|
|
||
|
sudo vim /etc/fail2ban/filter.d/nextcloud.local
|
||
|
|
||
|
add in the following content
|
||
|
|
||
|
```
|
||
|
[Definition]
|
||
|
failregex=^{"reqId":".*","remoteAddr":".*","app":"core","message":"Login failed: '.*' \(Remote IP: '<HOST>'\)","level":2,"time":".*"}$
|
||
|
^{"reqId":".*","level":2,"time":".*","remoteAddr":".*","user":".*","app":".*","method":".*","url":".*","message":"Login failed: '.*' \(Remote IP: '<HOST>'\)".*}$
|
||
|
^{"reqId":".*","level":2,"time":".*","remoteAddr":".*","user":".*","app":".*","method":".*","url":".*","message":"Login failed: .* \(Remote IP: <HOST>\).*}$
|
||
|
```
|
||
|
|
||
|
Now add the filter to your main fail2ban config file
|
||
|
|
||
|
sudo vim /etc/fail2ban/jail.local
|
||
|
|
||
|
Add the following to the end of the file
|
||
|
|
||
|
```
|
||
|
## Nextcloud
|
||
|
[nextcloud]
|
||
|
filter = nextcloud
|
||
|
enabled = true
|
||
|
logpath = /data/nextcloud/data/nextcloud.log
|
||
|
```
|
||
|
|
||
|
restart fail2ban to make it take effect
|
||
|
|
||
|
sudo systemctl restart fail2ban
|
||
|
|
||
|
# Updating the Server
|
||
|
To maintain the system we simply need to update it by running the following command
|
||
|
|
||
|
sudo pacman -Syu
|
||
|
|
||
|
To update the docker containers we need to pull every container and then restart it, it would be a lot of work every time you want to upgrade. It is easier to create a script instead of manually typing all the commands every time.
|
||
|
|
||
|
So create a file
|
||
|
|
||
|
vim ~/Scripts/update-docker.sh
|
||
|
|
||
|
put in the following text
|
||
|
|
||
|
```
|
||
|
#!/bin/bash
|
||
|
|
||
|
# Update Nginx
|
||
|
docker-compose -f /home/USERNAME/Docker/nginx/docker-compose.yml pull
|
||
|
docker-compose -f /home/USERNAME/Docker/nginx/docker-compose.yml down
|
||
|
docker-compose -f /home/USERNAME/Docker/nginx/docker-compose.yml up -d --remove-orphans
|
||
|
|
||
|
# Update MariaDB
|
||
|
docker-compose -f /home/USERNAME/Docker/mariadb/docker-compose.yml pull
|
||
|
docker-compose -f /home/USERNAME/Docker/mariadb/docker-compose.yml down
|
||
|
docker-compose -f /home/USERNAME/Docker/mariadb/docker-compose.yml up -d --remove-orphans
|
||
|
|
||
|
# Update Nextcloud
|
||
|
docker-compose -f /home/USERNAME/Docker/nextcloud/docker-compose.yml pull
|
||
|
docker-compose -f /home/USERNAME/Docker/nextcloud/docker-compose.yml down
|
||
|
docker-compose -f /home/USERNAME/Docker/nextcloud/docker-compose.yml up -d --remove-orphans
|
||
|
```
|
||
|
|
||
|
You should add any services you add to this simple script so all containers get updated
|
||
|
|
||
|
Save, exit and then make it executable with the following command
|
||
|
|
||
|
sudo chmod u+x update-docker.sh
|
||
|
|
||
|
You can now run the script to update all docker containers
|
||
|
|
||
|
# Let the script run with pacman
|
||
|
Why mess around with 2 commands, when you can simply force the docker-update.sh script to run when you update your system using pacman. We can do this very easy with pacman hooks
|
||
|
|
||
|
create a file in the hooks directory for docker
|
||
|
|
||
|
sudo vim /usr/share/libalpm/hooks/docker.hook
|
||
|
|
||
|
Add in the following text
|
||
|
|
||
|
```
|
||
|
[Trigger]
|
||
|
Operation = Upgrade
|
||
|
Type = Package
|
||
|
Target = *
|
||
|
[Action]
|
||
|
Description = Update Docker Containers
|
||
|
When = PostTransaction
|
||
|
Exec = /bin/bash /home/USERNAME/Scripts/update-docker.sh
|
||
|
```
|
||
|
|
||
|
Save and exit and try to update your system, if you are lucky you have an update and you can see the script in action after the update.
|
||
|
|
||
|
# Next
|
||
|
For now I would continue the guide Terminal and Programming are quite useful for a server, I would also recommend reading the maintenance part of the guide. But you can skip basic programs, office, gaming, etc
|
||
|
|
||
|
When you are done you can go to my Docker guide, and install any service your heart desires :)
|