Table of Contents

Работа zabbix-прокси за NAT

Вводная

Есть головной офис HQ1 в котором стоит zabbix-сервер.
Всё сетевое оборудование HQ1 под нашим управлением, есть внешний ip адрес 80.x.x.201

Есть офис POP1 с мобильным интернетом или интернетом который даёт владелец здания.
Постоянного внешнего адреса нет, по Source NAT в интернет офис выходит с адресом их сети 178.x.x.0/24.
Внутрь POP1 сделать Destination NAT нельзя.

И есть машина подскока jb1 c постоянным внешним адресом 51.144.x.142.
ВM jb1 работает у одного из провайдера облаков или VCP .

L3-схема.

Необходимо в офисе POP1 разместить zabbix-прокси (mon1), который будет подключаться к zabbix-серверу в HQ1.
Так же надо обеспечить ssh доступ к mon1, что бы иметь возможность поправить какие-нибудь настройки.

Zabbix-прокси может работать на чём угодно - RaspberryPi, ВМ, железный сервер или машина.
В данном случае mon1 это ВМ с Centos8.

Схема решения:


Обратный ssh-туннель

Что бы с jb1 получить доступ к машине mon1, необходимо с построить обратный ssh-туннель c до jb1.
В общем случаем, это делается следующей командой:

[root@mon1 ~]# ssh -f -N -R 22002:127.0.0.1:22 root@jumpbox.sitename.com

В результате, на jb1 будет слушаться tcp порт 22002.

[root@jb1 ~]# netstat -nap | grep 22002
tcp        0      0 127.0.0.1:22002         0.0.0.0:*               LISTEN      

Обращения на порт 22002 будут перенаправлены на машину mon1 на порт 22.
Что бы подключиться к mon1 на jb1 нужно будет дать команду:

[root@jb1 ~]# ssh -p 22002  root@127.0.0.1

Динамический ssh-туннель

Динамический ssh-туннель это фактически локальный SOCKS-сервер, запросы к которому будут через ssh доставлены к удалённой машине.

В общем случаем, это туннель поднимается следующей командой:

[root@mon1 ~]# ssh -f -N -D 127.0.0.1:31010 root@jumpbox.sitename.com

На mon1 начнет слушаться порт tcp/31010.

[root@mon1 ~]# netstat -nap | grep 3101
tcp        0      0 127.0.0.1:31010         0.0.0.0:*               LISTEN      22216/ssh     

SOCKS обращения на этот порт будут перенаправлены по ssh на удаленный сервер jumpbox.sitename.com и дальше трафик пойдет как будто от jumpbox.

Дальше рассмотрим как авторизовываться не под пользователем root и как автоматически поднимать и поддерживать ssh-туннель.


Настройка и использование autossh

Для автоматического старта и поддержания ssh-туннеля есть программа autossh.

Установка и тест autossh

[root@mon1 ~]# yum install autossh

Тестово запускаем autossh на mon1.

[root@mon1 ~]# autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -N -R 22002:127.0.0.1:22 root@jb1.sitename.com" 
Password:

Картина со стороны jb1.

[root@jb1 ~]# netstat -nap | less
...
tcp        0      0 127.0.0.1:22002         0.0.0.0:*               LISTEN      13988/sshd: root    
...

Если со стороны jb1 сбросить ssh-сессию ([root@jb1 ~]# kill 13988), то со стороны mon1 autossh попробует ещё раз поднять ssh-туннель.

Настройки со стороны ssh-сервера

Заводим пользователя, под которым дальше будем логиниться на jb1 и строить туннели.
В данном случае это пользователь “autossh”.

[root@jb1 .ssh]$ useradd autossh
[root@jb1 .ssh]$ cat /etc/passwd | grep autossh
autossh:x:1013:1013::/home/autossh:/bin/bash

Правим /etc/ssh/sshd_config, запрещаем пользователю autossh логиниться по паролю и разрешаем форвардинг.

[root@jb1 .ssh]$ cat /etc/ssh/sshd_config
...
Match User autossh
   AllowTcpForwarding yes
   PasswordAuthentication no
   AllowAgentForwarding no
   ForceCommand echo 'This account can only be used for one secret thing'
...

Дальше надо как-то обменяться ssh-ключами.
Способ обмена ключами зависит от конкретной ситуации.
Обычно mon1 настраивается заранее и доступ к нему есть.
Мне удобнее создать все ключи на jb1 и передать их на mon1.

На jb1 генерим новую пару ключей, делаем от пользователя autossh.

[root@jb1 .ssh]$ su - autossh
[autossh@jb1 ~]$ cd .ssh/
[autossh@jb1 .ssh]$ ssh-keygen -t rsa -b 8192 -f ~/.ssh/autossh_to_jb1_from_mon1_id_rsa

Добавляем свежий публичный ключ в authorized_keys.

[autossh@jb1 .ssh]$ cp authorized_keys authorized_keys.bup \
&& echo "#key_for_mon1" >> authorized_keys \
&& cat autossh_to_jb1_from_mon1_id_rsa.pub >> authorized_keys \
&& echo "#/key_for_mon1" >> authorized_keys

Отправляем приватную часть ключа на mon1.
На mon1 autossh будем запускать от root, копируем ключ в папку рута.

[autossh@jb1 .ssh]$ scp autossh_to_jb1_from_mon1_id_rsa root@z.z.z.z:/root/.ssh

Со стороны jb1 все сделали.

Настройка со стороны ssh-клиента

Системный пользователь autossh создался при установке autossh, с ним делать ничего не надо.

[root@mon1 ~]# cat /etc/passwd | grep auto
autossh:x:990:986:autossh service account:/etc/autossh:/usr/sbin/nologin

Отключаем проверку ssh ключей при подключении к jb1.
Идем на это, что бы при смене ssh ключа на jb1, обратные ssh-туннели продолжали строиться.

[root@mon1 .ssh]# cat ~/.ssh/config 
Host jumpbox.sitename.com
   StrictHostKeyChecking no
   UserKnownHostsFile=/dev/null
   ServerAliveInterval=30
   ServerAliveCountMax=3

Пишем юнит-файлы для запуска туннелей через systemd.

Обратный ssh-туннель.

[root@mon1 ~]# cat /etc/systemd/system/autossh-r-22-jb1.service 
[Unit]
Description=AutoSSH reverse ssh tunnel to jb1
After=network.target

[Service]
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" \
-N -R 22002:127.0.0.1:22 -i /root/.ssh/autossh_to_jb1_from_mon1_id_rsa autossh@jumpbox.sitename.com

[Install]
WantedBy=multi-user.target

Динамический ssh-туннель.

[root@mon1 ~]# cat /etc/systemd/system/autossh-d-31010-jb1.service
[Unit]
Description=AutoSSH dynamic tunnel to jb1
After=network.target

[Service]
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 0 -N -D 127.0.0.1:31010 -i /root/.ssh/autossh_to_jb1_from_mon1_id_rsa autossh@jb1.sitename.com

[Install]
WantedBy=multi-user.target

Применяем изменения, запускаем и включаем автозагрузку.

[root@mon1 ~]# systemctl daemon-reload
[root@mon1 ~]# systemctl start autossh-d-31010-jb1 autossh-r-22-jb1
[root@mon1 ~]# systemctl enable autossh-d-31010-jb1 autossh-r-22-jb1

С autossh все, перегружаем mon1 и проверяем, что все туннели поднялись.


Работа с redsocks

Есть не активно поддерживающийся, но работающий проект redsocks, который позводяет через iptables завернуть трафик приложения в SOCKS-сервер.
Фактически redsocks это прослойка между SOCKS-сервером и приложением не поддерживающий работу через SOCKS.

Будем использовать redsocks, что бы на mon1 завернуть трафик от zabbix-прокси к zabbix-серверу в динамический ssh-туннель.
В результате до zabbix-сервера трафик zabbix-прокси будет доходить от внешнего ip адреса jb1.

Установка redsocks

Ставим нужные пакеты.

[root@mon1 ~]# yum install git gcc libevent libevent-devel make
[root@mon1 ~]# yum group install "Development Tools"

Клонируем репозиторий.

[root@mon1 ~]# mkdir src
[root@mon1 ~]# cd src/
[root@mon1 src]# git clone https://github.com/darkk/redsocks.git
Cloning into 'redsocks'...
remote: Enumerating objects: 1043, done.
remote: Total 1043 (delta 0), reused 0 (delta 0), pack-reused 1043
Receiving objects: 100% (1043/1043), 776.60 KiB | 1.90 MiB/s, done.
Resolving deltas: 100% (683/683), done.

Компилируется одной командой make.

[root@mon1 src]# cd redsocks/
[root@mon1 redsocks]# make
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o parser.o parser.c
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o main.o main.c
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o redsocks.o redsocks.c
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o log.o log.c
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o http-connect.o http-connect.c
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o socks4.o socks4.c
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o socks5.o socks5.c
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o http-relay.o http-relay.c
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o base.o base.c
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o base64.o base64.c
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o md5.o md5.c
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o http-auth.o http-auth.c
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o utils.o utils.c
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o redudp.o redudp.c
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o dnstc.o dnstc.c
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o dnsu2t.o dnsu2t.c
dnsu2t.c:50:22: warning: ‘dnsq_soa_root’ defined but not used [-Wunused-const-variable=]
 static const uint8_t dnsq_soa_root[] = {
                      ^~~~~~~~~~~~~
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall   -c -o gen/version.o gen/version.c
cc -g -O2 -std=c99 -D_XOPEN_SOURCE=600 -D_DEFAULT_SOURCE -D_GNU_SOURCE -Wall -o redsocks parser.o main.o redsocks.o log.o http-connect.o socks4.o socks5.o http-relay.o base.o base64.o md5.o http-auth.o utils.o redudp.o dnstc.o dnsu2t.o gen/version.o  -levent_core

В результате в директории появится бинарник redsocks, который надо будет скопировать, например, в “/bin”.

[root@mon1 redsocks]# ll 
...
-rwxr-xr-x 1 root root 514416 ноя  1 17:37 redsocks
...
[root@mon1 redsocks]# mv redsocks /bin/

Настройка redsocks

Создаем конфигурационный файл в директории “/etc/redsocks”.
Правим порты и пока выключаем режим демона.

[root@mon1 redsocks]# cat /etc/redsocks/redsocks.conf
base {
	// debug: connection progress
	log_debug = off;

	// info: start and end of client session
	log_info = on;

	/* possible `log' values are:
	 *   stderr
	 *   "file:/path/to/file"
	 *   syslog:FACILITY  facility is any of "daemon", "local0"..."local7"
	 */
	log = stderr;
	// log = "file:/path/to/file";
	// log = "syslog:local7";

	// detach from console
	daemon = off;

	/* possible `redirector' values are:
	 *   iptables   - for Linux
	 *   ipf        - for FreeBSD
	 *   pf         - for OpenBSD
	 *   generic    - some generic redirector that MAY work
	 */
	redirector = iptables;
}

redsocks {
	/* `local_ip' defaults to 127.0.0.1 for security reasons,
	 * use 0.0.0.0 if you want to listen on every interface.
	 * `local_*' are used as port to redirect to.
	 */
	local_ip = 127.0.0.1;
	local_port = 31000;

	// `ip' and `port' are IP and tcp-port of proxy-server
	// You can also use hostname instead of IP, only one (random)
	// address of multihomed host will be used.
	ip = 127.0.0.1;
	port = 31010;

	// known types: socks4, socks5, http-connect, http-relay
	type = socks5;

	// login = "foobar";
	// password = "baz";
}

Локальные обращения к 127.0.0.1:31000 будут перенаправляться на 127.0.0.1:31010 в виде socks5 “запросов”.
А на порту 31010 у нас висит динамический ssh-туннель до jb1.

Тестово запускаем redsocks

Для теста будем заворачивать трафик к ipinfo.io и смотреть какой внешний адрес показывает.

ВМ mon1 в интернет без redsocks выходит с адресом 178.x.x.7.

[root@mon1 system]# curl ipinfo.io
{
  "ip": "178.x.x.7",
  "city": "Moscow",
  "region": "Moscow",
  "country": "RU",
  "loc": "55.7520,37.6150",
  "org": ,
  "postal": "117246",
  "timezone": "Europe/Moscow",
  "readme": "https://ipinfo.io/missingauth"
}

Имя ipinfo.io резолвится в 4 ip адреса.

[root@mon1 system]# host ipinfo.io
ipinfo.io has address 216.239.38.21
ipinfo.io has address 216.239.34.21
ipinfo.io has address 216.239.36.21
ipinfo.io has address 216.239.32.21
...

Ловим трафик и завоорачиваем на redsock порт 31000.

[root@mon1 system]# cat /etc/sysconfig/iptables
###############################################################################
*nat
:PREROUTING ACCEPT [39:1750]
:INPUT ACCEPT [1:60]
:POSTROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A OUTPUT -p tcp -d 216.239.32.21/32 -j REDIRECT --to-ports 31000
-A OUTPUT -p tcp -d 216.239.34.21/32 -j REDIRECT --to-ports 31000
-A OUTPUT -p tcp -d 216.239.36.21/32 -j REDIRECT --to-ports 31000
-A OUTPUT -p tcp -d 216.239.38.21/32 -j REDIRECT --to-ports 31000
COMMIT
################################################################################
*filter
:INPUT ACCEPT [0:0]
...

Руками запускаем redsocks.

[root@mon1 system]# /bin/redsocks -c /etc/redsocks/redsocks.conf
1604432720.612731 notice main.c:167 main(...) redsocks started, conn_max=128

Проверяем адрес, mon1 выходит через ВМ jb1.

[root@mon1 ~]# curl ipinfo.io
{
  "ip": "51.144.x.142",
  "city": "Amsterdam",
  "region": "North Holland",
  "country": "NL",
  "loc": "52.3618,4.9280",
  "org": "AS8075 Microsoft Corporation",
  "postal": "1093",
  "timezone": "Europe/Amsterdam",
  "readme": "https://ipinfo.io/missingauth"
}

Все работает.

Лог redsocks.

...
1604432723.304937 info redsocks.c:1243 redsocks_accept_client(...) [10.170.1.17:54474->216.239.34.21:80]: accepted
1604432723.551905 info redsocks.c:671 redsocks_drop_client(...) [10.170.1.17:54474->216.239.34.21:80]: connection closed
...

Автозапуск redsocks

В конфиге /etc/redsocks/redsocks.conf ставим “daemon = on”.
Пишем юнит-файл.

[root@mon1 system]# cat /etc/systemd/system/redsocks.service
[Unit]
Description=Transparent redirector of any TCP connection to proxy using your firewall
StartLimitBurst=5
StartLimitIntervalSec=10

[Service]
Type=forking
PIDFile=/run/redsocks.pid
EnvironmentFile=/etc/redsocks/redsocks.conf
ExecStartPre=/bin/redsocks -t -c /etc/redsocks/redsocks.conf
ExecStart=/bin/redsocks -c /etc/redsocks/redsocks.conf -p /run/redsocks.pid
ExecStopPost=/bin/rm /run/redsocks.pid
Restart=always
RestartSec=1

[Install]
WantedBy=multi-user.target

Применяем изменения, запускаем и включаем автозагрузку.

[root@mon1 ~]# systemctl daemon-reload
[root@mon1 ~]# systemctl start redsocks
[root@mon1 ~]# systemctl enable redsocks

Все, redsocks настроен.


Установка и настройка zabbix-proxy

Ставим репозиторий.

[root@mon1 ~]# rpm -i https://repo.zabbix.com/zabbix/4.0/rhel/8/x86_64/zabbix-release-4.0-2.el8.noarch.rpm
warning: /var/tmp/rpm-tmp.zvV0js: Header V4 RSA/SHA512 Signature, key ID a14fe591: NOKEY

Ставим zabbix-прокси с поддержкой sqlite.

yum install zabbix-proxy-sqlite3

Создаем базу и меняем владельца.

[root@mon1 ~]# mkdir /var/lib/sqlite-zbx
[root@mon1 ~]# gunzip /usr/share/doc/zabbix-proxy-sqlite3/schema.sql.gz 
[root@mon1 ~]# sqlite3 /var/lib/sqlite-zbx/zabbix_proxy.db < /usr/share/doc/zabbix-proxy-sqlite3/schema.sql 
[root@mon1 ~]# chown -R zabbix:zabbix /var/lib/sqlite-zbx/

Правим конфиг zabbix-прокси.

...
Server=80.x.x.201
...
Hostname=proxy30-mon1
...
DBName=/var/lib/sqlite-zbx/zabbix_proxy.db
...
TLSConnect=psk
...
TLSAccept=psk
...
TLSPSKIdentity=PSK_OighJuber0
...
TLSPSKFile=/etc/zabbix/zbxpsk
...

Создаем файл с psk и правим права.

[root@mon1 zabbix]# openssl rand -hex 36 > /etc/zabbix/zbxpsk
[root@mon1 zabbix]# cat /etc/zabbix/zbxpsk 
f856c36f2bee726156e62604a15b49a9abc00d6b9f9ee8e5e02d30fec2a90d5b9bc19f98

[root@mon1 zabbix]# chown zabbix:zabbix zbxpsk 
[root@mon1 zabbix]# chmod 400 zbxpsk 

Правим iptables, ловим трафик в zabbix-серверу.

[root@mon1 zabbix]# cat /etc/sysconfig/iptables
###############################################################################
*nat
:PREROUTING ACCEPT [39:1750]
:INPUT ACCEPT [1:60]
:POSTROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
#CATCH_TRAFFIC_TO_ZABBIX_SERVER
-A OUTPUT -p tcp -d 80.x.x.201/32 --dport 10051 -j REDIRECT --to-ports 31000
COMMIT
################################################################################
*filter
:INPUT ACCEPT [0:0]
...

Запускаем zabbix-прокси.

[root@mon1 zabbix]# systemctl start zabbix-proxy
[root@mon1 zabbix]# systemctl enable zabbix-proxy
Created symlink /etc/systemd/system/multi-user.target.wants/zabbix-proxy.service → /usr/lib/systemd/system/zabbix-proxy.service.

Добавляем свеженастроенный проски на zabbix-сервер.

Раз.

Два.

Три.

Всё, настройка закончена.
Еще раз перегружаем mon1 и проверяем, что все тунели и сервисы поднялись.


Полезные ссылки