RedisConnection: AUTH and SELECT

... or PING to trigger NOAUTH.
This commit is contained in:
Alexander A. Klimov 2021-07-27 12:05:27 +02:00
parent 37e53eaa68
commit e1e8ec2ea2
2 changed files with 71 additions and 0 deletions

View File

@ -28,6 +28,8 @@
using namespace icinga; using namespace icinga;
namespace asio = boost::asio; namespace asio = boost::asio;
boost::regex RedisConnection::m_ErrAuth ("\\AERR AUTH ");
RedisConnection::RedisConnection(const String& host, int port, const String& path, const String& password, int db, RedisConnection::RedisConnection(const String& host, int port, const String& path, const String& password, int db,
bool useTls, bool insecure, const String& certPath, const String& keyPath, const String& caPath, const String& crlPath, bool useTls, bool insecure, const String& certPath, const String& keyPath, const String& caPath, const String& crlPath,
const String& tlsProtocolmin, const String& cipherList, DebugInfo di, const RedisConnection::Ptr& parent) const String& tlsProtocolmin, const String& cipherList, DebugInfo di, const RedisConnection::Ptr& parent)
@ -296,6 +298,7 @@ void RedisConnection::Connect(asio::yield_context& yc)
} }
} }
Handshake(conn, yc);
m_TlsConn = std::move(conn); m_TlsConn = std::move(conn);
} else { } else {
Log(m_Parent ? LogNotice : LogInformation, "IcingaDB") Log(m_Parent ? LogNotice : LogInformation, "IcingaDB")
@ -303,6 +306,7 @@ void RedisConnection::Connect(asio::yield_context& yc)
auto conn (Shared<TcpConn>::Make(m_Strand.context())); auto conn (Shared<TcpConn>::Make(m_Strand.context()));
icinga::Connect(conn->next_layer(), m_Host, Convert::ToString(m_Port), yc); icinga::Connect(conn->next_layer(), m_Host, Convert::ToString(m_Port), yc);
Handshake(conn, yc);
m_TcpConn = std::move(conn); m_TcpConn = std::move(conn);
} }
} else { } else {
@ -311,6 +315,7 @@ void RedisConnection::Connect(asio::yield_context& yc)
auto conn (Shared<UnixConn>::Make(m_Strand.context())); auto conn (Shared<UnixConn>::Make(m_Strand.context()));
conn->next_layer().async_connect(Unix::endpoint(m_Path.CStr()), yc); conn->next_layer().async_connect(Unix::endpoint(m_Path.CStr()), yc);
Handshake(conn, yc);
m_UnixConn = std::move(conn); m_UnixConn = std::move(conn);
} }

View File

@ -5,6 +5,7 @@
#include "base/array.hpp" #include "base/array.hpp"
#include "base/atomic.hpp" #include "base/atomic.hpp"
#include "base/convert.hpp"
#include "base/io-engine.hpp" #include "base/io-engine.hpp"
#include "base/object.hpp" #include "base/object.hpp"
#include "base/ringbuffer.hpp" #include "base/ringbuffer.hpp"
@ -25,6 +26,7 @@
#include <boost/asio/streambuf.hpp> #include <boost/asio/streambuf.hpp>
#include <boost/asio/write.hpp> #include <boost/asio/write.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>
#include <boost/utility/string_view.hpp> #include <boost/utility/string_view.hpp>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
@ -150,6 +152,8 @@ namespace icinga
template<class AsyncWriteStream> template<class AsyncWriteStream>
static void WriteRESP(AsyncWriteStream& stream, const Query& query, boost::asio::yield_context& yc); static void WriteRESP(AsyncWriteStream& stream, const Query& query, boost::asio::yield_context& yc);
static boost::regex m_ErrAuth;
RedisConnection(boost::asio::io_context& io, String host, int port, String path, String password, RedisConnection(boost::asio::io_context& io, String host, int port, String path, String password,
int db, bool useTls, bool insecure, String certPath, String keyPath, String caPath, String crlPath, int db, bool useTls, bool insecure, String certPath, String keyPath, String caPath, String crlPath,
String tlsProtocolmin, String cipherList, DebugInfo di, const Ptr& parent); String tlsProtocolmin, String cipherList, DebugInfo di, const Ptr& parent);
@ -171,6 +175,9 @@ namespace icinga
void IncreasePendingQueries(int count); void IncreasePendingQueries(int count);
void DecreasePendingQueries(int count); void DecreasePendingQueries(int count);
template<class StreamPtr>
void Handshake(StreamPtr& stream, boost::asio::yield_context& yc);
String m_Path; String m_Path;
String m_Host; String m_Host;
int m_Port; int m_Port;
@ -387,6 +394,65 @@ void RedisConnection::WriteOne(StreamPtr& stream, RedisConnection::Query& query,
} }
} }
/**
* Initialize a Redis stream
*
* @param stream Redis server connection
* @param query Redis query
*/
template<class StreamPtr>
void RedisConnection::Handshake(StreamPtr& strm, boost::asio::yield_context& yc)
{
if (m_Password.IsEmpty() && !m_DbIndex) {
// Trigger NOAUTH
WriteRESP(*strm, {"PING"}, yc);
} else {
if (!m_Password.IsEmpty()) {
WriteRESP(*strm, {"AUTH", m_Password}, yc);
}
if (m_DbIndex) {
WriteRESP(*strm, {"SELECT", Convert::ToString(m_DbIndex)}, yc);
}
}
strm->async_flush(yc);
if (m_Password.IsEmpty() && !m_DbIndex) {
Reply pong (ReadRESP(*strm, yc));
if (pong.IsObjectType<RedisError>()) {
// Likely NOAUTH
BOOST_THROW_EXCEPTION(std::runtime_error(RedisError::Ptr(pong)->GetMessage()));
}
} else {
if (!m_Password.IsEmpty()) {
Reply auth (ReadRESP(*strm, yc));
if (auth.IsObjectType<RedisError>()) {
auto& authErr (RedisError::Ptr(auth)->GetMessage().GetData());
boost::smatch what;
if (boost::regex_search(authErr, what, m_ErrAuth)) {
Log(LogWarning, "IcingaDB") << authErr;
} else {
// Likely WRONGPASS
BOOST_THROW_EXCEPTION(std::runtime_error(authErr));
}
}
}
if (m_DbIndex) {
Reply select (ReadRESP(*strm, yc));
if (select.IsObjectType<RedisError>()) {
// Likely NOAUTH or ERR DB
BOOST_THROW_EXCEPTION(std::runtime_error(RedisError::Ptr(select)->GetMessage()));
}
}
}
}
/** /**
* Read a Redis protocol value from stream * Read a Redis protocol value from stream
* *