-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* restic backups to local and remote repositories * wrapper script to create local backups and copy them to remote repositories * SSH setup for NAS to NAS access * OpenVPN setup for NAS to NAS connection * rootless * automatic reconnection * helper script and documentation for CA setup and certificate flow * Makefile to simplify playbook usage and OpenVPN setup * various improvements * rootless miniDLNA * ddclient for DynV6 dynamic IP service * split vaults into machine-specific and global
- Loading branch information
1 parent
7b51c55
commit b8dd7ef
Showing
62 changed files
with
1,925 additions
and
391 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,11 @@ | ||
/*.retry | ||
/.vault_pass | ||
|
||
# default certificate authority location | ||
/ca/ | ||
# files in transit to/from the certificate authority | ||
/files-ca-transit/ | ||
# sensitive files distributed to OpenVPN clients | ||
/files/*/openvpn/ta.key | ||
|
||
/wiki |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
ANSIBLE_VAULT_PASSWORD_FILE = .vault_pass | ||
ANSIBLE_INVENTORY = ko | ||
EASYRSA_DIR = ca | ||
EASYRSA_PKI_DIR = ${EASYRSA_DIR}/pki | ||
CA_TRANSFER_FILES_DIR = files-ca-transit | ||
REQUESTS_ARCHIVE = ${CA_TRANSFER_FILES_DIR}/requests.zip | ||
CERTS_ARCHIVE = ${CA_TRANSFER_FILES_DIR}/certificates.zip | ||
|
||
koblenz: | ||
@$(eval ANSIBLE_INVENTORY=ko) | ||
|
||
bendorf: | ||
@$(eval ANSIBLE_INVENTORY=bendorf) | ||
|
||
openvpn-initialize: | ||
ansible-playbook --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} -i inventories/${ANSIBLE_INVENTORY} setup.yml --tags openvpn-init | ||
|
||
openvpn-pack-requests: | ||
@./openvpn-helper.sh pack-requests | ||
|
||
openvpn-distribute-server-files: | ||
@./openvpn-helper.sh copy-files "${ANSIBLE_VAULT_PASSWORD_FILE}" | ||
|
||
openvpn-extract-certificates: | ||
@[ -f "${CERTS_ARCHIVE}" ] || ( echo 'Certificates archive path invalid or unknown, use `make openvpn-extract-certificates CERTS_ARCHIVE=/path/to/certificates.zip`'; exit 2 ) | ||
@./openvpn-helper.sh extract-certificates "${CERTS_ARCHIVE}" | ||
|
||
openvpn-configure: | ||
ansible-playbook --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} -i inventories/${ANSIBLE_INVENTORY} setup.yml --tags openvpn-config | ||
|
||
# for usage on a secure, offline CA device | ||
openvpn-ca-init: export EASYRSA_PKI = ${EASYRSA_PKI_DIR} | ||
openvpn-ca-init: | ||
@echo '[WARN] Ensure to have your certificate authority reside on an offline device, due to security!' | ||
@[ ! -f "${EASYRSA_PKI}/ca.crt" ] || ( echo 'CA is already fully initialized'; exit 3 ) | ||
@dpkg -s easy-rsa | grep Status | grep -q installed || sudo apt install -y easy-rsa | ||
make-cadir "${EASYRSA_DIR}" | ||
"${EASYRSA_DIR}/easyrsa" init-pki | ||
@dd if=/dev/urandom of="${EASYRSA_PKI}/.rnd" bs=256 count=1 | ||
"${EASYRSA_DIR}/easyrsa" build-ca | ||
openvpn-ca-sign-requests: | ||
@[ -f "${REQUESTS_ARCHIVE}" ] || ( echo 'Requests archive path unknown, use `make openvpn-ca-sign-requests REQUESTS_ARCHIVE=/path/to/requests.zip`'; exit 2 ) | ||
@./openvpn-helper.sh sign-requests "${REQUESTS_ARCHIVE}" | ||
|
||
setup-all: | ||
ansible-playbook --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} -i inventories/${ANSIBLE_INVENTORY} setup.yml | ||
|
||
setup-base-without-mount: | ||
ansible-playbook --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} -i inventories/${ANSIBLE_INVENTORY} setup.yml --tags os,sshd,ufw,userdata | ||
|
||
setup-base-with-mount: | ||
ansible-playbook --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} -i inventories/${ANSIBLE_INVENTORY} setup.yml --tags os,sshd,ufw,mount,userdata | ||
|
||
setup-auto-upgrade: | ||
ansible-playbook --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} -i inventories/${ANSIBLE_INVENTORY} setup.yml --tags auto-upgrade | ||
|
||
setup-backup: | ||
ansible-playbook --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} -i inventories/${ANSIBLE_INVENTORY} setup.yml --tags backup | ||
|
||
setup-dynv6: | ||
ansible-playbook --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} -i inventories/${ANSIBLE_INVENTORY} setup.yml --tags ddns | ||
|
||
setup-minidlna: | ||
ansible-playbook --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} -i inventories/${ANSIBLE_INVENTORY} setup.yml --tags dlna | ||
|
||
setup-mumble: | ||
ansible-playbook --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} -i inventories/${ANSIBLE_INVENTORY} setup.yml --tags mumble | ||
|
||
setup-samba: | ||
ansible-playbook --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} -i inventories/${ANSIBLE_INVENTORY} setup.yml --tags samba | ||
|
||
vault-edit-global-nas: | ||
ansible-vault edit --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} group_vars/nas/vault | ||
|
||
vault-edit-inventory-nas: | ||
ansible-vault edit --vault-password-file=${ANSIBLE_VAULT_PASSWORD_FILE} inventories/${ANSIBLE_INVENTORY}/group_vars/nas/vault |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,135 +1,143 @@ | ||
# NAS Playbook | ||
|
||
This repository contains Ansible playbooks and roles to transform your RaspberryPi (or another system based on Debian/Ubuntu) into a NAS. | ||
At the very heart, the term NAS refers to an OpenSSH server to securely backup user data to: | ||
At the very heart, the term NAS refers to a machine with: | ||
|
||
* secure OpenSSH server to automatically backup files | ||
* a secure OpenSSH server to automatically backup files | ||
* encrypted file transfer (SSH) | ||
* key-based authentication (e.g. [backupnas](https://github.com/sebschlicht/backupnas) for UNIX clients, [FreeFileSync](https://freefilesync.org/) for Windows clients) | ||
* with all users created automatically and their SSH keys in place | ||
* with `fail2ban` enabled | ||
* with all users created and configured automatically | ||
* `fail2ban` enabled | ||
|
||
However, this playbook supports a variety of services (e.g. Samba, DLNA, Nextcloud) that can be plugged in by specifying one or more [tags](#tags). | ||
|
||
While most configuration options are hard-coded to fit the recommendations for the respective service, you can configure (among others): | ||
|
||
* the users, their SSH keys and passwords (individually for all services) | ||
* the storage locations (for SSH-based backups and the Nextcloud instance) | ||
* which folders to expose via DLNA | ||
* the mail account to use for notifications | ||
* users, their SSH keys and passwords | ||
* storage locations | ||
* folders accessible via DLNA | ||
* mail account used for system notifications | ||
|
||
## Tags | ||
|
||
* `mount`: automatically mount an external hard drive to extent storage capabilities | ||
* `mount`: automatically mount external hard drives to store user data/backups at | ||
* `backup`: schedule [restic](https://restic.net/) to securely backup the entire user data (at 2 a.m.) to a local directory / external hard drive or remote location (SFTP) | ||
* `cloud`: [Nextcloud](https://nextcloud.com/) server to upload and share files in a personal cloud | ||
* encrypted file transfer (HTTPS) | ||
* automatically renewed SSL certificates (Let's Encrypt) | ||
* Mozilla's intermediate SSL configuration (secure but aiming for compatibility) | ||
* fully installed and configured | ||
* including all required components and users | ||
* applying performance recommendations and hardening | ||
* `samba`: [Samba](https://www.samba.org/) server to access stored files from any desktop or mobile device | ||
* `samba`: [Samba](https://www.samba.org/) server to access stored files from any desktop or mobile device in your network | ||
* password authentication for users, optional guest shares | ||
* *unencrypted file transfer* | ||
* `dlna`: [DLNA](https://en.wikipedia.org/wiki/Digital_Living_Network_Alliance) server to access stored media files from smartTVs etc. | ||
* `dlna`: [DLNA](https://en.wikipedia.org/wiki/Digital_Living_Network_Alliance) server to access stored media files from a SmartTV etc. | ||
* automatically discover and play videos and/or music | ||
* *unencrypted file transfer* | ||
* `mumble`: [Mumble](https://www.mumble.info/) server (aka. Murmur) for low-latency voice chats | ||
* `ddns`: dynamic DNS update client | ||
* hourly (and on reboot) reports the device's IP address to DynV6, to have it accessible via a domain | ||
* `auto-upgrade`: keep installed software up-to-date | ||
* `ufw`: firewall to restrict incoming traffic to the selected services (i.e. specified tags) | ||
* `openvpn-config`: make the device an [OpenVPN client/server](#openvpn-setup) to form a network with another device | ||
|
||
## Usage | ||
|
||
In order to apply this playbook to one of your machines, there are basically two steps required: | ||
|
||
1. [configure the inventory](#inventory-configuration) to your needs | ||
1. Run the playbook: | ||
1. Use the named [tags](#tags) to precisely choose which services to install, e.g. only Samba and Nextcloud: | ||
2. Run the playbook, using [tags](#tags) to precisely choose which services to install: | ||
|
||
ansible-playbook -i inventories/custom setup.yml --tags samba,nextcloud | ||
ansible-playbook -i inventories/example setup.yml --tags samba,nextcloud | ||
|
||
1. Omit tags to install all available services: | ||
## Configuration | ||
|
||
ansible-playbook -i inventories/custom setup.yml | ||
The configuration consists of | ||
1. global variables at `group_vars/all.yml` | ||
2. an Ansible inventory containing | ||
1. the address of the remote machine(s) at `hosts.yml` | ||
2. specific variables at `group_vars/nas/vars.yml` | ||
3. specific credentials in an [Ansible vault](https://docs.ansible.com/ansible/latest/user_guide/vault.html) at `group_vars/nas/vault` | ||
3. files to be copied to the remote machine in the `files` folder | ||
|
||
## Inventory Configuration | ||
The example inventory at `inventories/example` can be copied and fully adapted to your needs. | ||
|
||
The inventory `inventories/custom` is intended to be used when running this playbook and can be fully adapted to your needs. | ||
The configuration variables are loosely structured by the features that this playbook offers. | ||
If you use tags to selectively enable features, you only have to adapt their variables and may omit others. | ||
|
||
Within this inventory, you *must* adapt the first two files and *should* make use of the third, though it's not technically required: | ||
Check existing files for available variables and their meaning. | ||
|
||
Path | Defines | ||
----------------------------- | ------- | ||
`hosts.yml` | [hosts](#hosts) that the playbook is running against | ||
`group_vars/custom/vars.yml` | [variables](#variables) that are used in the playbook | ||
`group_vars/custom/vault.yml` | [vault](#vault) for credential variables | ||
## Port Forwarding | ||
|
||
### Hosts | ||
> **_Note:_** Instead of forwarding relevant ports from your router to the NAS, strongly consider using a VPN to avoid any unencrypted file transfer (e.g. Samba) via internet. | ||
The hosts file `hosts.yml` defines the machine that will be configured by this playbook. | ||
The NAS runs different services that operate on various ports. | ||
When operating the NAS behind a router (i.e. firewall), these ports have to be accessible. | ||
|
||
Simply edit the file and place your machine's address/hostname (e.g. `pi3`): | ||
In case you insist on using port forwarding, the following ports are used by the respective services: | ||
|
||
all: | ||
children: | ||
nas: | ||
hosts: | ||
pi3 | ||
| Service | TCP | UDP | | ||
| --------- | -------- | -------- | | ||
| Samba | 139, 445 | 137, 138 | | ||
| OpenSSH | 22 | | ||
| Nextcloud | 80, 443 | | ||
|
||
### Variables | ||
These are the default ports. | ||
Strongly-consider to expose the services at non-default ports to prevent the load and risk caused by bots. | ||
An execption are the ports 80 and 443, as they are required for the automatic certificate renewal. | ||
|
||
All variables of this playbook that have to be customized are defined in the YAML file `group_vars/custom/vars.yml`. | ||
There are additional variables that usually do not need to be changed but are included in the table below. | ||
## OpenVPN Setup | ||
|
||
The variables are loosely structured by the different features that this playbook offers and should be quite self-explanatory already. | ||
The table below lists which variables are available for each feature. | ||
You only need to care about the variables of features that you are going to install. | ||
This repository contains make goals (scripts) to setup a permanent OpenVPN connection between machines with internet access. | ||
One of them acts as the server and thus must be reachable via internet (e.g. dynamic DNS). | ||
|
||
| Role | Variable | Description | ||
| ---- | -------- | ----------- | ||
| * | `setup_user` | remote user to login and use for the setup process | ||
| * | `nas.hostname` | desired hostname of the NAS | ||
| * | `nas.user.name` | name of the artificial NAS user that is used for service tasks (will be created if missing) | ||
| * | `backup_location` | path to store all user data at. defaults to the primary mount, if specified | ||
| * | `sshd.allow_additional_users` | list of users that are allowed to SSH into the NAS, in addition to the individual users | ||
| * | `sshd.enable_password_authentication` | flag to enable the password-based authentication for SSH clients (default: `false`). strongly discouraged, specify authorized keys per user instead | ||
| * | `users` | list of individual users, each having a user `name`, an `initial_password` hash, a `nextcloud_password` and a `samba_password` (if the respective features are used). you may also specify a `backup_folder_name` (defaults to username) and additional group assignments (`groups`) on the OS level. you may as well specify a list of `authorized_keys` for passwordless SSH access | ||
| mount | `mounts_base_dir` | directory for mount points for external storage devices | ||
| mount | `mounts.primary.*` | primary mount point that all user data will be stored at, by default. mounted via the `uuid` field. use `fstype` and `opts` to specify the filesystem and options of the mount (defaults fit NTFS-formatted drives) | ||
| mount | `mounts.secondary.*` | optional secondary mount point, that the primary mount point will be mirrored to (same configuration options available) | ||
| auto-upgrade, cloud | `mailing.*` | `server`, `user` name, `address`, `password` and `sender_name` for the mail account to be used for sending mails (e.g. notifications, password resets) | ||
| auto-upgrade, cloud | `mail_recipient` | recipient for administrator mails (e.g. performed upgrades) | ||
| cloud | `nas.domain` | public domain to access the machine | ||
| cloud | `dynv6.token` | token to update the current IP address of the NAS domain on DynV6 | ||
| samba | `samba.internal_shares` | internal Samba shares, each having a `name` and a `path`, that are accessible with any account | ||
| samba | `samba.public_shares` | public Samba shares, each having a `name` and a `path`, that are accessible even without an account | ||
| dlna | `minidlna.*` | `display_name` to be shown in client devices, `directories` to list paths that should be accessible for clients | ||
| mumble | `mumble.password` | password that is required by clients to connect (may be empty) | ||
### Participants | ||
|
||
### Vault | ||
There are multiple participants in such an OpenVPN setup: | ||
|
||
An Ansible vault is a place for storing credentials and sensitive information alike in an encrypted manner. Please refer to the [official documentation](https://docs.ansible.com/ansible/latest/user_guide/vault.html) if you want to learn how to create and use one. | ||
1. certificate authority (CA): owns the key that will sign server and client certificates; may be an offline machine (more secure but requires manually copying files) or your Ansible host | ||
2. server: accepts OpenVPN connections by clients; must be accessible via internet (best to use dynamic DNS) | ||
3. clients: connect to the server; must have internet access to reach the server | ||
|
||
Using a vault is mandatory if you want to have your credentials under version control, for example. | ||
For simplicity, you may choose to not use a vault and assign your credentials to the variables of this playbook directly. | ||
However, you should always strongly consider to use a vault for security reasons. | ||
### Configuration | ||
|
||
## Port Forwarding | ||
Depending on a machine's role (i.e. type of participant), a different property has to be used in the respective Ansible inventory. | ||
|
||
> **_NOTE:_** Instead of forwarding the relevant ports from your router to the NAS, you should strongly consider to use a VPN to avoid any unencrypted file transfer in the internet. | ||
Use the `openvpn.server` property in the inventory of the machine that will act as the server and use the `openvpn.client` property for all remaining machines. | ||
|
||
The NAS runs different services that operate on various ports. | ||
When operating the NAS behind a router (i.e. firewall), these ports have to be accessible. | ||
### Setup instructions | ||
|
||
In case you insist on using port forwarding, the following ports are used by the respective services: | ||
On the CA machine, initialize the CA, generating key and certificate: | ||
|
||
Service | TCP | UDP | ||
--------- |----------|--------- | ||
Samba | 139, 445 | 137, 138 | ||
OpenSSH | 22 | | ||
Nextcloud | 80, 443 | | ||
make openvpn-ca-init | ||
|
||
These are the default ports. Strongly-consider to expose the services at non-default ports to prevent the load and risk that is caused by bots. | ||
An execption are the ports 80 and 443, as they are required for the automatic certificate renewal. | ||
On the Ansible host and for each participant, install OpenVPN, create a certificate request and retrieve it: | ||
|
||
make ANSIBLE_INVENTORY=<inventory> openvpn-initialize | ||
|
||
On the Ansible host, pack the retrieved requests: | ||
|
||
make openvpn-pack-requests | ||
|
||
>If you use a dedicated offline machine as CA: | ||
>This will create a ZIP file including all retrieved requests at `./files-ca-transit/requests.zip`. | ||
>Copy this archive file to the same relative path on the CA machine now. | ||
On the CA machine, import and sign all requests: | ||
|
||
make openvpn-ca-sign-requests | ||
|
||
>If you use a dedicated offline machine as CA: | ||
>This will create a ZIP file including all certificates at `./files-ca-transit/certificates.zip`. | ||
>Copy this archive file to the same relative path on the Ansible host now. | ||
Extract the certificate archive | ||
|
||
make openvpn-extract-certificates | ||
|
||
and distribute the certificates to each OpenVPN participant via | ||
|
||
make ANSIBLE_INVENTORY=<inventory> openvpn-configure | ||
|
||
This will also start the OpenVPN service on each machine and should leave you with a working OpenVPN setup (wait a few minutes). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
$ANSIBLE_VAULT;1.1;AES256 | ||
39616637313631303837646632383036656463666438666165313738383238396166633535616337 | ||
6633393433633435343263623432333532373433323931610a313038383934363832353430646666 | ||
61353338623563666530333061386233396261386536326437646461383862623638393166346333 | ||
6130303130323661340a326363656433316638643264646238643863346130383862633362306134 | ||
32343231316339313035346366623137666434333939323138646335343430663731303732343537 | ||
62333864303966303935363036383434663062646330353164393766373637633135353862363737 | ||
34633262666538633634663064333331306239346234346365303931343832316461343533313230 | ||
65353135376561623264353738396639353033643134666166356430383234356535353130373434 | ||
35346265333438363362396132313566306337323738383966376464313865353737313536646634 | ||
31623537653336383638363932636131393733313034353533323038336363383235363663306666 | ||
31356537656235616266306632626331373736663661326131666165666366303265613836303464 | ||
38316264633732373837656365663138356230306633613362386233646436333131333130343636 | ||
65383539333863626635626439323261353664633135363133376436656433633666643936313436 | ||
30626565653537336631346465653030393933333466663166633734396331366466336463626533 | ||
36646466316161386435353163366433336330386362663638336534376430366138643734626565 | ||
38626531306365356461383562323738343534636533613238633230663336386464343763643466 | ||
62306239383735303631656161643931653634323235393436666330663162316362343135623736 | ||
39623236373866633734303761326365356630366362336638306362313536613165613261386636 | ||
64386537666637366532366430383239343334306531383437383635326533613466396463353338 | ||
65313237343361616530626231326165653835633037343036613333613338633331356537336437 | ||
31316364373233356337326638643136386430363831613138666136626231346664326565383164 | ||
38643938396664623866343866306434616333666631343333633136323439366261336263366365 | ||
30363331336531366132623662376531366432653232646661656563363762613934623064366238 | ||
39333236626238356134383236613663663961653631383866633032653364653761633035303137 | ||
64393930653839363837363437313837653038303130616637616166333536396635 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOzsiAPArvlwUlxyxkrHyUaggQgs8hgXr0EtuPfBvKKv sebschlicht@bastis-ideapad-5i |
Oops, something went wrong.