Starter project for your Synology NAS.
This document describes the Synology NAS automation and manual services that is provided for DevSecOps engineers.
The audience for this document includes:
-
Mobile User who will use their iPhone to access the container applications hosted on Synology NAS.
-
DevSecOps Engineer who will install, configure and update the Synology NAS services to ensure continuous uptime and backup redundancy.
Term | Definition |
---|---|
DSM | Disk Station Manager |
- We recommend Cloudflare security over both Tailscale and Zerotier VPNs to access your Synology NAS.
- write infrastructure as code using a Terraform provider.
- manage infrastructure as code using a GitLab CI/CD pipeline.
- DSM 7.2 has stopped support for Docker.
- Cloudflare has a generous free tier (no credit card) compared to Tailscale.
- has both a built-in SSO and a reverse proxy tunnel.
- does not require an agent to be installed on each device.
- requires a custom domain name.
- High level overview of Cloudflare resources
%%{ init: { 'theme': 'dark' } }%%
flowchart
user["Public Users"]
cf_url-->|resolved by|cf_dns
user-->|HTTPS access|cf_url
%% Zone
cf_zone["Domain Names"]
cf_dns["DNS Records"]
cf_url["Self Hosted Domain Names"]
%% Access
cf_application["Access Applications"]
cf_group["Access Groups"]
cf_policy["Access Policies"]
%% Ingress Rules
cf_tunnel["Tunnels"]
cf_ingress["Ingress Rules"]
%% External Resources
ext_tunnel_agent["Tunnel Agents"]
ext_connect["Connector IDs"]
subgraph Cloudflare SaaS
cf_zone-->|configure|cf_dns
cf_zone-->|required by|cf_ingress
cf_zone-->|required by|cf_application
cf_application-->|authorize|cf_ingress
subgraph SSO
cf_application-->|assign|cf_policy
cf_policy-->|bounded by|cf_group
end
subgraph Reverse Proxy
cf_tunnel-->|configure|cf_ingress
end
end
subgraph Home LAN
cf_tunnel-->|map to|ext_connect
cf_tunnel-->|HTTP access|ext_tunnel_agent
ext_tunnel_agent-->|create|ext_connect
cf_ingress-->|allow to|ext_tunnel_agent
end
-
ZeroTier was deprecated in favour of Tailscale.
-
Tailscale was deprecated in favour of Cloudflare.
- Tailscale is supported by Synology Package Center, hence it does not require running a custom Docker container.
- Tailscale has a lower limit on users and devices than ZeroTier.
-
Tailscale overview
Category | Activity | Mobile User | DSO Engineer |
---|---|---|---|
Installation & Configuration | Installing Ansible | R,A | |
Installation & Configuration | Setting up a cloudflared agent on your Synology NAS |
R,A | |
Installation & Configuration | Protecting the route from Public Domain to your server | R,A | |
Execution | Creating a basic inventory file | R,A | |
Execution | Running your first Ad-Hoc Ansible command | R,A | |
Execution | Your first Ansible playbook | R,A | |
Execution | Docker-compose Deployment with SSH | R,A | |
Execution | Docker-compose Deployment with Portainer Stack | R,A | |
Execution | Docker-compose Deployment with Portainer Containers | R,A | |
Execution | Adding a new route from Public Domain to your server | R,A | |
Updating | Manually updating DSM | R,A |
You can enable SSH service from the Control Panel
. Go to Terminal & SNMP
and check the box Enable SSH service
and click Apply
.
After enabling SSH service, you will find out that you are unable to login with the admin password on the Synology web interface. We will need to temporarily enable Telnet service to fix this problem.
Click here to Temporarily Enabling Telnet (Deprecated).
Under the previous Terminal & SNMP
, check the box Enable Telnet service
and click Apply
. You have to connect to both Telnet and SSH on the LAN because it doesn't work with Quickconnect.
On your iPhone, download the app iTerminal. Open the app and create a Telnet connection specifying your [PRIVATE_IP]
address on port 23
.
Login using admin
and same password as your Synology web interface. Type the following command to change the SSH password:
$ sudo synouser --setpw admin [PASSWORD]
If it doesn't work the first time, try again. After changing the password, you should be able to SSH to your Synology.
Return to your Synology Control Panel
, and disable the Telnet service.
Warning: Changing the admin
password using Task Scheduler did not work.
Click here to Connecting via SSH.
Using the app iTerminal, create an SSH connection specifying your IP address on port 22. Login using admin and your password, and type EXACT:
$ sudo ln -s /var/run/docker.sock /volume1/docker/docker.sock
Warning: Even after creating the symlink you cannot create the container from the Docker UI. This is because symlinks are not listed when trying to create a volume/file link.
Click here to Installing Portainer.
The Synology Docker UI is nice but lacks some functionality such as Stacks, Templates, etc. Portainer will run seamlessly along side the Synology Docker UI.
First make a folder on your Synology Web Interface to hold the portainer data, using File Station, i.e. /DBDock/docker/portainer
.
However before we can install Portainer, we need to login via SSH as admin
(password is same as your Synology Web Interface).
ssh admin:[PASSWORD]@[PRIVATE_IP]
Now run the following command to grab the Portainer image.
docker run -d -p 8000:8000 -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v /volume1/docker/portainer:/data portainer/portainer
Warning: Do not change volume1
as it is the EXACT name of DBDock.
Now check to see if it worked, you need to access the Portainer container from your LAN, i.e. [PRIVATE_IP], on port 9000. Create the admin user and password.
Once logged in, select the Local environment and press the Connect button. You should be able to see a Dashboard of all your Docker files.
Click here to Using Portainer.
Access the Portainer container from your LAN, i.e. [PRIVATE_IP], on port 9000, and login as admin
.
Click on the side menu Containers
, then click on + Add Container
.
Enter both the container and image names. For example, objTeedy
and jdreinhardt/teedy:latest
.
Click on + publish a new network port
and enter host port 8080
and container port 8080
. Ensure TCP
is selected.
Click on Volumes
and then + map additional volume
button.
Map container path /data
to host volume teedy_vol_data
.
Click on Network
and then select nginxpm_net_public
.
Click on Deploy the container
.
Click here to Installing Ansible.
Ansible's only real dependency is Python. Once Python is installed, the simplest way to get Ansible running is to use pip
.
pip install ansible
You must enable SSH remote connection on your Synology NAS before running Ansible commands. You can follow the steps in this article:
This runbook should be performed by the DevSecOps.
Click here to Setting up a `cloudflared` agent on your Synology NAS.
Warning: You should perform this runbook for client-facing applications that do not contain sensitive data.
-
Navigate to your Synology DSM Console > Package Center > Settings > Package Sources.
-
Click Add to add a new package source.
- For Name, enter
SynoCommunity
. - For Location, enter
https://packages.synocommunity.com
.
-
Click OK, then click Community in the left hand menu.
-
Search for
cloudflared
package, then click Install. -
Before you can complete the installation, you will require a
cloudflared
registration token. -
Open a new browser tab, and navigate to your Cloudflare Console > Zero Trust > Access > Tunnels.
-
Click Create a tunnel, then enter a Tunnel name, e.g.
dbdock-synocommunity-package-cloudflared
. -
Click Save tunnel to generate a registration token, then copy this token.
-
Return to your browser tab Synology Console, then paste this token to complete the
cloudflared
package installation. -
If successful, you should see
cloudflared
package Running on your Synology NAS. -
Return to your browser tab Cloudflare Console, and you should see the status
HEALTHY
for your new tunnel.
Note: For the next step, you may expose multiple applications or services within your new tunnel by Adding a new route from Public Domain to your server.
Click here to Protecting the route from Public Domain to your server.
This runbook should be performed by the DevSecOps.
Cloudflare Zero Trust enables you to protect your routes from Public Domain to your applications and services hosted on internal server, by setting up an Application Policy, which is similar to a web application firewall (WAF).
-
Navigate to your Cloudflare Console > Zero Trust > Access > Applications.
-
Click Add an application, and then select Self-hosted type.
-
Enter the following values:
- For Application name, enter a name for the application policy, e.g.
dbdock-synocommunity-package-cloudflared
. - For Session Duration, enter the length of each session, e.g.
24 hours
.
- Under the Application domain, enter one or more Public domains that you want to protect.
- For Subdomain, enter your Public subdomain, e.g.
localstack
. - For Domain, select your Public domain from the drop-down list, e.g.
markit.work
.
- Click Next, for Policy name, enter a new policy, e.g.
default
.
- For Action, leave as Allow.
- Under Configure rules, add one or more Include rules to whitelist your users.
- For Selector, choose
Country
, and for Value, choose one or more countries from the drop-down list, e.g.Singapore
. - Click Add include, then for Selector, choose
Emails
, and for Value, enter one or more emails to whitelist.
- Click Next, leave all values as default, and click Add application.
Click here to Creating a basic inventory file.
Ansible uses an inventory file (basically, a list of servers) to communicate with your servers. Create a file hosts
in your project root folder and add one server to it:
[wifi]
192.0.2.5
The wifi
is an arbitrary name for the group of servers you're managing, followed by the IP addresses of your servers, one per line. If you're not using port 22 for SSH, you will need to append it to the address.
Click here to Running your first Ad-Hoc Ansible command.
Now that you've installed Ansible and created an inventory file, it's time to run a command:
ansible <group> -i ./hosts -m ping -u <user>
Ansible assumes you're using a passwordless login for SSH (e.g. you login by entering ssh <user>@<serverip>
). If you're using a password, add the -k
flag to Ansible commands (you may need to install the sshpass
package for this to work).
You can associate a <user>
with each IP address, thus making your Ansible commands shorter.
[wifi]
192.0.2.5 ansible_user=admin
Alternatively, you can place the ansible_user
variable under a group <group>:vars
, which then applies the variables to all servers in that group.
[wifi]
192.0.2.5
[wifi:vars]
ansible_user=admin
The previous command would now be:
ansible <group> -i ./hosts -m ping
Click here to Your first Ansible playbook.
Create a folder plays/
and add a file check-linux-system-playbook.yml
in that folder. Copy and paste the following code:
---
- hosts: all
become: yes
tasks:
- name: Ensure NTP is running
service: name=ntpd state=started
Now that you've created your first Ansible playbook, it is time to run it.
ansible-playbook -i ./hosts plays/check-linux-system-playbook.yml
Click here to Docker-compose Deployment with SSH.
- Open a local terminal and remote SSH to your Synology NAS.
ssh admin@IP_ADDRESS
-
Navigate to the path
~/docker-compose/paperless
, where HOME folder is/var/services/homes/admin/
. -
Run docker-compose with
sudo
privileges.
sudo docker-compose.yml
- Ensure that the
USERMAP_UID
andUSERMAP_GID
is unused.
cat /etc/passwd | grep 1000
- Create a paperless admin user.
docker exec -it paperlessng_webserver_1 python3 /usr/src/paperless/src/manage.py createsuperuser --username=admin --
Click here to Docker-compose Deployment with Portainer Stack.
-
Navigate and login to Portainer UI, e.g. http://localhost:9000, then click on Local.
-
Click on Stacks -> + Add stack.
- Name: paperlessng
Navigate to the section Build method, and click on Upload, then Select file. Select your docker-compose.yml
file.
# docker-compose file for running paperless from the Docker Hub.
# This file contains everything paperless needs to run.
# Paperless supports amd64, arm and arm64 hardware.
#
# All compose files of paperless configure paperless in the following way:
#
# - Paperless is (re)started on system boot, if it was running before shutdown.
# - Docker volumes for storing data are managed by Docker.
# - Folders for importing and exporting files are created in the same directory
# as this file and mounted to the correct folders inside the container.
# - Paperless listens on port 8010.
#
# In addition to that, this docker-compose file adds the following optional
# configurations:
#
# - Instead of SQLite (default), PostgreSQL is used as the database server.
#
# To install and update paperless with this file, do the following:
#
# - Open portainer Stacks list and click 'Add stack'
# - Paste the contents of this file and assign a name, e.g. 'Paperless'
# - Click 'Deploy the stack' and wait for it to be deployed
# - Open the list of containers, select paperless_webserver_1
# - Click 'Console' and then 'Connect' to open the command line inside the container
# - Run 'python3 manage.py createsuperuser' to create a user
# - Exit the console
#
# For more extensive installation and update instructions, refer to the
# documentation.
version: "2"
services:
broker:
image: redis:6.0
restart: unless-stopped
volumes:
- vol_redis:/data
networks:
- net_private
db:
image: postgres:13
restart: unless-stopped
volumes:
- vol_postgres:/var/lib/postgresql/data
environment:
POSTGRES_DB: paperless
POSTGRES_USER: paperless
POSTGRES_PASSWORD: paperless
networks:
- net_private
webserver:
image: jonaswinkler/paperless-ng:latest
restart: unless-stopped
depends_on:
- db
- broker
ports:
- 8080:8000
# URL: http://localhost:8080/
volumes:
- vol_data:/usr/src/paperless/data
- vol_media:/usr/src/paperless/media
- /var/services/homes/admin/docker-compose/paperless/export:/usr/src/paperless/export
- /var/services/homes/admin/docker-compose/paperless/consume:/usr/src/paperless/consume
environment:
PAPERLESS_REDIS: redis://broker:6379
PAPERLESS_DBHOST: db
# The UID and GID of the user used to run paperless in the container. Set this
# to your UID and GID on the host so that you have write access to the
# consumption directory.
USERMAP_UID: 1000
USERMAP_GID: 1000
# Additional languages to install for text recognition, separated by a
# whitespace. Note that this is
# different from PAPERLESS_OCR_LANGUAGE (default=eng), which defines the
# language used for OCR.
# The container installs English, German, Italian, Spanish and French by
# default.
# See https://packages.debian.org/search?keywords=tesseract-ocr-&searchon=names&suite=buster
# for available languages.
#PAPERLESS_OCR_LANGUAGES: tur ces
# Adjust this key if you plan to make paperless available publicly. It should
# be a very long sequence of random characters. You don't need to remember it.
#PAPERLESS_SECRET_KEY: change-me
# Use this variable to set a timezone for the Paperless Docker containers. If not specified, defaults to UTC.
PAPERLESS_TIME_ZONE: Asia/Singapore
# The default language to use for OCR. Set this to the language most of your
# documents are written in.
#PAPERLESS_OCR_LANGUAGE: eng
networks:
- net_public
- net_private
volumes:
vol_redis:
vol_postgres:
vol_data:
vol_media:
# Named volumes are stored in a part of the host filesystem
# which is managed by Docker (/volume1/@docker/volumes/ on Synology)
# Docker appends [FOLDER] name to named volumes.
# paperlessng_vol_data
networks:
net_public:
net_private:
# Docker appends [FOLDER] name to named networks.
# paperlessng_net_public
# paperlessng_net_private
-
Open the container
paperless_webserver_1
. Navigate to Connected networks, and select a networkbridge
. Then, click Join network. -
Navigate to the Container status, and click on >_ Console. Then, click Connect and type the following command:
python3 /usr/src/paperless/src/manage.py createsuperuser --username=admin
This command will create a superuser admin
. Enter your email and password when prompted. Type exit
when done.
- Navigate to the Container status, and click on Logs. Check if there are any errors in the log.
Click here to Docker-compose Deployment with Portainer Containers.
-
Navigate and login to Portainer UI, e.g. http://localhost:9000, then click on Local.
-
Add volumes for all containers. Click on Volumes -> + Add volume.
- Name: paperless_vol_redis
Click Create the volume. Do this for all the volumes.
- Add the database container. Click on + Add container button and enter the database info.
- Name: paperless_redis
- Image: redis:6.0
Navigate to the section Advanced container settings, click on Volumes -> + map additional volume.
- container: /data
- volume: paperless_vol_redis
Navigate to Network, and select nginxpm_net_private
.
Navigate to Restart policy, and select Unless stopped.
Click Deploy the container and your database container should start. Do this for all the databases.
- Add the web server container. Click on + Add container button and enter the application info.
- Name: paperless_app
- Image jonaswinkler/paperless-ng:latest
Navigate to the section Network ports configuration, click on + publish a new network port.
- host: 8080
- container: 8000
Navigate to the section Advanced container settings, click on Volumes -> + map additional volume.
-
container: /usr/src/paperless/data
-
volume: paperless_vol_data
-
container: /usr/src/paperless/media
-
volume: paperless_vol_media
-
container: /usr/src/paperless/export
-
volume: /var/services/homes/admin/docker-compose/paperless/export
-
container: /usr/src/paperless/consume
-
volume: /var/services/homes/admin/docker-compose/paperless/consume
Navigate to Network, and select bridge
. Also, add nginxpm_net_private
.
Navigate to Env, and click on + add environment variable.
-
name: PAPERLESS_REDIS
-
value: redis://paperless_redis:6379
-
name: PAPERLESS_DBHOST
-
value: paperless_db
-
name: USERMAP_UID
-
value: 1000
-
name: USERMAP_GID
-
value: 1000
Navigate to Restart policy, and select Unless stopped.
Click here to Adding a new route from Public Domain to your server.
This runbook should be performed by the DevSecOps.
-
Navigate to your Cloudflare Console > Zero Trust > Access > Tunnels > select an existing tunnel.
-
Click Configure > Public Hostname > click Add a public hostname.
-
Enter the following values, under Public hostname:
- For Subdomain, enter your application name, e.g.
localstack
. - For Domain, select an existing domain from the drop-down list, e.g.
markit.work
.
- Enter the following values, under Service:
- For Type, select
HTTP
. - For URL, enter the LAN IP address and port of your application, e.g.
192.x.x.1:8080
.
Warning: You can only add a LAN IP address of an application that is hosted on the same server as your
cloudflared
agent.
-
Leave the other fields as default values, and click Save hostname.
-
Navigate to Cloudflare Console > Zero Trust > Access > select an existing application policy name.
-
Click Configure > Policies, select a policy name, e.g.
default
> click Overview. -
Under Application domain, click Add domain.
- For Subdomain, enter your application name, e.g.
localstack
. - For Domain, select an existing domain from the drop-down list, e.g.
markit.work
.
- Test the domain by navigating to the your new URL, e.g.
https://localstack.markit.work
.
Click here to Manually updating Disk Station Manager (DSM).
This runbook should be performed by the DevSecOps Engineer.
-
Navigate to your Synology NAS Console > Control Panel > Update & Restore
-
Click on Download to download the latest DSM.
-
After download has completed successfully, click on Update Now.
-
The update may restart the device including all services and packages once the update is complete.
Click here to Manually installing additional RAM in Synology DS920+.
This runbook should be performed by the DevSecOps Engineer.
- Before installing the RAM, perform any updates to the DSM first.
| The RAM used is the official Synology Memory Module, D4NESO-2666-4G, DDR4-2666 260pin, Non-ECC Unbuffered SO-DIMM 4GB RAM.
-
Check your existing memory size before shutting down your Synology NAS.
-
Remove all the HDD trays from your NAS while taking note of the order.
-
Earth yourself by touching a heavy metal object first.
-
Insert the additional RAM on the right inner wall of the NAS until you hear a click sound.
-
Insert all the HDD trays in the same order as before.
-
Start your Synology NAS and check your new memory size.
Click here to troubleshoot Using SSH connection type with passwords.
If you get this error when using option -k
to ask for connection password, you must install the sshpass program.
<SERVER_IP> | FAILED! => {
"msg": "to use the 'ssh' connection type with passwords, you must install the sshpass program"
}
There are instructions on how to install sshpass here:
For macOS, you will need to install Xcode and command line tools then use the unofficial Homebrew command:
brew install hudochenkov/sshpass/sshpass
Click here to troubleshoot Too many authentication failure.
If you get the following SSH Error:
$ Received disconnect from host: 2: Too many authentication failures
This could happen if you have five or more DSA/RSA identity files stored in your .ssh directory. In this case if the -i option isn't specified at the command line the ssh client will first attempt to login using each identity (private key) and next prompt for password authentication. However, sshd drops the connection after five bad login attempts (again default may vary).
So if you have a number of private keys in your .ssh directory you could disable Public Key Authentication at the command line using the -o optional argument.
ssh -o PubkeyAuthentication=no <user>@<serverip>
Click here to troubleshoot You are still asked by SSH to enter a password.
SSH to your server and change the following permissions:
chmod 755 $HOME
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
Click here to troubleshoot Missing sudo password.
If you get the following error:
fatal: [192.168.86.32]: FAILED! => {"msg": "Missing sudo password"}
Connect to your Synology NAS via SSH and then become a superuser sudo su -
. Enter your root
password which should be the same as your admin
password.
Create a file /etc/sudoers.d/admin
and add the following line:
admin ALL=(ALL) NOPASSWD:ALL
You have enabled superuser for your Ansible commands.
The following resources were used as a single-use reference.
Title | Author | Publisher Date [Short Code] |
---|---|---|
GitHub repo: CLI client for Portainer | Juan Carlos Mejías Rodríguez | Oct 2019 |
Ansible for DevOps | ||
How to build your inventory | Ansible Documentation | 2022 |
Synology Docker Media Server with Traefik, Docker Compose, and Cloudflare | Anand | Jul 2020 |
DSM broken after latest Update |