Outdated Tutorial: Please use as a guide only
Hi all,
I've been meaning to write this tutorial since last year , now right out of the gate I will say I am far from an expert.
This is how I set up my NAS Server and docker containers , but I will gladly accept any critique of my setup and advice to better secure or improve the overall setup.
To start with I did try OpenMediaVault, I got everything how I wanted it, however I felt that OpenMediaVault was too bloated for my needs and a little complicated in some of its options. Whilst on the Armbian forum ,seeking advice for getting the fan to work properly for my server (yes I did try tuxd3v 's ATS fan configuration but this didn't work after using a later kernel to get my sata card to work (more on that later)).
I discovered SimpNAS , I posted a thread on this forum for all who are interested . SimpNAS was exactly what I was looking for , simple, elegant and easy .
So to begin I setup OpenMediaVault and long story short I had issues with the pine64 supplied Sata card , I wont go in to detail here as there are many threads that describe this issue, here is some slight insight
So I bought a marvell 88SE9230 Sata card , which so far has been absolutely amazing , rock solid no matter what I throw at it, but to use said card I had to use a newer kernel which as described in the thread resulted in me not being able to use ATS to control the fan.
Now down to business , I have a rockpro64 with NAS case , tall heatsink , case fan, a marvell 88SE9230 Sata card, 2 SSD drives and a very decent SD card , the software is Armbian , with SimpNAS and docker installed and 6 containers - Nginx, Portainer, Homeassistant , Radicale caldav server , Jellyfin media server and EspHome for my home automation gadgets. home assistant and radicale are exposed to internet so they are accessible from outside my local network .
so here are my instructions :
10. add udev rule so drives will show up nano /etc/udev/rules.d/99-marvell.rules.
11. reboot then type lsblk to see if drives are discovered
12. now install SimpNAS
13. follow setup wizard on url shown in terminal at the end of install .
14. login into SimpNAS admin panel using username administrator and password set during wizard
15. in admin panel Add Volumes to add the drives in SimpNAS
16. add users in user section
17. add shares to drives
18. at this point you now have your NAS setup , you can upload all the files you need to via samba
19. now onto portainer run this to get portainer installed and running, Docker is installed when SimpNAS is.
20. now pull the images for the other containers, you can do this via the portainer UI (hence why I installed it first or via command line ) I like to do this now as it can take some time to pull the images ,although they will be installed with docker-compose later, if you prefer doing it all at once and don't want to know how docker works .
21. now to install docker compose which will help later with renewing lets encrypt certificates via certbot . first we need a few packages
22. then using the newly installed pip3 install wheel and docker-compose
23.now lets create a docker-compose.yml file to start our containers and provide all the info they need to get running. When docker compose is run it will create all the necessary directories and files based on this docker-compose file .
24. create nginx.conf file in root home directory this is needed for the nginx container to access the other containers and to have a few needed settings for secure access( edit the duckdns address to suit the url you create on duckdns.org , you will need to create an account and register for a maximum of 5 subdomains) (this if free)
25. in the root home directory create an nginx directory, then a cert directory and then create a dhparam file this is used by the nginx container
26. create a radicale directory , a config directory and add a config file for the radicale container
27. now we need to install apache2-utils so we can use htpasswd to create a password file for users of Radicale . ( I only use Radicale for calendar sync i.e only one user in this case user calendar, but it can be used for multiple users with calendars , address books and to-do lists for each .)
28. now in order for radicale to work a radicale user and group needs to be created , more info can be gleamed from the provider of the Radicale docker container , here
29. we are almost there now , just a few more steps and some optional ones at the end . Time to install fail2ban and certbot ( certbot obtains a lets encrypt certificate for us and will renew the cert via a cronjob specified later .)
30. A few necessary adjustments need to be made to fail2ban , lots of articles are available on the on the internet so I wont touch on them here.u Currently I have it working for home assistant but not Radicale , as Radicale doesn't provide logging easily (logging is how fail2ban block unwanted log ins and block the IP address). It is in the works however and hopefully will be available soon.
I tend to edit this section of jail.local to ignore my local network so as not to ban the IP Address of the machine I use to SSH into the Rockpro64 , this is because SSH Jail is enabled by default on fail2ban .
31. I now add 2 files as specified in the home assistant documentation on fail2ban to use the logging from home assistant and what to look(filter) for in the logging .
32. A word of warning , if you are starting home assistant from new( as in no existing configuration file yet) I would disable fail2ban for now the reason is once restarted it will complain that there is no log located at /root/homeassistant/home-assistant.log . As stated in home assistants documentation linked above you need to have
added to your configuration file for this to work. If you have an existing home assistant configuration you can copy this across now to a home assistant directory located in the root home directory . add the above logger to the config file and then use the touch command to create the home-assistant.log file . Fail2ban can then be restarted or started if disabled and it wont complain about a missing file and will work when the conatiner is started and begins logging to said file .
( change above to start instead of restart if stopped , or change to enable if previously disabled and then start )
now check to see if the jail is up and working
33. Ok finally certbot , to get certbot to work port 80 and port 443 need to be open on your router and directed to the Rockpro64 IP address (I would also highly recommend that a static IP is set for the rockpro64 in your routers settings also )
lets begin by getting a lets encrypt certificate for the duckdns subdomains associated with our home assisatnt and radicale services.
this will retrieve a certificate and store it for you ready for the nginx container to use .
now certbot can renew this certificate for all associated subdomains when they have less than 30 days left until expiration this is done with a cronjob
add this to the now open file
34. and finally lets start the containers and run them in the background .
35. if successful you will see the new containers up and running in portainer at the rockpro64 IP address port 9000 (for example 192.168.1.120:9000)
now you should be able to access the home assistant container at https://yourHomeassisantAddress.duckdns.org or locally at 192.168.1.120:8123(notice port 8123)
you can access the Radicale container via https://yourRadicaleAddress.duckdns.org/radicale/ (notice the extension /radicale/ this was specified in the nginx.conf file earlier.) I would suggest closing the ports on teh router for the time being and accessing home assistant via its local IP address until you have a password and the setup complete then reopen the ports to access outside of you local network . this is of course not necessary if you have an existing configuration with home assistant and are transitioning to the Rockpro64 server .
36. the next few steps are optional , I installed Jellyfin as media server , I did this through the admin panel of SimpNAS under apps this creates a docker container for you and removes some of the steps to get it to access your media files correctly . of course you can install it manually like we did with the other containers and even use docker-compose by adding the container to the docker-compose.yml file , please note that when certbot renews the certificate it stops all the containers in this file before renewing the certs this is so port 80 and 443 are open for it to use and then it restarts the containers again. this will not happen to Jellyfin if installed manually or via the SimpNAS apps
37 . to top it all off, for those who want the firewall blocking ports on their local network to and from the rockpro64 , this is a little over kill as only port 80 and 443 are open to the outside world via the router, but I understand that some people like to add rules for local traffic to prevent spread locally of a virus or similar between machines on that network .
I say this because it quite difficult to get a firewall working properly as docker provides it own set of rules to control the firewall and when you add rules of your own it might not work well with dockers rules if they work at all , so as suggested by a great article I found regarding this issue . here is a solution
firstly create a iptables.conf file in /etc/ directory
and add any rules you want after allow something, those above are just examples using the ports most of the services we have installed, use .
next add these new rules from the file to the kernel to implement them
and then create a service that loads this file on startup .
then add this
then enable and start the service
I think I've covered most of the setup process for my install , of course you will need to configure the service to suit your individual needs and for that I turn you to there excellent documentation
Jellyfin
Radicale
HomeAssistant
Esphome
Docker
SimpNAS
Duckdns
if you have any questions or feedback for me I will help where I can and edit the tutorial based on giving accurate information .
Hi all,
I've been meaning to write this tutorial since last year , now right out of the gate I will say I am far from an expert.
This is how I set up my NAS Server and docker containers , but I will gladly accept any critique of my setup and advice to better secure or improve the overall setup.
To start with I did try OpenMediaVault, I got everything how I wanted it, however I felt that OpenMediaVault was too bloated for my needs and a little complicated in some of its options. Whilst on the Armbian forum ,seeking advice for getting the fan to work properly for my server (yes I did try tuxd3v 's ATS fan configuration but this didn't work after using a later kernel to get my sata card to work (more on that later)).
I discovered SimpNAS , I posted a thread on this forum for all who are interested . SimpNAS was exactly what I was looking for , simple, elegant and easy .
So to begin I setup OpenMediaVault and long story short I had issues with the pine64 supplied Sata card , I wont go in to detail here as there are many threads that describe this issue, here is some slight insight
So I bought a marvell 88SE9230 Sata card , which so far has been absolutely amazing , rock solid no matter what I throw at it, but to use said card I had to use a newer kernel which as described in the thread resulted in me not being able to use ATS to control the fan.
Now down to business , I have a rockpro64 with NAS case , tall heatsink , case fan, a marvell 88SE9230 Sata card, 2 SSD drives and a very decent SD card , the software is Armbian , with SimpNAS and docker installed and 6 containers - Nginx, Portainer, Homeassistant , Radicale caldav server , Jellyfin media server and EspHome for my home automation gadgets. home assistant and radicale are exposed to internet so they are accessible from outside my local network .
so here are my instructions :
- burn Armbian Buster server to SD Card using imager of choice I typically use Gnome disks , which is simple to use .
- after booting ssh to armbian with root and password 1234
- Change root password
- Do not add new user when logged in as root
- apt update && apt upgrade (reboot)
- add ssh keys to root home to ease login with out password (optional )
- type Armbian-config and then select options > System > Other > install latest kernel (5.8+) (server will reboot)
- type echo 0 > /sys/devices/platform/pwm-fan/hwmon/hwmon2/pwm1 this is to turn off the fan which is continously running on the newer kernel .
- edit /etc/rc.local file and add fan settings for startup, I chose these, which allow the fan to run continously but at a whisper quiet setting more info here
Code:
rmmod pwm-fan
echo 0 > /sys/class/pwm/pwmchip1/export
echo 110 > /sys/class/pwm/pwmchip1/pwm0/duty_cycle
echo 500 > /sys/class/pwm/pwmchip1/pwm0/period
echo 1 > /sys/class/pwm/pwmchip1/pwm0/enable
10. add udev rule so drives will show up nano /etc/udev/rules.d/99-marvell.rules.
Code:
ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x1b4b", ATTR{device}=="0x9230", RUN+="/bin/bash -c 'echo %k > /sys/bus/pci/drivers/ahci/bind'"
11. reboot then type lsblk to see if drives are discovered
12. now install SimpNAS
Code:
wget https://simpnas.com/install.sh; bash install.sh
13. follow setup wizard on url shown in terminal at the end of install .
14. login into SimpNAS admin panel using username administrator and password set during wizard
15. in admin panel Add Volumes to add the drives in SimpNAS
16. add users in user section
17. add shares to drives
18. at this point you now have your NAS setup , you can upload all the files you need to via samba
19. now onto portainer run this to get portainer installed and running, Docker is installed when SimpNAS is.
Code:
docker volume create portainer_data
docker run -d -p 8000:8000 -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce
20. now pull the images for the other containers, you can do this via the portainer UI (hence why I installed it first or via command line ) I like to do this now as it can take some time to pull the images ,although they will be installed with docker-compose later, if you prefer doing it all at once and don't want to know how docker works .
Code:
docker pull homeassistant/home-assistant:stable tomsquest/docker-radicale esphome/esphome nginx:latest
21. now to install docker compose which will help later with renewing lets encrypt certificates via certbot . first we need a few packages
Code:
apt install python3 python3-pip python3-setuptools python3-dev libffi-dev
22. then using the newly installed pip3 install wheel and docker-compose
Code:
pip3 install wheel
pip3 install docker-compose
23.now lets create a docker-compose.yml file to start our containers and provide all the info they need to get running. When docker compose is run it will create all the necessary directories and files based on this docker-compose file .
Code:
version: '3.7'
services:
nginx:
depends_on:
- radicale
- homeassistant
image: nginx:latest
container_name: nginx_reverse_proxy
volumes:
- /etc/localtime:/etc/localtime:ro
- /root/nginx.conf:/etc/nginx/nginx.conf
- /root/nginx/error.log:/etc/nginx/error.log
- /root/nginx/access.log:/etc/nginx/access.log
- /etc/letsencrypt/:/etc/letsencrypt/
- /root/nginx/dhparams.pem:/etc/nginx/dhparams.pem
restart: unless-stopped
network_mode: host
radicale:
image: tomsquest/docker-radicale
container_name: radicale
ports:
- 127.0.0.1:5232:5232
init: true
read_only: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- SETUID
- SETGID
- CHOWN
- KILL
healthcheck:
test: curl -f http://127.0.0.1:5232 || exit 1
interval: 30s
retries: 3
restart: unless-stopped
volumes:
- /etc/localtime:/etc/localtime:ro
- /root/radicale/data:/data
- /root/radicale/config:/config:ro
- /root/radicale/users:/etc/radicale/users
- /root/radicale/log:/var/log/radicale/log
homeassistant:
container_name: home-assistant
image: homeassistant/home-assistant:stable
volumes:
- /etc/localtime:/etc/localtime:ro
- /root/homeassistant:/config
environment:
- TZ=Europe/London
restart: unless-stopped
network_mode: host
esphome:
image: esphome/esphome
volumes:
- /root/esphome/config:/config:rw
- /etc/localtime:/etc/localtime:ro
network_mode: host
restart: always
Code:
user www-data;
worker_rlimit_core 500M;
worker_processes 1;
events {
worker_connections 1024;
}
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
error_log /etc/nginx/error.log warn;
access_log /etc/nginx/access.log;
ssl_dhparam /etc/nginx/dhparams.pem;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_protocols TLSv1.2 TLSv1.3;
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
ssl_stapling on;
ssl_stapling_verify on;
ssl_certificate /etc/letsencrypt/live/yourRadicaleAddress.duckdns.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourRadicaleAddress.duckdns.org/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/yourRadicaleAddress.duckdns.org/chain.pem;
ssl_session_cache shared:SSL:10m;
proxy_buffering off;
server {
listen 80;
server_name yourRadicaleAddress.duckdns.org;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name yourRadicaleAddress.duckdns.org;
location /radicale/ {
proxy_pass http://localhost:5232/;
proxy_set_header X-Script-Name /radicale;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_header Authorization;
}
}
server {
listen 80;
server_name yourHomeassistantAddress.duckdns.org;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name yourHomeassistantAddress.duckdns.org;
location / {
proxy_pass http://localhost:8123;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /api/websocket {
proxy_pass http://localhost:8123/api/websocket;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
Code:
openssl dhparam -out /root/nginx/cert/dhparam.pem -dsaparam 2048
Code:
[server]
hosts = 0.0.0.0:5232
max_connections = 15
# 100 Megabyte
max_content_length = 100000000
# 30 seconds
timeout = 20
[auth]
# Average delay after failed login attempts in seconds
delay = 600
type = htpasswd
htpasswd_filename = /etc/radicale/users
# encryption method used in the htpasswd file
htpasswd_encryption = md5
[storage]
filesystem_folder = /data/collections
Code:
apt install apache2-utils
htpasswd -c /root/radicale/users calendar
28. now in order for radicale to work a radicale user and group needs to be created , more info can be gleamed from the provider of the Radicale docker container , here
Code:
addgroup --gid 2999 radicale
adduser --gid 2999 --uid 2999 --shell /bin/false --disabled-password --no-create-home radicale
29. we are almost there now , just a few more steps and some optional ones at the end . Time to install fail2ban and certbot ( certbot obtains a lets encrypt certificate for us and will renew the cert via a cronjob specified later .)
Code:
apt install fail2ban certbot
Code:
cp jail.conf jail.local
nano jail.local
Code:
# "ignoreip" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban
# will not ban a host which matches an address in this list. Several addresses
# can be defined using space (and/or comma) separator.
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24
Code:
nano /etc/fail2ban/jail.d/hass.conf
[hass-iptables]
enabled = true
chain = DOCKER-USER
filter = hass
action = iptables-allports[name=HASS]
logpath = /root/homeassistant/home-assistant.log
bantime = 3600
maxretry = 3
Code:
nano /etc/fail2ban/filter.d/hass.conf
[INCLUDES]
before = common.conf
[Definition]
failregex = ^%(__prefix_line)s.*Login attempt or request with invalid authentication from <HOST>.*$
ignoreregex =
[Init]
datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S
32. A word of warning , if you are starting home assistant from new( as in no existing configuration file yet) I would disable fail2ban for now the reason is once restarted it will complain that there is no log located at /root/homeassistant/home-assistant.log . As stated in home assistants documentation linked above you need to have
Code:
logger:
logs:
homeassistant.components.http.ban: warning
added to your configuration file for this to work. If you have an existing home assistant configuration you can copy this across now to a home assistant directory located in the root home directory . add the above logger to the config file and then use the touch command to create the home-assistant.log file . Fail2ban can then be restarted or started if disabled and it wont complain about a missing file and will work when the conatiner is started and begins logging to said file .
Code:
systemctl restart fail2ban.service
now check to see if the jail is up and working
Code:
fail2ban-client status hass-iptables
33. Ok finally certbot , to get certbot to work port 80 and port 443 need to be open on your router and directed to the Rockpro64 IP address (I would also highly recommend that a static IP is set for the rockpro64 in your routers settings also )
lets begin by getting a lets encrypt certificate for the duckdns subdomains associated with our home assisatnt and radicale services.
Code:
certbot certonly --standalone -d yourRadicaleAddress.duckdns.org -d yourHomeassistantAddress.duckdns.org
now certbot can renew this certificate for all associated subdomains when they have less than 30 days left until expiration this is done with a cronjob
Code:
crontab -e
Code:
@daily certbot renew --pre-hook "docker-compose -f /root/docker-compose.yml down" --post-hook "docker-compose -f /root/docker-compose.yml up -d"
34. and finally lets start the containers and run them in the background .
Code:
docker-compose up -d
now you should be able to access the home assistant container at https://yourHomeassisantAddress.duckdns.org or locally at 192.168.1.120:8123(notice port 8123)
you can access the Radicale container via https://yourRadicaleAddress.duckdns.org/radicale/ (notice the extension /radicale/ this was specified in the nginx.conf file earlier.) I would suggest closing the ports on teh router for the time being and accessing home assistant via its local IP address until you have a password and the setup complete then reopen the ports to access outside of you local network . this is of course not necessary if you have an existing configuration with home assistant and are transitioning to the Rockpro64 server .
36. the next few steps are optional , I installed Jellyfin as media server , I did this through the admin panel of SimpNAS under apps this creates a docker container for you and removes some of the steps to get it to access your media files correctly . of course you can install it manually like we did with the other containers and even use docker-compose by adding the container to the docker-compose.yml file , please note that when certbot renews the certificate it stops all the containers in this file before renewing the certs this is so port 80 and 443 are open for it to use and then it restarts the containers again. this will not happen to Jellyfin if installed manually or via the SimpNAS apps
37 . to top it all off, for those who want the firewall blocking ports on their local network to and from the rockpro64 , this is a little over kill as only port 80 and 443 are open to the outside world via the router, but I understand that some people like to add rules for local traffic to prevent spread locally of a virus or similar between machines on that network .
I say this because it quite difficult to get a firewall working properly as docker provides it own set of rules to control the firewall and when you add rules of your own it might not work well with dockers rules if they work at all , so as suggested by a great article I found regarding this issue . here is a solution
firstly create a iptables.conf file in /etc/ directory
Code:
*filter
:INPUT ACCEPT [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:FILTERS - [0:0]
:DOCKER-USER - [0:0]
-F INPUT
-F DOCKER-USER
-F FILTERS
# BASIC Allow
-A INPUT -i lo -j ACCEPT
# Chain to FILTERS
-A INPUT -j FILTERS
-A DOCKER-USER -i eth0 -j FILTERS
# COMMON FIREWALL RULES
# ALLOW something
-A FILTERS -p all -j ACCEPT -m conntrack --ctstate ESTABLISHED,RELATED
-A FILTERS -p icmp --source 192.168.1.0/24 -j ACCEPT
-A FILTERS -p tcp --source 192.168.1.0/24 --dport 22 -j ACCEPT
-A FILTERS -p tcp --source 192.168.1.0/24 --dport 81 -j ACCEPT
-A FILTERS -p tcp --source 192.168.1.0/24 --dport 82 -j ACCEPT
-A FILTERS -p tcp --source 192.168.1.0/24 --dport 139 -j ACCEPT
-A FILTERS -p tcp --source 192.168.1.0/24 --dport 445 -j ACCEPT
-A FILTERS -p tcp --dport 80 -j ACCEPT
-A FILTERS -p tcp --dport 443 -j ACCEPT
-A FILTERS -p tcp --source 192.168.1.0/24 --dport 8123 -j ACCEPT
-A FILTERS -p tcp --source 192.168.1.0/24 --dport 8096 -j ACCEPT
-A FILTERS -p udp --source 192.168.1.0/24 --dport 1900 -j ACCEPT
-A FILTERS -p udp --source 192.168.1.0/24 --dport 7359 -j ACCEPT
-A FILTERS -p tcp --source 192.168.1.0/24 --dport 8000 -j ACCEPT
-A FILTERS -p tcp --source 192.168.1.0/24 --dport 9000 -j ACCEPT
-A FILTERS -p tcp --source 192.168.1.0/24 --dport 6052 -j ACCEPT
-A FILTERS -p udp --source 192.168.1.0/24 --dport 5353 -j ACCEPT
# DENY something
###################################################################
### special cases for servers
### please modify by the server
### end special cases
############################################################
# FINAL REJECT
# Optional logging
#-A FILTERS -m limit --limit 5/min -j LOG --log-prefix "iptables_INPUT_denied: " --log-level 7
-A FILTERS -p all -j REJECT
COMMIT
and add any rules you want after allow something, those above are just examples using the ports most of the services we have installed, use .
next add these new rules from the file to the kernel to implement them
Code:
iptables-restore -n /etc/iptables.conf
and then create a service that loads this file on startup .
Code:
nano /etc/systemd/system/iptables.service
then add this
Code:
[Unit]
Description=Restore iptables firewall rules
Before=network-pre.target
[Service]
Type=oneshot
ExecStart=/sbin/iptables-restore -n /etc/iptables.conf
[Install]
WantedBy=multi-user.target
Code:
systemctl enable iptables.service
systemctl start iptables.service
I think I've covered most of the setup process for my install , of course you will need to configure the service to suit your individual needs and for that I turn you to there excellent documentation
Jellyfin
Radicale
HomeAssistant
Esphome
Docker
SimpNAS
Duckdns
if you have any questions or feedback for me I will help where I can and edit the tutorial based on giving accurate information .