Комментарии 19
На данный момент вот весь incoming_info_t. Пока там доступен только remote_endpoint.
Если нужна информация из SSL-подключения… То нужно подумать, как ее извлечь и в incoming_info_t передать.
Спасибо за направление, сделаем себе зарубку.
Приглядываюсь к вашему изделию, возможно скоро попробую применить. :)
Пока пользуюсь этим — 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 — когда завершился хендшейк
Тут мне сложно комментировать. Автор основной части кода 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
. Сможете ли вы из этого извлечь информацию, о которой выше шла речь (т.е. клиентский сертификат, версии алгоритмов шифрования и пр.)? Или для этих задач еще что-то нужно?
в стриме есть
/// 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 на что-нибудь другое. По крайне мере за свой счет мы это вряд ли будем делать :)
Пользователь может хранить свои данные сам в своих отдельных структурах данных
Тут есть два момента:
- Как определять, когда данные больше не нужны? Получается, что пользователь будет вынужден завязываться на state_listener-ы. В каких-то случаях это будет неудобно.
- Как писать эти самые контейнеры так, чтобы они подходили и для однопоточного режима работы 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 сам может просто выбросить эту информацию вместе с описанием соединения, без дополнительных телодвижений со стороны пользователя.
Доброго дня!
Мы выпустили версию 0.6.0 в которой connection state listener может получить доступ к параметрам TLS-соединений.
А так — в проект вставил, все работает, проблем пока не отмечено.
Достраиваем в RESTinio четвертый этаж из C++ных шаблонов. Зачем и как?