REST Api Firebase, Unit (service) systemd и прочее а рамках доработки backend к openvpn server.

В этой статье я рассказал как создать приложение на php, она взята за основу и многие моменты второй раз не освещаются.

А здесь рассказано как запустить безопасный сервер с php от root.
Здесь же я решил доработать backend до нормального состояния и упаковать все в установочный пакет. Хотя в первую очередь, было решено переписать хранение данных с MySQL на Firebase, просто потому что она достаточно удовлетворяет условиям проекта и не требует разворачивать еще и сервис баз данных.

Чтобы не геморроиться потом с запуском, создадим сервис. Сервисы в CentOS 7.xx распределены по трем папкам:
/usr/lib/systemd/system/ – сервисы из установленных пакетов rpm.
/run/systemd/system/ — сервисы, созданные в рантайме.
/etc/systemd/system/ — сервисы, созданные администратором, здесь и создадим наш сервис:

С содержимым:

[Service]
ExecStart=/bin/php -S 127.0.0.1:999 -t /etc/openvpn/scripts/
ExecStop=kill -9 $(pidof php -S)

[Install]
WantedBy=multi-user.target

Психанул и решил запилить всю работу через firebase. Т.к. из коробки php с firebase не работает, а серверной части по сути нужно только получить инфу из хранилища — будем работать через REST API.
Для начала попробовал работу в браузере. Для начала побеспокоимся о безопасности и нарисуем права в firebase:

Пример конфигурации базы данных: test-db.json

что означает запрет на чтение-запись всем пользователям во всей базе, кроме как в каталог с собственным uid.
Для авторизованной работы с firebase для начала получим токен авторизации POST-запросом:

А затем получим данные GET-запросом:

Для всего этого написал три функции, получение токена авторизации:

Получение параметров сервера:

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, «GET»);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
‘Content-Type: application/json’,)
);
$result = curl_exec($ch);
try {
$result_arr = json_decode($result, true);
$variable_this = $result_arr[‘server’][$id_server_this];
if(isset($variable_this[‘dev’])){
$variable_this[‘Id’] = $id_server_this;
}
} catch(Exception $e) {
}
}

Получение конфигурации исходящей почты:

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, «GET»);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
‘Content-Type: application/json’,)
);
$result = curl_exec($ch);
try {
$result_arr = json_decode($result, true);
$variable_this = $result_arr[‘adminmail’];
} catch(Exception $e) {
}
}

Функция получения токена авторизации — промежуточная и используется в двух последних. Те же, в свою очередь, возвращают крайнюю переменную результат работы в том же виде, в котором его возвращал MySQL.
Поменял запросы к базе данных в cert_send_toemail.php (настройки почты и сборка конфигурационного файла для пользователя) и build_server.php (сборка конфигурационного файла сервера), файл с настройками settings.php.
Собственно осталось отредактировать управляющий скрипт core_v1.php и core_web.php для возможности редактирования нового файла settings.php
Добавили строчку:

Скорректрировали «справку»:

И удалили две лишние строки формирования settings.php: db_name, db_host
Для core-web.php все чуть сложнее (т.к. она у нас работает через json), итоговый код:

if(isset($command[‘build’])){
$exec = ‘php /etc/openvpn/scripts/build_’.$command[‘build’].’.php —args ‘;
if(!isset($command[‘server’]) || !isset($command[‘com’])){
helper();
}
if($command[‘build’] == ‘server’){
$exec .= ‘-‘.$command[‘com’] . ‘ ‘ . $command[‘server’];
}
if($command[‘build’] == ‘client’){
if(!isset($command[‘client’])){
helper();
}
if($command[‘com’] != ‘remove’){
if(!isset($command[’email’])){
helper();
}
}
$exec .= ‘-‘.$command[‘com’] . ‘ ‘ . $command[‘server’] . ‘ ‘ . $command[‘client’];
if(isset($command[’email’])){
$exec .= ‘ ‘ . $command[’email’];
}
}
}

if(isset($command[‘send’])){
if($command[‘send’] == ‘toemail’){
if(!isset($command[‘server’]) || !isset($command[’email’]) || !isset($command[‘client’])){
helper();
}
$exec = ‘php /etc/openvpn/scripts/cert_send_’.$command[‘send’].’.php —args ‘.$command[‘server’].’ ‘.$command[‘client’].’ ‘.$command[’email’].»;
}
}

if(isset($exec)){
exec($exec, $callback);
for($i=0;$i<count($callback);$i++){
echo $callback[$i];
if(($i+1)<count($callback)){
echo PHP_EOL;
}
}
echo PHP_EOL;
} else {
if(isset($command[‘settings’])){
settings($command);
} else {
helper();
}
}

function helper(){
echo ‘Доступные аргументы:’ .PHP_EOL;
echo ‘Вызов справки: -help’ .PHP_EOL;
echo ‘Создать/удалить openvpn сервер: build:server com:add(или remove) server:1(численный номер сервера)’ .PHP_EOL;
echo ‘Создать/удалить сертификаты клиента: build:client com:add(или remove) server:1(численный номер сервера, к которому будет привязан клиент) email:admin@sergdudko.tk client:1(численный номер клиентского сертификата)’ .PHP_EOL;
echo ‘Отправить сертификаты клиента на email: send:toemail(отправка на email) server:1(численный номер сервера, к которому будет привязан клиент) email:admin@sergdudko.tk client:1(численный номер клиентского сертификата)’ .PHP_EOL;
echo ‘Для настройки программы, наберите settings: {db_host:hostname(адрес БД MySQL или ip) db_name:name(имя БД) db_user:user(пользователь БД) db_pass:password(пароль пользователя БД) encoding:Europe/Minsk(таймзона)}’ .PHP_EOL;
echo PHP_EOL;
exit;
}

function settings($command){
$settings_file = «/etc/openvpn/scripts/settings.php»;
if(!file_exists($settings_file)){
echo ‘Файл с настройками не найден!’ . PHP_EOL;
exit;
}
$handle = @fopen($settings_file, «r»);
if ($handle) {
$i=0;
while (($buffer = fgets($handle, 4096)) !== false) {
$str[$i] = $buffer;
$i++;
}
if (!feof($handle)) {
echo «Error: unexpected fgets() fail\n»;
}
fclose($handle);
}
$current = »;
$flag = 0;
for($i=0;$i<count($str);$i++){ if(isset($command[‘settings’][‘db_key’]) && (substr($str[$i], 0, 7) == ‘$db_key’)){ $str[$i] = ‘$db_key = \».$command[‘settings’][‘db_key’].’\’; //токен БД’ . PHP_EOL; $flag = 1; } if(isset($command[‘settings’][‘db_user’]) && (substr($str[$i], 0, 8) == ‘$db_user’)){ $str[$i] = ‘$db_user = \».$command[‘settings’][‘db_user’].’\’; //пользователь БД’ . PHP_EOL; $flag = 1; } if(isset($command[‘settings’][‘db_pass’]) && (substr($str[$i], 0, 8) == ‘$db_pass’)){ $str[$i] = ‘$db_pass = \».$command[‘settings’][‘db_pass’].’\’; //пароль’ . PHP_EOL; $flag = 1; } if(isset($command[‘settings’][‘timezone’]) && (substr($str[$i], 0, 25) == ‘date_default_timezone_set’)){ $str[$i] = ‘date_default_timezone_set(\».$command[‘settings’][‘timezone’].’\’);’ . PHP_EOL; $flag = 1; } $current .= $str[$i]; } if($flag == 1) { if(file_put_contents($settings_file, $current)){ echo ‘Настройки изменены!’ . PHP_EOL; } } else { echo ‘Параметры: db_key:ключ API проекта(firebase) db_user:user(пользователь БД) db_pass:password(пароль пользователя БД) encoding:Europe/Minsk(таймзона)’ . PHP_EOL; echo ‘settings: обязательный префикс, лишние параметры можно пропустить’ . PHP_EOL; } exit; } exit; ?>

И соответственно добавили запрос вида:

Собственно серверная часть готова. Хотя нет, еще забыл вынести переменную имени проета firebase. Соответственно добавил в core_v1.php:

В веб-интерфейс core_web.php:

В сам файл настроек строчку:

И в скриптах cert_send_toemail.php и build_server.php добавил в url переменную $db_projectname
Ну и запрос стал вида:

Теперь побеспокоимся о безопасности и добавим авторизацию. Чтобы не плодить логины и пароли — запросим те же данные (пользователь и пароль), что есть в файле настроек settings.php. Соответственно в core_web.php добавим следующую проверку:

И в каждый запрос нам нужно будет добавлять секцию с

Т.е. пример изменения настроек:

Или создания клиентских сертификатов:

Соберем релиз, для чего во-первых нужно изменить наименование скрипта для проброса на внутренний сервер (чтобы оно не совпадало), изменим его на core-web-ctrl.php.
Скопируем сам скрипт в папку с проектом, туда же скопируем файл сервиса (unit). Для удобства решил изменить наименование проекта с dudko-web-panel на dwpanel.
Создадим папку dwpanel-0.1.0, в которую положим все наши файлы:
SendMailSmtpClass.php
build_client.php
build_server.php
cert_send_toemail.php
core_v1.php
settings.php
man.md
core-web-ctrl.php
core_web.php
dwpanel-server.service

Перепишем наш SPEC-файл для сборки, согласно правилам установки и удаления (ну и изменившегося наименования проекта):

%description
This is Web panel for OpenVPN by Siarhei Dudko.

%prep
%setup -q

%build

%install
rm -rf %{buildroot}
install -d %{buildroot}%{_datadir}/%{name}
install SendMailSmtpClass.php %{buildroot}%{_datadir}/%{name}
install build_client.php %{buildroot}%{_datadir}/%{name}
install build_server.php %{buildroot}%{_datadir}/%{name}
install cert_send_toemail.php %{buildroot}%{_datadir}/%{name}
install core_v1.php %{buildroot}%{_datadir}/%{name}
install settings.php %{buildroot}%{_datadir}/%{name}
install man.md %{buildroot}%{_datadir}/%{name}
install core-web-ctrl.php %{buildroot}%{_datadir}/%{name}
install core_web.php %{buildroot}%{_datadir}/%{name}
install dwpanel-server.service %{buildroot}%{_datadir}/%{name}

%post
if ! [ -d /etc/openvpn/ ]; then
mkdir -p /etc/openvpn/
fi
if ! [ -d /etc/openvpn/scripts/ ]; then
mkdir -p /etc/openvpn/scripts/
fi
cp %{_datadir}/%{name}/SendMailSmtpClass.php /etc/openvpn/scripts/SendMailSmtpClass.php
cp %{_datadir}/%{name}/build_client.php /etc/openvpn/scripts/build_client.php
cp %{_datadir}/%{name}/build_server.php /etc/openvpn/scripts/build_server.php
cp %{_datadir}/%{name}/cert_send_toemail.php /etc/openvpn/scripts/cert_send_toemail.php
cp %{_datadir}/%{name}/core_v1.php /usr/bin/dwpanel
cp %{_datadir}/%{name}/settings.php /etc/openvpn/scripts/settings.php
cp %{_datadir}/%{name}/core_web.php /etc/openvpn/scripts/core_web.php
cp %{_datadir}/%{name}/dwpanel-server.service /etc/systemd/system/dwpanel-server.service
rm -rf %{buildroot}%{_datadir}/%{name}/
systemctl start dwpanel-server
systemctl enable dwpanel-server
echo Успешно установлено!
echo Пожалуйста, прочтите спецификацию /usr/share/dwpanel/man.md

%postun
systemctl disable dwpanel-server
systemctl stop dwpanel-server
rm -rf /etc/systemd/system/dwpanel-server.service
rm -rf /etc/openvpn/scripts/
rm -rf /usr/bin/dwpanel
echo Успешно удалено!

%files
%dir %{_datadir}/%{name}
%defattr(0755,root,root)
%{_datadir}/%{name}/SendMailSmtpClass.php
%{_datadir}/%{name}/build_client.php
%{_datadir}/%{name}/build_server.php
%{_datadir}/%{name}/cert_send_toemail.php
%{_datadir}/%{name}/core_v1.php
%{_datadir}/%{name}/settings.php
%{_datadir}/%{name}/core-web-ctrl.php
%{_datadir}/%{name}/core_web.php
%{_datadir}/%{name}/dwpanel-server.service
%defattr(0644,root,root)
%doc %{_datadir}/%{name}/man.md

%clean
rm -rf %{buildroot}

%changelog
* Tue Feb 01 2018 Siarhei Dudko
— Initial build

Соберем пакет:

Пакет в GPG-ключом: dwpanel-0.1.0-1.noarch.rpm