Выставляем один из вебсервисов без авторизации, без второй публикации

Иногда нужно отключить авторизацию на вебсервере для одного из вебсервисов (сделать его общедоступным), или в принципе выставить один вебсервис "наружу", оставив остальные доступными только из локальной сети.

В принципе, задачу можно решить, создав руками вторую публикацию с нужными сервисами, но тогда приходится заморачиваться с настройками доступа для этой публикации.

Для себя я решил эту проблему с помощью универсального «прокси», который ставится на любой веб-сервер с установленным php с модулем curl, который перенаправляет входной поток запроса на внутренний сервер, параллельно подменяя имя пользователя и пароль.

Настройка сводится к замене переменных:

$_1c_user = '1c_user';
$_1c_pwd = '1c_password';
$_1c_addr = 'http://webserver/1c/ws/ws1.1cws';

$php_user = 'php_user';
$php_pwd = 'php_password';
$php_realm = 'php_realm';

на нужные значения. Скрипт также подменяет адрес сервиса в wsdl’е, полученном от 1с на свой, так что работа скрипта становится вообще прозрачной.

Естественно, у самого скрипта должен быть доступ к «основной» публикации 1с.

Совсем отключить авторизацию скрипта можно закомментировав строки с 11 по 16.

Файл в архиве, так как инфостарт не принимает файлы php

Сам текст скрипта, если у кого-то нет смартмани, чтобы его скачать:

<?php

$_1c_user = '1c_user';
$_1c_pwd = '1c_password';
$_1c_addr = 'http://webserver/1c/ws/ws1.1cws';

$php_user = 'php_user';
$php_pwd = 'php_password';
$php_realm = 'php_realm';

if (!isset($_SERVER['PHP_AUTH_USER']) || ($_SERVER['PHP_AUTH_USER'] !== $php_user) || ($_SERVER['PHP_AUTH_PW'] !== $php_pwd))
{
header('WWW-Authenticate: Basic realm="'.$php_realm.'"');
header('HTTP/1.0 401 Unauthorized');
exit;
}

if (isset($_GET['wsdl'])) {
$ch = curl_init($_1c_addr.'?wsdl');
} else {
$ch = curl_init($_1c_addr);
$post_data = file_get_contents('php://input');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
}

curl_setopt($ch, CURLOPT_USERPWD, $_1c_user.':'.$_1c_pwd);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$server_output = curl_exec ($ch);
$info = curl_getinfo($ch);
curl_close ($ch);

// подстановка "правильного" для внешнего мира адреса в wsdl
if (isset($_GET['wsdl'])) {
$full_url =
($_SERVER['HTTPS']?'https://':'http://').
$_SERVER['SERVER_NAME'].($_SERVER['HTTP_PORT']?':'.$_SERVER['HTTP_PORT']:'').
$_SERVER['PHP_SELF'];
$server_output = preg_replace('/(:address location=")(.*?)("/>)/', '$1'.$full_url.'$3', $server_output);
}

header('Content-Type: '.$info["content_type"]);
file_put_contents('php://output', $server_output);
?>

14 Comments

  1. Makushimo

    Интересно.

    Не понятно только куда этот скрипт подключать.

    в каком месте всей инфраструктуры его ставить?

    Reply ↓
  2. Fragster

    (1) Makushimo, на тот сервер, на котором вы планируете опубликовать общедоступный сервис. Это может быть тот же сервер, на котором крутится публикация 1с, но не обязательно, главное, чтобы с него был доступ до того сервера, на котором публикация 1с.

    Требования к серверу — php 5 с установленным модулем curl, ОС не важна.

    Reply ↓
  3. Makushimo

    ничего не понял, но спасибо за ответ -))

    Reply ↓
  4. Fragster

    (3) Makushimo, ну, допустим, есть сервер с публикацией 1с и адресом 192.168.0.1 и веб-сервер с сайтом компании http://webserver.ru/ с внутренним адресом в локальной сети 192.168.0.2 и неважно каким внешним.

    На вебсервере сайт лежит в папке /srv/www/site. Тогда мы кладем скрипт в эту папку и меняем в нем $_1c_user = ‘имяпользователя1с’, $_1c_pwd = ‘паротльпользователя1с’, $_1c_addr = ‘http://192.168.0.1/путьдопубликации публикаци/ws/имясервиса.1cws’.

    После этого при доступе извне по адресу http://webserver.ru/soap-proxy.php будет запрос логина и пароля, но не 1сных, а тех, которые прописаны в $php_user и $php_pwd. Как вообще отключить авторизацию — я уже написал в самой публикации.

    WSDL будет по адресу http://webserver.ru/soap-proxy.php?wsdl

    Плюсом такого подхода является то, что на вебсервере, доступном извне не нужно ставить устаревший апач 2.2, запрос авторизации никак не задействует 1с — т.е. не создает доп нагрузки на 1с «в случае чего», а также то, что мы можем одним движением выключить подобный доступ, или перенаправить на другой сервер, не заморачиваясь с «родной» публикацией 1с. Ну и к подобной авторизации что-нибудь типа fail2ban можно прикрутить, например, или еще какие службы, что в случае с 1с проблематично.

    Reply ↓
  5. baton_pk

    У меня всё работает из-коробки через SSH-туннели. Думаю, stunnel тоже может с таким справиться.

    Reply ↓
  6. Fragster

    (5) baton_pk, как ssh-туннели помогают _подменить_ авторизацию 1с?

    Reply ↓
  7. baton_pk

    (6)

    хм. Слона-то я и не заметил. Извиняюсь.

    Reply ↓
  8. planar74

    1. Каком смысл в подмене имени пользователя и пароля?

    2. Чем проброс apache не устраивает?

    Reply ↓
  9. Fragster

    (8) planar74,

    иногда есть требование не светить логин-пароль от 1с

    иногда нужно выставить один из нескольких веб сервисов публично

    иногда на публичном веб сервере есть требования к ПО и 1с там быть не должно (например это дешевый хостинг без возможности установки ПО).

    иногда нужно отсечь «недобросовестных пользователей сети», которые брутфорсят пароли с помощью fail2ban

    да и вообще можно придумать кучу вещей, которые можно сделать, вмешавшись в процесс авторизации, хоть направление запроса на разные базы в зависимости от имени пользователя и пароля.

    и да, что такое «проброс от апач»? modrewrite?

    Reply ↓
  10. planar74

    (9)

    «иногда есть требование не светить логин-пароль от 1с» — то есть, если засветится пароль от прокси — злоумышленник не получит доступа к данным? Получит и еще как.

    «и да, что такое «проброс от апач»? modrewrite?»

    <VirtualHost *:*>

    ServerName hostname.example.com

    ProxyPreserveHost On

    ProxyPass /freews http://192.168.111.2/base/ws/superpuper.1cws

    ProxyPassReverse /freews http://192.168.111.2/base/ws/superpuper.1cws

    RequestHeader set Authorization «Basic dXNlcjpwYXNzd29yZA==»

    </VirtualHost>

    Где dXNlcjpwYXNzd29yZA== user:password в кодировке base-64

    Обращение к сервису http://hostname.example.com/freews?wsdl

    З. Ы. Прикрепил файл с куском конфига

    Reply ↓
  11. Fragster

    (10) planar74, этот способ все запросы перенаправит, или я ошибаюсь? Нужен поддомен, получается.

    Reply ↓
  12. planar74

    (11)

    Все, которые поступят на http://hostname.example.com/freews?wsdl, остальные не тронет.

    Reply ↓
  13. planar74

    (11)

    А если по IP соединяться — можно так

    <VirtualHost 192.168.1.1:*>

    ProxyPreserveHost On

    ProxyPass /freews http://192.168.111.2/base/ws/superpuper.1cws

    ProxyPassReverse /freews http://192.168.111.2/base/ws/superpuper.1cws

    RequestHeader set Authorization «Basic dXNlcjpwYXNzd29yZA==»

    </VirtualHost>

    Reply ↓
  14. Fragster

    Нашлось еще одно применение — если менять $post_data с $server_output, то можно привести «несовместимые» с 1с сервисы в совместимый с 1с вид

    Reply ↓

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *