Pull to refresh

Comments 19

Добрый день! Надеюсь ip-блокер не только по ip блочит? Какая информация в него передаётся? Блокировку по клиентскому сертификату, по юзер-агенту, куке и пр. возможна? Могу, конечно и код залезть посмотреть.
Я про restinio::ip_blocker::incoming_info_t
Хотя наверное глупый вопрос, вся эта информация недоступна до установления соединения… Но все таки по сертификату можно блочить на этом этапе?

На данный момент вот весь incoming_info_t. Пока там доступен только remote_endpoint.


Если нужна информация из SSL-подключения… То нужно подумать, как ее извлечь и в incoming_info_t передать.


Спасибо за направление, сделаем себе зарубку.

Я думаю, для SSL возможно стоит сделать отдельный инспектор подключений, вызываемый между окончанием handshake и началом парсинга HTML — для проверки валидности клиентского сертификата и возможной аутентификации по нему (а также устаревших версий алгоритмов шифрования, кол-ва одновременных соединений и пр.), чтобы отсечь злонамеренные подключения еще до парсинга хидеров и задействования внешних ресурсов для аутентификации (баз данных всяких и т.д.). При DDos весьма пользительно будет. Если делать аналогично ip-блокеру, то объем работы вроде и не большой получится.

Приглядываюсь к вашему изделию, возможно скоро попробую применить. :)
Пока пользуюсь этим — github.com/eidheim/Simple-Web-Server
Я думаю, для SSL возможно стоит сделать отдельный инспектор подключений

Может быть. Но мне пока больше нравится мысль сделать incoming_info_t шаблоном. Тогда IP-blocker может иметь вид:


class my_ip_blocker {
public:
   template<typename Socket>
   restinio::ip_blocker::inspection_result_t
   inspect(const restinio::ip_blocker::incoming_info_t<Socket> & info) noexcept {
      ...
   }
};

В случае, если RESTinio запускается для HTTP, в качестве Socket будет использоваться restinio::asio_ns::ip::tcp::socket. А для HTTPS — restinio::tls_socket_t. И как раз для случая tls_socket_t в incoming_info_t будет дополнительная информация, касательно SSL-соединения.


Еще, вероятно, имеет смысл в incoming_info_t передавать константную ссылку на сам Socket. Тогда из него пользователь сможет извлечь все, что ему нужно. Даже то, о чем разработчики RESTinio сразу и не подумали.

Важна еще и текущая стадия подключения — блокировка по ip когда прилетел tcp-запрос на подключение, блокировка по ssl — когда завершился хендшейк, ну и блокировка по параметрам уже после разбора хидера…
блокировка по ip когда прилетел tcp-запрос на подключение, блокировка по ssl — когда завершился хендшейк

Тут мне сложно комментировать. Автор основной части кода RESTinio сейчас занят на совсем других задачах и не имеет возможности продолжать плотно работать над RESTinio. А я пока в реализацию поддержки TLS в RESTinio еще не погрузился.


Но пожелание понятно.


ну и блокировка по параметрам уже после разбора хидера…

В RESTinio после разбора хидеров и тела запроса вызывается заданный пользователем request_handler. Соответственно, по текущей идеологии RESTinio получается, что за обработку всего этого дела отвечает request_handler, а не RESTinio.

Немного восстановил картину работы с разными типами соединений. Очевидно, что есть возможность задействовать ip_blocker после принятия соединения, еще до инициирования async_handshake. А после async_handshake, соответственно, некий ssl_inspector. Вероятно, на следующей неделе попробуем этим заняться.


Открыт вопрос о том, нужно ли разделять ip_blocker и ssl_inspector на разные сущности? Есть ли какой-то смысл использовать ssl_inspector без ip_blocker-а? Может быть это должен быть один объект, у которого будут разные методы inspect?


И еще технический вопрос: допустим, у вас есть ссылка на asio::ssl::stream. Сможете ли вы из этого извлечь информацию, о которой выше шла речь (т.е. клиентский сертификат, версии алгоритмов шифрования и пр.)? Или для этих задач еще что-то нужно?

согласно www.boost.org/doc/libs/1_69_0/boost/asio/ssl/stream.hpp

в стриме есть
 /// Get the underlying implementation in the native type.
  /**
   * This function may be used to obtain the underlying implementation of the
   * context. This is intended to allow access to context functionality that is
   * not otherwise provided.
   *
   * @par Example
   * The native_handle() function returns a pointer of type @c SSL* that is
   * suitable for passing to functions such as @c SSL_get_verify_result and
   * @c SSL_get_peer_certificate:
   * @code
   * boost::asio::ssl::stream<asio:ip::tcp::socket> sock(io_context, ctx);
   *
   * // ... establish connection and perform handshake ...
   *
   * if (X509* cert = SSL_get_peer_certificate(sock.native_handle()))
   * {
   *   if (SSL_get_verify_result(sock.native_handle()) == X509_V_OK)
   *   {
   *     // ...
   *   }
   * }
   * @endcode
   */
  native_handle_type native_handle()
  {
    return core_.engine_.native_handle();
  }


Так что вроде хватит информации.

SSL-инспектор без ip-блокера (и наоборот) — все возможно. Задачи разные бывают.
Так что вроде хватит информации.

Отлично, спасибо!

Доброго дня!


По горячим следам сделать tls_inspector не получилось. Время нашлось только сейчас. Первая версия готова, но у меня самого есть к ней претензии. Будет здорово, если вы сможете высказать свое мнение по этому поводу (на GitHub-е или здесь не суть важно).

Добрый день!

Первый момент, который мне не нравится, — это возвращение inspection_result_t из метода tls_inspector

Кажется логичным, что tls_inspector может проверить некоторые учетные данные клиента или параметры TLS и разрешить или запретить соединение. Но, может быть, set_verify_callback от Asio больше подходит для этой цели?


Я думаю, что set_verify_callback — это всего лишь часть внутреннего механизма реализации вашего сервера, и вы вправе предоставить пользователю свой расширенный механизм инспектирования состояния подключения. В конце концов, сегодня внутри ASIO, а завтра собственный более шустрый механизм асинхронной работы. Ну и можно дать пользователю возможность добавить свой коллбэк при обработке set_verify_callback, если пользователю захочется большей гибкости, не думаю что это сложно.
зачем нам отдельная сущность для tls_inspector? Что если state_listener достаточно, и все, что нам нужно, — это способ доступа к TLS-сокету из метода state_listener state_changed?


Тут я согласен, зачем плодить сущности… state_listener'а с указанием текущего этапа подключения, возможностью заблокировать соединение и доступом к сокету вполне достаточно. Завтра какой-нить новый протокол типа QUIC будет встроен, к нему quic_lnspector рисовать? Лишее, я считаю.

Может быть, должен быть способ хранить некоторые предоставленные пользователем данные в соединении с возможностью извлечения их через Requiest_handle_t


Пользователь может хранить свои данные сам в своих отдельных структурах данных и обращаться к ним по какому-нить идентификатору типа host:port, считаю не стоит это добавлять
в реализацию web-сервера, поскольку напрямую к его работе это не относится. У разных пользователей разные запросы, на всех не угодишь.
Я думаю, что set_verify_callback — это всего лишь часть внутреннего механизма реализации вашего сервера, и вы вправе предоставить пользователю свой расширенный механизм инспектирования состояния подключения.

Ну как бы не совсем. Параметры TLS пользователь должен выставить на уровне Asio. Вот, например:


asio_ns::ssl::context tls_context{ asio_ns::ssl::context::sslv23 };
tls_context.set_options(
    asio_ns::ssl::context::default_workarounds
    | asio_ns::ssl::context::no_sslv2
    | asio_ns::ssl::context::single_dh_use );

tls_context.use_certificate_chain_file( certs_dir + "/server.cer" );
tls_context.use_private_key_file(
    certs_dir + "/server.key",
    asio_ns::ssl::context::pem );
tls_context.set_verify_mode(
    asio_ns::ssl::verify_peer
    | asio_ns::ssl::verify_fail_if_no_peer_cert );
tls_context.load_verify_file( certs_dir + "/ca.cer" );
tls_context.use_tmp_dh_file( certs_dir + "/dh2048.pem" );

Особенно важен здесь вызов set_verify_mode. Ну и, поскольку, именно пользователь определяет как проверять клиента, то set_verify_callback для этого может подходить больше, чем RESTinio-вский tls_inspector.


Тогда как tls_inspector может быть полезен для того, чтобы забрать и сертификата клиента (или параметров tls-соединения) какую-то полезную информацию.


В конце концов, сегодня внутри ASIO, а завтра собственный более шустрый механизм асинхронной работы.

Боюсь, что в наши планы в принципе не входит замена Asio в RESTinio на что-нибудь другое. По крайне мере за свой счет мы это вряд ли будем делать :)


Пользователь может хранить свои данные сам в своих отдельных структурах данных

Тут есть два момента:


  1. Как определять, когда данные больше не нужны? Получается, что пользователь будет вынужден завязываться на state_listener-ы. В каких-то случаях это будет неудобно.
  2. Как писать эти самые контейнеры так, чтобы они подходили и для однопоточного режима работы RESTinio (когда защита контейнера не нужна), и для многопоточного (когда защита необходима)? В случае, если сервис по хранению connection-специфичной информации предоставляет RESTinio, это все будет разруливаться средствами RESTinio автоматически.
Параметры TLS пользователь должен выставить на уровне Asio. Вот, например:

Разве параметры TLS не задаются однократно при создании экзмемпляра сервера перед стартом? Зачем выносить их в коллбэк?

Как определять, когда данные больше не нужны? Получается, что пользователь будет вынужден завязываться на state_listener-ы. В каких-то случаях это будет неудобно.


В каких-то случаях будет неудобно удалять эти структуры, особенно если у пользователя в этом случае сложная логика, не сводящаяся только к деструкции привязанных данных. Может, он коннект к какой БД или файлу должен зарубить, но не хочет выносить это в деструктор удаляемых данных. Должна быть предоставлена процедура-коллбэк для действий перед и после закрытия соединения.
Разве параметры TLS не задаются однократно при создании экзмемпляра сервера перед стартом?

Параметры TLS задаются однократно. В частности, именно перед стартом сервера указывается, должен ли проверяться сертификат клиента и должен ли этот сертификат быть предъявлен клиентом. Далее этой проверкой занимается не RESTinio и не Asio, а лежащий под ними OpenSSL.


Зачем выносить их в коллбэк?

Asio позволяет задать коллбэк, который будет вызван в процессе установления подключения клиента. И этот коллбэк как раз предназначен для того, чтобы можно было проверить какие-то параметры соединения с клиентом и либо разрешить, либо запретить подключение. Как раз то, что сейчас и делает tls_inspector в RESTinio. Т.е. получается дублирование функциональности. Нет большого смысла в RESTinio повторять то, что можно сделать средствами Asio/OpenSSL.


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

Так ведь пользователю никто не запрещает делать свои контейнеры. И посредством state_listener-а управлять тем, когда информация туда попадает и когда удаляется. Речь идет о случаях, когда с соединением нужно связать какую-то информацию, которая может быть полезна request_handler-ам, но когда в этой информации нет смысла, если содинение закрылось. ИМХО, хорошо, когда RESTinio сам может просто выбросить эту информацию вместе с описанием соединения, без дополнительных телодвижений со стороны пользователя.

И в догонку — загрузка файлов не реализована у вас или я чего-то пропустил? В тестовых примерах не нашел…

А так — в проект вставил, все работает, проблем пока не отмечено.
И в догонку — загрузка файлов не реализована у вас или я чего-то пропустил?

В смысле upload файлов на сервер? Такой upload не реализован пока.


в проект вставил, все работает, проблем пока не отмечено.

Что не может не радовать :)

Sign up to leave a comment.

Articles