mirror of
https://github.com/tborychowski/self-hosted-cookbook.git
synced 2025-05-13 10:53:38 +00:00
backup, other
This commit is contained in:
parent
5368aa005a
commit
51627aa442
30
README.md
30
README.md
@ -22,6 +22,11 @@ So, without further ado, here's the current list:
|
||||
- How to use docker-compose
|
||||
- Troubleshooting
|
||||
|
||||
# Other self-hosted sources
|
||||
- [awesome-selfhosted](https://github.com/awesome-selfhosted/awesome-selfhosted)
|
||||
- [r/selfhosted](https://www.reddit.com/r/selfhosted/)
|
||||
- [Homelab OS](https://homelabos.com/docs/#available-software)
|
||||
|
||||
|
||||
# Ad Blockers & local DNS
|
||||
- [AdGuard Home](apps/ad-blockers/adguard.md)
|
||||
@ -32,8 +37,10 @@ So, without further ado, here's the current list:
|
||||
- [MalwareMultiScan](https://github.com/mindcollapse/MalwareMultiScan) 🔗
|
||||
|
||||
# Backup
|
||||
- Duplicati
|
||||
- Elkar Backup
|
||||
- [Duplicati](apps/backup/duplicati.md)
|
||||
- [Elkar Backup](apps/backup/elkar-backup.md)
|
||||
- [websync](https://furier.github.io/websync/) 🔗 - an rsync task manager, where tasks can be added, scheduled and maintained in a sane manner
|
||||
|
||||
|
||||
# Blogging
|
||||
- [AnchorCMS](https://github.com/anchorcms/anchor-cms#installation) 🔗
|
||||
@ -122,10 +129,19 @@ So, without further ado, here's the current list:
|
||||
- [DNS Records checker](https://www.digwebinterface.com/) 🔗
|
||||
- [Domain security checker](https://www.hardenize.com/) 🔗
|
||||
|
||||
# GIT
|
||||
- [Gitea](https://docs.gitea.io/en-us/install-with-docker/) 🔗
|
||||
- [GitLab](https://about.gitlab.com/) 🔗
|
||||
- [Gogs](https://gogs.io/) 🔗
|
||||
- [Phabricator](https://secure.phabricator.com/book/phabricator/article/configuration_guide/) 🔗
|
||||
|
||||
|
||||
# Home Automation
|
||||
- [HomeAssistant](apps/home-automation/home-assistant.md)
|
||||
- [Huginn](https://github.com/huginn/huginn) 🔗
|
||||
- [Node-RED](https://nodered.org/) 🔗
|
||||
- [Beehive](https://github.com/muesli/beehive) 🔗 - flexible event/agent & automation system
|
||||
- [Huginn](https://github.com/huginn/huginn) 🔗 - Create agents that monitor and act on your behalf.
|
||||
- [Node-RED](https://nodered.org/) 🔗 - Low-code programming for event-driven applications
|
||||
- [Kibitzr](https://kibitzr.github.io/) 🔗 - Personal Web Assistant
|
||||
|
||||
|
||||
# Media Managers
|
||||
@ -183,7 +199,11 @@ So, without further ado, here's the current list:
|
||||
- [pushover](https://pushover.net/) 🔗
|
||||
- [unifi event monitor](https://github.com/tborychowski/unifi-event-monitor) 🔗
|
||||
|
||||
|
||||
# Other services
|
||||
- [bitwarden_rs](apps/other/bitwarden.md)
|
||||
- [Cockpit](apps/other/cockpit.md)
|
||||
- [Code server](apps/other/code.md)
|
||||
- [Firefox sync server](apps/other/firefox-sync.md)
|
||||
|
||||
# Photos
|
||||
- [Comparison table](apps/photos/comparison.md)
|
||||
|
36
apps/backup/duplicati.md
Normal file
36
apps/backup/duplicati.md
Normal file
@ -0,0 +1,36 @@
|
||||
# Duplicati
|
||||
|
||||
- no notifications!
|
||||
- ugly as hell
|
||||
- confusing to set-up
|
||||
|
||||
<br>
|
||||
|
||||
- [Github repo](https://github.com/duplicati/duplicati)
|
||||
- [Docker Hub](https://hub.docker.com/r/linuxserver/duplicati)
|
||||
- [Homepage](https://www.duplicati.com/)
|
||||
|
||||
|
||||
## docker-compose.yml
|
||||
```yml
|
||||
---
|
||||
version: '3.3'
|
||||
services:
|
||||
duplicati:
|
||||
image: linuxserver/duplicati
|
||||
container_name: duplicati
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=Europe/Dublin
|
||||
# - CLI_ARGS= #optional
|
||||
ports:
|
||||
- 8200:8200
|
||||
volumes:
|
||||
- ./config:/config
|
||||
|
||||
- /mnt/backups:/backups/backups # backup target
|
||||
- ~:/backups/home:ro # backup source - home
|
||||
- /mnt/docker:/backups/docker:ro # backup source - dockers
|
||||
```
|
55
apps/backup/elkar-backup.md
Normal file
55
apps/backup/elkar-backup.md
Normal file
@ -0,0 +1,55 @@
|
||||
# Elkar Backup
|
||||
|
||||
Open source backup solution based on RSync/RSnapshot.
|
||||
- Decent & clean UI
|
||||
- Notification scripts
|
||||
- Really easy to use
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
- [Github repo](https://github.com/elkarbackup/elkarbackup)
|
||||
- [Docker Hub](https://hub.docker.com/r/elkarbackup/elkarbackup/)
|
||||
- [Docs](https://docs.elkarbackup.org/docs/getting-started.html)
|
||||
- [Homepage](https://www.elkarbackup.org/)
|
||||
|
||||
|
||||
## docker-compose.yml
|
||||
```yml
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
elkarbackup:
|
||||
image: elkarbackup/elkarbackup
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
EB_CRON: "enabled"
|
||||
TZ: "Europe/Dublin"
|
||||
SYMFONY__DATABASE__PASSWORD: "123123123"
|
||||
SYMFONY__MAILER__TRANSPORT: "smtp"
|
||||
SYMFONY__MAILER__HOST: "mail.example.com"
|
||||
SYMFONY__MAILER__USER: "home@example.com"
|
||||
SYMFONY__MAILER__PASSWORD: "123123123123"
|
||||
SYMFONY__MAILER__FROM: "home@example.com"
|
||||
|
||||
volumes:
|
||||
- ./uploads:/app/uploads
|
||||
- ./sshkeys:/app/.ssh
|
||||
- /mnt/backup:/paths/backups # backup target
|
||||
- /mnt/docker:/paths/docker:ro # backup source
|
||||
- ~:/paths/home:ro # backup source
|
||||
ports:
|
||||
- 3070:80
|
||||
|
||||
db:
|
||||
image: mysql:5.7.22
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: "1NXXQ5U8Uw6611Y70MSWvU0Vw"
|
||||
volumes:
|
||||
- ./db:/var/lib/mysql
|
||||
|
||||
client:
|
||||
image: elkarbackup/client
|
||||
restart: unless-stopped
|
||||
```
|
@ -11,6 +11,10 @@
|
||||
- [Full MDI icons](https://cdn.materialdesignicons.com/5.2.45/)
|
||||
- [DubhAd/Home-AssistantConfig](https://github.com/DubhAd/Home-AssistantConfig)
|
||||
- [compatible devices](https://www.hadevices.com/)
|
||||
- [presence monitoring](https://community.home-assistant.io/t/monitor-reliable-multi-user-distributed-bluetooth-occupancy-presence-detection/68505)
|
||||
- [touch panel](https://singlebox.tv/how-to-all-in-one-home-assistant-and-touch-panel/)
|
||||
- [reddit thread guide](https://www.reddit.com/r/homeautomation/comments/ejo7zg/i_built_an_allinone_touch_panel_to_control_my/)
|
||||
|
||||
|
||||
## Integrations
|
||||
- [hacs](https://github.com/hacs/integration)
|
||||
@ -139,3 +143,9 @@ media_player.lgtv:
|
||||
```
|
||||
|
||||
## Tips & Tricks
|
||||
|
||||
### Nest integration
|
||||
- [HA Integration guide](https://www.home-assistant.io/integrations/nest/#device-access-registration)
|
||||
- [Acc auth manual](https://developers.google.com/nest/device-access/authorize)
|
||||
- [nest console](https://console.nest.google.com/device-access/project-list)
|
||||
- [devs console](https://console.developers.google.com/)
|
||||
|
29
apps/other/bitwarden.md
Normal file
29
apps/other/bitwarden.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Bitwarden
|
||||
Bitwarden is a password manager & vault.<br>
|
||||
|
||||
bitwarden_rs is an unofficial Bitwarden compatible server.
|
||||
|
||||
<br>
|
||||
|
||||
- [Official Bitwarden Site](https://bitwarden.com/)
|
||||
- [bitwarden_rs Github repo](https://github.com/dani-garcia/bitwarden_rs)
|
||||
- [bitwarden_rs Docs](https://github.com/dani-garcia/bitwarden_rs/wiki)
|
||||
|
||||
|
||||
## docker-compose.yml
|
||||
```yml
|
||||
---
|
||||
version: '3'
|
||||
services:
|
||||
bitwarden:
|
||||
image: bitwardenrs/server:latest
|
||||
container_name: bitwarden
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:80"
|
||||
volumes:
|
||||
- ./data:/data/
|
||||
environment:
|
||||
- SIGNUPS_ALLOWED=false
|
||||
- ADMIN_TOKEN=123123123123123123123123123123123123123123
|
||||
```
|
20
apps/other/cockpit.md
Normal file
20
apps/other/cockpit.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Cockpit
|
||||
|
||||
|
||||
- [Github repo](https://github.com/cockpit-project/cockpit)
|
||||
- [Docs](https://cockpit-project.org/running.html#ubuntu)
|
||||
|
||||
|
||||
## Installation
|
||||
```sh
|
||||
sudo apt-get install cockpit
|
||||
```
|
||||
|
||||
Then go to `https://<SERVER IP>:9090`
|
||||
|
||||
|
||||
## Tips & Tricks
|
||||
To remove the cockpit's motd (welcome message) automatically added to every ssh login:
|
||||
```sh
|
||||
sudo rm /etc/motd.d/cockpit
|
||||
```
|
37
apps/other/code.md
Normal file
37
apps/other/code.md
Normal file
@ -0,0 +1,37 @@
|
||||
# Code Server
|
||||
|
||||
VSCode in the browser!
|
||||
|
||||
<br>
|
||||
|
||||
- [Github repo](https://github.com/cdr/code-server)
|
||||
- [Docker Hub](https://hub.docker.com/r/linuxserver/code-server)
|
||||
|
||||
|
||||
## docker-compose.yml
|
||||
```yml
|
||||
---
|
||||
version: "2.1"
|
||||
services:
|
||||
code-server:
|
||||
image: linuxserver/code-server
|
||||
container_name: code-server
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 3000:8443
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=Europe/Dublin
|
||||
# - PASSWORD=password #optional
|
||||
# - SUDO_PASSWORD=password #optional
|
||||
- PROXY_DOMAIN=code.example.com #optional
|
||||
|
||||
# To get extensions from M$ store - add these:
|
||||
- SERVICE_URL=https://marketplace.visualstudio.com/_apis/public/gallery
|
||||
- ITEM_URL=https://marketplace.visualstudio.com/items
|
||||
volumes:
|
||||
- ./config:/config
|
||||
# folder that will show up in the Code file tree
|
||||
- /var/www/project1:/config/workspace/project1
|
||||
```
|
57
apps/other/firefox-sync.md
Normal file
57
apps/other/firefox-sync.md
Normal file
@ -0,0 +1,57 @@
|
||||
# Firefox Sync Server
|
||||
|
||||
Run-Your-Own Firefox Sync Server.
|
||||
I couldn't make it fully work with MacOS & iOS.
|
||||
|
||||
<br>
|
||||
|
||||
- [Github repo](https://github.com/mozilla-services/syncserver)
|
||||
- [HowTo](https://mozilla-services.readthedocs.io/en/latest/howtos/run-sync-1.5.html)
|
||||
|
||||
|
||||
## docker-compose.yml
|
||||
```yml
|
||||
---
|
||||
version: '3'
|
||||
services:
|
||||
firefox-sync:
|
||||
image: mozilla/syncserver:latest
|
||||
container_name: firefox-sync
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Europe/Dublin
|
||||
- SYNCSERVER_PUBLIC_URL=http://localhost:5000
|
||||
- SYNCSERVER_SECRET=0123123123123123123123123123123
|
||||
- SYNCSERVER_SQLURI=sqlite:////data/syncserver.db
|
||||
- SYNCSERVER_BATCH_UPLOAD_ENABLED=true
|
||||
- SYNCSERVER_FORCE_WSGI_ENVIRON=false
|
||||
ports:
|
||||
- "3000:5000"
|
||||
volumes:
|
||||
- ./data:/data
|
||||
```
|
||||
|
||||
## Tips & Tricks
|
||||
|
||||
#### Change in Firefox desktop
|
||||
in `about:config`, change: `identity.sync.tokenserver.uri` to `https://firefox.example.com/token/1.0/sync/1.5`<br>
|
||||
|
||||
(default is `https://token.services.mozilla.com/1.0/sync/1.5`)
|
||||
|
||||
#### Change on iOS
|
||||
1. In `Settings` - tap 5 times on the version number to get into **Debug Mode**
|
||||
2. Go to `Advanced Sync Settings` and enter your sync URL: `https://firefox.example.com/token/1.0/sync/1.5`
|
||||
|
||||
#### Generate secret
|
||||
```sh
|
||||
head -c 20 /dev/urandom | sha1sum
|
||||
```
|
||||
|
||||
#### Test
|
||||
`/usr/local/bin/python -m syncstorage.tests.functional.test_storage --use-token-server http://localhost:5000/token/1.0/sync/1.5`
|
||||
|
||||
#### Remove mozilla hosted data
|
||||
```sh
|
||||
pip install PyFxA
|
||||
python ./bin/delete_user_data.py user@example.com
|
||||
```
|
@ -3,17 +3,21 @@ This is one of the best reverse-proxy solutions for self-hosting.
|
||||
Very easy to run & maintain (once you pass the setup).<br>
|
||||
|
||||
Traefik can detect docker services and use docker labels to automatically create routes.
|
||||
However, I prefer to keep my docker-compose files clean and explicitly set routers & services myself, so this solution does that exactly.<br>
|
||||
However, I prefer to keep my docker-compose files clean and explicitly set routers & services myself, so this solution does exactly that.<br>
|
||||
|
||||
Traefik can also be set-up to automatically provide Let's Encrypt certs for your services.
|
||||
However, there are some services that need cert files (AdGuard Home, Mailcow), and because I want to have a single wildcard certificate for my whole domain (and all subdomains) I prefer to generate it manually (i.e. scripts in cron) and just reference it whenever it's required - so this setup reflects that.
|
||||
|
||||
## General overview
|
||||
Traefik has 2 types of config: static (requires restart of the container) and dynamic (refreshes live).
|
||||
Dynamic config can be provided as a folder, where all `yml` files are parsed and configuration from them is applied to the running server.
|
||||
You can create multiple files and split the dynamic config to your preference. I prefer to keep the 2 main layers (routers & services) separate, as it's easy for me to structure the files and it's clear to see what services are defined and the ports that they use. The down-side is that adding/removing a service requires editing 2 files.<br>
|
||||
Another approach would be to use 1 yaml file per service (with route & service definition). It would be clearer from the Filesystem (ls -al) to see what services are configured, but e.g. checking all ports would require viewing all config files.<br>
|
||||
For that reason it's also good to keep a note somewhere with a table of service-port mapping.
|
||||
Traefik has 2 types of config:
|
||||
- static - requires restart of the container
|
||||
- dynamic - refreshes live.
|
||||
|
||||
Dynamic config can be provided as a folder, where all `toml`/`yml` files are parsed and configuration from them is applied to the running server.<br>
|
||||
|
||||
You can create multiple files and split the dynamic config to your preference. I prefer to keep 1 main file (for tls/cert settings and global middlewares), and then add 1 config file per service (with route & service definition).<br>
|
||||
|
||||
It's also good to keep a note with a table of service-port mapping (to quickly see which ports are used by which service).
|
||||
|
||||
<br>
|
||||
|
||||
@ -70,114 +74,111 @@ providers:
|
||||
|
||||
## Dynamic config
|
||||
|
||||
### config/middlewares.yml
|
||||
```yml
|
||||
http:
|
||||
middlewares:
|
||||
authelia:
|
||||
forwardAuth:
|
||||
address: http://<SERVER IP>:9091/api/verify?rd=https://login.example.com/
|
||||
trustForwardHeader: true
|
||||
### config/_main.toml
|
||||
```toml
|
||||
[[tls.certificates]]
|
||||
certFile = "/certs-com/fullchain.cer"
|
||||
keyFile = "/certs-com/example.com.key"
|
||||
stores = [ "default" ]
|
||||
|
||||
redirect-to-https:
|
||||
redirectScheme:
|
||||
scheme: https
|
||||
permanent: true
|
||||
[[tls.certificates]]
|
||||
certFile = "/certs-org/fullchain.cer"
|
||||
keyFile = "/certs-org/example.org.key"
|
||||
stores = [ "default" ]
|
||||
|
||||
security-headers:
|
||||
headers:
|
||||
referrerPolicy: "same-origin"
|
||||
contentTypeNosniff: true
|
||||
frameDeny: false
|
||||
forceSTSHeader: true
|
||||
stsIncludeSubdomains: true
|
||||
stsPreload: true
|
||||
stsSeconds: 15552000
|
||||
[tls.stores.default.defaultCertificate]
|
||||
certFile = "/certs-com/fullchain.cer"
|
||||
keyFile = "/certs-com/example.com.key"
|
||||
|
||||
nextcloud-redirectregex:
|
||||
redirectRegex:
|
||||
permanent: true
|
||||
regex: 'https://(.*)/.well-known/(card|cal)dav'
|
||||
replacement: 'https://${1}/remote.php/dav/'
|
||||
[http.middlewares.redirect-to-https.redirectScheme]
|
||||
scheme = "https"
|
||||
permanent = true
|
||||
|
||||
some-redirect:
|
||||
redirectRegex:
|
||||
regex: "https://subdomain1.example.com/"
|
||||
replacement: "https://subdomain2.example.com?query=123"
|
||||
permanent: true
|
||||
[http.middlewares.security-headers.headers]
|
||||
referrerPolicy = "same-origin"
|
||||
contentTypeNosniff = true
|
||||
frameDeny = false
|
||||
forceSTSHeader = true
|
||||
stsIncludeSubdomains = true
|
||||
stsPreload = true
|
||||
stsSeconds = 15_552_000
|
||||
|
||||
```
|
||||
[[http.services.noop.loadBalancer.servers]]
|
||||
url = "http://192.168.0.1:666" # this is a fake url
|
||||
|
||||
### config/tls.yml
|
||||
```yml
|
||||
tls:
|
||||
certificates:
|
||||
- certFile: /example1-com/fullchain.cer
|
||||
keyFile: /example1-com/example1.com.key
|
||||
stores:
|
||||
- default
|
||||
- certFile: /example2-com/fullchain.cer
|
||||
keyFile: /example2-com/example2.com.key
|
||||
stores:
|
||||
- default
|
||||
|
||||
stores:
|
||||
default:
|
||||
defaultCertificate:
|
||||
certFile: /example1-com/fullchain.cer
|
||||
keyFile: /example1-com/example1.com.key
|
||||
```
|
||||
|
||||
### config/routers.yml
|
||||
```yml
|
||||
http:
|
||||
routers:
|
||||
authelia:
|
||||
rule: "Host(`login.example.com`)"
|
||||
service: authelia
|
||||
tls: {}
|
||||
middlewares:
|
||||
- security-headers
|
||||
|
||||
nextcloud:
|
||||
rule: "Host(`cloud.example.com`)"
|
||||
service: nextcloud
|
||||
tls: {}
|
||||
middlewares:
|
||||
- security-headers
|
||||
- nextcloud-redirectregex
|
||||
|
||||
sonarr:
|
||||
rule: "Host(`sonarr.example.com`)"
|
||||
service: sonarr
|
||||
tls: {}
|
||||
middlewares:
|
||||
- security-headers
|
||||
- authelia
|
||||
[http.routers.http-catchall]
|
||||
rule = "HostRegexp(`{host:(www\\.)?.+}`)"
|
||||
entryPoints = [ "http" ]
|
||||
middlewares = [ "redirect-to-https" ]
|
||||
service = "noop"
|
||||
```
|
||||
|
||||
|
||||
### config/services.yml
|
||||
```yml
|
||||
http:
|
||||
services:
|
||||
### config/service-authelia.toml
|
||||
```toml
|
||||
[http.middlewares.authelia.forwardAuth]
|
||||
address = "http://<SERVER IP>:9091/api/verify?rd=https://login.example.com/"
|
||||
trustForwardHeader = true
|
||||
|
||||
authelia:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://<SERVER IP>:9091"
|
||||
[[http.services.authelia.loadBalancer.servers]]
|
||||
url = "http://<SERVER IP>:9091"
|
||||
|
||||
nextcloud:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://<SERVER IP>:3100"
|
||||
|
||||
sonarr:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://<SERVER IP>:8989/"
|
||||
[http.routers.authelia]
|
||||
rule ="Host(`login.example.com`)"
|
||||
service = "authelia"
|
||||
tls = { }
|
||||
middlewares = [ "security-headers" ]
|
||||
```
|
||||
|
||||
### config/service-nextcloud.toml
|
||||
```toml
|
||||
[http.middlewares.nextcloud-redirectregex.redirectRegex]
|
||||
permanent = true
|
||||
regex = "https://(.*)/.well-known/(card|cal)dav"
|
||||
replacement = "https://${1}/remote.php/dav/"
|
||||
|
||||
[[http.services.nextcloud.loadBalancer.servers]]
|
||||
url = "http://<SERVER IP:3000"
|
||||
|
||||
[http.routers.nextcloud]
|
||||
rule = "Host(`cloud.example.com`)"
|
||||
service = "nextcloud"
|
||||
tls = { }
|
||||
middlewares = [ "security-headers", "nextcloud-redirectregex" ]
|
||||
```
|
||||
|
||||
### config/service-sonarr.toml
|
||||
```toml
|
||||
[[http.services.sonarr.loadBalancer.servers]]
|
||||
url = "http://<SERVER IP>:8989/"
|
||||
|
||||
[http.routers.sonarr]
|
||||
rule = "Host(`sonarr.example.com`)"
|
||||
service = "sonarr"
|
||||
tls = { }
|
||||
middlewares = [ "security-headers", "authelia" ]
|
||||
```
|
||||
|
||||
### config/service-traefik.toml
|
||||
```toml
|
||||
[[http.services.traefik.loadBalancer.servers]]
|
||||
url = "http://<SERVER IP>:3080"
|
||||
|
||||
[http.routers.traefik]
|
||||
rule = "Host(`traefik.example.com`) && PathPrefix(`/dashboard`)"
|
||||
service = "traefik"
|
||||
tls = { }
|
||||
middlewares = [ "security-headers", "authelia" ]
|
||||
|
||||
[http.routers.traefik_api]
|
||||
rule = "Host(`traefik.example.com`)"
|
||||
service = "api@internal"
|
||||
tls = { }
|
||||
middlewares = [ "security-headers", "authelia" ]
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Useful links
|
||||
- [Traefik 2 + Docker — a Simple Step by Step Guide](https://medium.com/@containeroo/traefik-2-0-docker-a-simple-step-by-step-guide-e0be0c17cfa5#37d9)
|
||||
- [Traefik 2 + Docker — an Advanced Guide](https://medium.com/@containeroo/traefik-2-0-docker-an-advanced-guide-d098b9e9be96)
|
||||
|
Loading…
Reference in New Issue
Block a user