Разворачиваем приватный Firefly III в закрытой сети Wireguard

MLPops / November 10, 2024
12 мин.
Привет, в этой статье разберем запуск такой штуки как Firefly III в закрытой сети.
Firefly III что это такое?
Firefly III — это не просто какой-то там калькулятор. Это полноценный финансовый менеджер. Вы можете записывать свои доходы и расходы, классифицировать их по категориям, а потом смотреть, куда у вас уходит бабло. Это очень удобно, особенно если вы хотите понять, почему в конце месяца у вас на счету остается только пара копеек.
И, внимание, у него есть функция бюджетирования! Вы можете выставить себе лимиты на разные категории расходов и следить за тем, как вы их выполняете. Это как будто вы сами себе бухгалтер, только без скучных отчетов и с хорошей визуализацией. Графики, диаграммы — всё это вам покажут, где вы можете подрезать расходы.
WireGuard
WireGuard — это VPN-протокол, который просто разрывает рынок! Он суперпростой в настройке. Вы знаете, как обычно с VPN — там столько настроек, что можно запутаться. А тут всё максимально просто. Настроил — и забыл. Взял, подключился и пошел, как будто у тебя обычный интернет, только безопасный!
Теперь про скорость. WireGuard работает так быстро, что у вас не будет никаких лагов. Стримите, играйте, смотрите видео — всё летит. Это особенно важно, когда у тебя стрим, и ты не хочешь, чтобы что-то тормозило.
А еще он очень легкий! Вся его кодовая база — это всего лишь несколько тысяч строк. Для сравнения, у старых протоколов, таких как OpenVPN, их в разы больше. Это значит, что WireGuard проще поддерживать и обновлять, и меньше шансов на всякие баги.
Безопасность — это отдельная тема. WireGuard использует современные алгоритмы шифрования, так что ваши данные будут под надежной защитой. Никто не сможет подслушать ваши разговоры или украсть информацию.
Подготавливаем ВМку
Я буквально перерыл интернет в поисках лучшего облачного сервиса и понял, что ничего лучше Cloud.ru просто не существует. У них все самое топовое в этой сфере - и они постоянно совершенствуются и наращивают свои возможности, понимаешь. Так что если хочешь работать с облаками и ИИ - это точно единственный вариант, и совсем не потому что я там работают, нет.
ВМка с конфигом 2 ядра 2 гига, и оплатой за IP тачки, это самое дешевое что есть. Да, ты правильно понял, там могут дать тачку бесплатно, но платить ты будешь только за IP адрес что бы в/из инета ходить 150р/месяц. Тот же TimeWeb берет 150р/месяц за тачку + 150р/месяц за IP.
Документация там на изи, так что не будем разбирать этап создания ВМки и перейдем сразу к docker подготовке.
Ставим Docker и Docker Compose
Docker
В пару шагов поставим docker
-
Откройте терминал.
-
Запустите команду для установки Docker:
curl -fsSL https://get.docker.com | sh
- Проверьте установку Docker:
docker --version
- (Опционально) Добавьте своего пользователя в группу Docker для запуска без sudo:
sudo usermod -aG docker $USER
Перезагрузите систему или выйдите и снова войдите в систему, чтобы изменения вступили в силу.
Если вам не критично, чтобы у вас была самая последняя версия Docker Compose, то давайте сделаем это проще — установим его из репозитория Ubuntu. Всё просто, как дважды два!
Docker Compose
Просто вбиваете в терминал вот эту команду:
sudo apt-get install docker-compose
И всё! Нажимаете Enter, и терминал сам сделает всю работу. Не нужно ничего мудрить — просто ждете, пока всё установится.
Вот и вся магия! Теперь у вас есть Docker Compose, и вы готовы запускать свои контейнеры!
Настраиваем сетку
Создадим сеть
docker network create
--subnet 10.20.25.128/25
--opt com.docker.network.driver.mtu=1420
--opt com.docker.network.bridge.name=eht0
--opt com.docker.network.container_iface_prefix=eth
--gateway 10.20.25.254
wgnet
Давайте разберем эту команду. Эта команда создаёт новую сеть в Docker, и тут много интересных настроек. Давайте по порядку!
docker network create
— это команда, которая говорит Docker: “Эй, давай создадим новую сеть!” Это как создать свой собственный виртуальный мир, где контейнеры могут общаться друг с другом.--subnet 10.20.25.128/25
— здесь мы задаем подсеть для нашей сети. Это значит, что все контейнеры, которые будут подключены к этой сети, получат IP-адреса из этого диапазона./25
означает, что у нас будет 126 доступных адресов.--opt com.docker.network.driver.mtu=1420
— это опция для настройки MTU (Maximum Transmission Unit). Мы указываем максимальный размер пакета, который может передаваться по сети. Это важно, чтобы избежать проблем с фрагментацией пакетов.--opt com.docker.network.bridge.name=eht0
— здесь мы задаем имя для моста, который будет использоваться в сети. Обычно этоeth0
, но мы можем назвать его как угодно. Это просто удобно, чтобы знать, с чем мы работаем.--opt com.docker.network.container_iface_prefix=eth
— эта опция задает префикс для интерфейсов контейнеров. То есть, если у вас несколько контейнеров, их сетевые интерфейсы будут называтьсяeth0
,eth1
и так далее. Удобно, чтобы не путаться.--gateway 10.20.25.254
— это адрес шлюза для нашей сети. Контейнеры будут использовать этот адрес для выхода в другие сети, например, в интернет. Это как ворота в наш виртуальный мир!wgnet
— это имя, которое мы даём нашей сети. Вы можете назвать её как угодно, но лучше, чтобы было понятно, что это за сеть. В нашем случае этоwgnet
, потому что мы, скорее всего, используем её для WireGuard.
Собираем сервисы
Конфиг для wireguard
version: '3.8'
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy
container_name: wg-easy
environment:
- LANG=ru
- WG_HOST=<IP_VM>
- PASSWORD_HASH=<HASH>
- PORT=51821
- WG_PORT=51820
- WG_DEFAULT_ADDRESS=10.20.25.x
- WG_DEFAULT_DNS=1.1.1.1
- WG_MTU=1420
- WG_ALLOWED_IPS=10.20.25.128/25
- WG_PERSISTENT_KEEPALIVE=25
- WG_ENABLE_ONE_TIME_LINKS=true
- UI_ENABLE_SORT_CLIENTS=true
- WG_ENABLE_EXPIRES_TIME=true
volumes:
- ./config/wg-easy:/etc/wireguard
ports:
- "51820:51820/udp"
- "51821:51821/tcp"
networks:
wgnet:
ipv4_address: 10.20.25.130
restart: unless-stopped
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
networks:
wgnet:
external: true
fireflynw:
Итак, у нас тут версия 3.8, и это значит, что мы используем современный подход к конфигурации. Начнем с самого интересного — сервисы. У нас есть сервис под названием wg-easy
. Это, по сути, наше чудо для управления WireGuard, чтобы все было просто и удобно.
- Имя образа:
ghcr.io/wg-easy/wg-easy
— это наш образ. Он уже готовый, так что нам не нужно париться с установкой. Просто берём и запускаем! - Переменные окружения:
LANG=ru
— чтобы всё было на русском, как мы любим!WG_HOST=<IP_VM>
— тут нужно указать IP вашего виртуального сервера. Это важно, чтобы всё работало как надо.PASSWORD_HASH=<HASH>
— здесь будет ваш хэш пароля. Безопасность, ребята, безопасность! Вот тут инструкция по созданиюPASSWORD_HASH
PORT
,WG_PORT
,WG_DEFAULT_ADDRESS
,WG_DEFAULT_DNS
— это всё настройки для WireGuard. Тут мы указываем, какие порты использовать и какие IP-адреса будут по умолчанию.
- Volumes:
./config/wg-easy:/etc/wireguard
— это значит, что все настройки будут храниться в папкеconfig/wg-easy
на вашем хосте. Удобно, чтобы не потерять настройки при перезапуске контейнера! - Порты:
51820:51820/udp
и51821:51821/tcp
— это порты, которые мы открываем. Первый для WireGuard, второй для веб-интерфейса. Так что вы сможете легко управлять своим VPN! - Сети: Мы подключаемся к сети wgnet и задаем статический IP-адрес
10.20.25.130
. Это удобно, чтобы всегда знать, где наш контейнер. - Перезапуск:
restart: unless-stopped
— это значит, что контейнер будет перезапускаться, если что-то пойдет не так, но не будет перезапускаться, если вы его сами остановите. Удобно, чтобы не париться! - Права:
cap_add
— мы добавляем праваNET_ADMIN
иSYS_MODULE
, чтобы наш контейнер мог управлять сетевыми настройками. Это важно для работы WireGuard. - Sysctls:
net.ipv4.ip_forward=1
иnet.ipv4.conf.all.src_valid_mark=1
— это настройки, которые позволяют пересылать пакеты между интерфейсами. Без этого WireGuard не сможет нормально работать.
Ставим Firefly III
Для начала, давайте посмотрим на то, как мы можем запустить Firefly III с помощью Docker. Вот вам пример конфигурации:
firefly-iii:
container_name: firefly-iii
image: fireflyiii/core:latest
restart: always
volumes:
- firefly_iii_upload:/var/www/html/storage/upload
env_file: ./.env
networks:
fireflynw:
depends_on:
- db
db:
container_name: firefly-db
image: mariadb:lts
restart: always
env_file: ./.db.env
networks:
fireflynw:
volumes:
- firefly_iii_db:/var/lib/mysql
cron:
image: alpine
container_name: firefly-cron
restart: always
command: sh -c "echo \"0 3 * * * wget -qO- http://firefly-iii:8080/api/v1/cron/REPLACEME;echo\" | crontab - && crond -f -L /dev/stdout"
networks:
fireflynw:
volumes:
firefly_iii_upload:
firefly_iii_db:
networks:
wgnet:
external: true
fireflynw:
- Firefly III: Это основной контейнер, который будет управлять вашим финансовым приложением. Он всегда будет перезапускаться, если что-то пойдет не так. Мы монтируем папку для загрузок, чтобы все ваши документы были под рукой.
- База данных: Здесь у нас MariaDB, которая хранит все ваши данные. Она тоже будет перезапускаться и подключена к той же сети, что и Firefly III. Важно, чтобы данные не потерялись, поэтому мы используем отдельный том для базы.
- Cron: Это контейнер для запуска задач по расписанию. Вы можете настроить его так, чтобы он выполнял определённые команды в нужное время. Главное — правильно указать STATIC_CRON_TOKEN, чтобы всё работало!
Теперь надо сделать переменные виртуального окружения
Создадим .db_env
MYSQL_RANDOM_ROOT_PASSWORD=yes
MYSQL_USER=firefly
MYSQL_PASSWORD=secret_firefly_password
MYSQL_DATABASE=firefly
Скачаем базовые .env для Firefly III
wget https://raw.githubusercontent.com/firefly-iii/firefly-iii/main/.env.example && mv .env.example .env
И поменяем пару переменных
SITE_URL=https://firefly.<your_domain>
APP_URL=https://firefly.<your_domain>
SITE_OWNER=<EMAIL>
APP_KEY=<RANDOM_GENERATED>
DEFAULT_LANGUAGE=ru_RU
TZ=Europe/Moscow
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=firefly
DB_USERNAME=firefly
DB_PASSWORD=secret_firefly_password
Такс, вроде бы подготовили все.
Подготовка Nginx
Nginx - это, по сути, ваш веб-сервер, который будет раздавать контент, как горячие пирожки! Так что поехали
Вот что у нас есть в конфиге:
nginx:
image: nginx:alpine
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
- /home/user/certbot/certs:/etc/nginx/certs
container_name: nginx
networks:
wgnet:
ipv4_address: 10.20.25.150
fireflynw:
restart: unless-stopped
Разбор по пунктам
image: nginx:alpine
: Мы используем образ Nginx на базе Alpine. Это значит, что он будет лёгким и быстрым, как ветер! Альпийская версия — это всегда хорошо, меньше мусора, больше скорости.volumes
: Тут мы монтируем два тома:- Первый том — это ваш конфиг Nginx. Мы берём
nginx.conf
из вашей локальной папки и помещаем его в контейнер. Это значит, что вы можете легко редактировать настройки сервера, не лезя в контейнер. - Второй том — это сертификаты от Certbot. Мы монтируем папку с сертификатами, чтобы обеспечить безопасное соединение через HTTPS. Безопасность — это важно!
- Первый том — это ваш конфиг Nginx. Мы берём
container_name: nginx
: Просто задаём контейнеру имя. Это удобно, чтобы потом не путаться, когда у вас много контейнеров.networks
: Здесь мы подключаем наш Nginx к двум сетям:wgnet
: Это ваша локальная сеть, где мы задаём статический IP-адрес10.20.25.150
. Это удобно, чтобы другие контейнеры могли легко находить Nginx.fireflynw
: Это ещё одна сеть, к которой подключаемся. Она может использоваться для взаимодействия с другими сервисами, такими как Firefly III.
restart: unless-stopped
: Это значит, что если контейнер упадёт, он автоматически перезапустится, пока вы его не остановите вручную. Отличная фича, чтобы ваш сервер всегда был на связи!
Настройка домена
Что бы не париться с настройкой своего DNS сервера и SSL сертефикатами, самое простое что есть это просто настроить домен с указанием на наш адрес. При создании сабдомена/домена просто укажите в A записи 10.20.25.150
, домен будет вести сразу на наш nginx, а с него мы спроксируем трафик на Firefly и проблем не будет.
SSL сертификаты
Так как наш домен имеет А запись в закрытую сеть, то нам нужно как то получить SSL сертификат. Мы можем его купить, но поскольку мы с тобой еще те JoePeach, то бабки тратить не будем. Попробуем с тобой получить сертификат от Let’s Encrypt. Просто так по домену мы не сможем получить, по сколько сеть закрытая и callback от Let’s Encrypt принят не сможем, так что попробуем второй вариант. Второй вариант это DNS Challenge.
DNS Challenge - это способ проверить, что ты действительно владеешь доменным именем, на который запрашиваешь SSL-сертификат для своего сайта. Сертификаты нужны для безопасности сайта и чтобы люди видели вот такой маленький замочек в адресной строке своего броузера. DNS Challenge - это один из способов доказать, что ты хозяин домена
Как это работает?
- Запрос на сертификат: Вы начинаете процесс получения сертификата (например, через Certbot). Сертификатный центр (CA) хочет убедиться, что вы действительно владеете доменом.
- Создание записи: Вам нужно создать специальную DNS-запись. Обычно это TXT-запись, которая содержит уникальный токен, который вам предоставляет сертификатный центр. Это как пароль, который доказывает, что вы — это вы.
- Проверка: Сертификатный центр запрашивает DNS-записи вашего домена. Он ищет ту самую TXT-запись с токеном. Если он её находит — поздравляю, вы подтвердили право собственности на домен!
- Получение сертификата: После успешной проверки сертификатный центр выдаёт вам SSL-сертификат, и ваш сайт теперь работает по HTTPS. Всё просто, как дважды два!
Зачем это нужно?
DNS Challenge полезен, потому что он позволяет вам получать сертификаты для доменов, которые могут быть недоступны по HTTP. Это особенно актуально для поддоменов или для ситуаций, когда у вас нет доступа к веб-серверу из интернета, прям как у нас.
Как мы можем его сделать? Если домены в TimeWeb то изи, регаем там домен и выполняем скрипт на тачке. Воспользуемся плагином CertBot-DNS-TimeWeb
apt-get update
mkdir /home/user/certbot/certs/
apt-get install python3-pip
pip install certbot-dns-timeweb
certbot certonly --authenticator dns-timeweb \
--dns-timeweb-credentials timeweb-creds.ini \
-d firefly.<domain>
cp -r /etc/letsencrypt/archive/firefly.<domain> /home/user/certbot/certs/
Все, теперь нужно будет каждые 90 дней выполнять это скрипт и перезапускать nginx для обновления SSL сертификата.
Финал
Теперь все готово, глянем на наш итоговый docker-compose
version: '3.8'
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy
container_name: wg-easy
environment:
- LANG=ru
- WG_HOST=<IP_VM>
- PASSWORD_HASH=<HASH>
- PORT=51821
- WG_PORT=51820
- WG_DEFAULT_ADDRESS=10.20.25.x
- WG_DEFAULT_DNS=1.1.1.1
- WG_MTU=1420
- WG_ALLOWED_IPS=10.20.25.128/25
- WG_PERSISTENT_KEEPALIVE=25
- WG_ENABLE_ONE_TIME_LINKS=true
- UI_ENABLE_SORT_CLIENTS=true
- WG_ENABLE_EXPIRES_TIME=true
volumes:
- ./config/wg-easy:/etc/wireguard
ports:
- "51820:51820/udp"
- "51821:51821/tcp"
networks:
wgnet:
ipv4_address: 10.20.25.130
restart: unless-stopped
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
firefly-iii:
container_name: firefly-iii
image: fireflyiii/core:latest
restart: always
volumes:
- firefly_iii_upload:/var/www/html/storage/upload
env_file: ./.env
networks:
fireflynw:
depends_on:
- db
db:
container_name: firefly-db
image: mariadb:lts
restart: always
env_file: ./.db.env
networks:
fireflynw:
volumes:
- firefly_iii_db:/var/lib/mysql
cron:
image: alpine
container_name: firefly-cron
restart: always
command: sh -c "echo \"0 3 * * * wget -qO- http://firefly-iii:8080/api/v1/cron/REPLACEME;echo\" | crontab - && crond -f -L /dev/stdout"
networks:
fireflynw:
nginx:
image: nginx:alpine
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
- /home/user/certbot/certs:/etc/nginx/certs
container_name: nginx
networks:
wgnet:
ipv4_address: 10.20.25.150
fireflynw:
restart: unless-stopped
volumes:
firefly_iii_upload:
firefly_iii_db:
networks:
wgnet:
external: true
fireflynw:
Ну теперь все можно поднять и кайфовать
docker-compose up -d
В наш Firefly III теперь нельзя зайти без подключения по VPN в нашу сетку, ваши копейки в безопастности.
P.S. Спасибо @ddydinin за помощь в настройке Wireguard