Skip to content

Commit

Permalink
Icinga DB: Support Redis username authentication
Browse files Browse the repository at this point in the history
The Redis ACL system was introduced with Redis 6.0. It introduced users
with precisely granular permissions. This change allows Icinga 2 to use
the Icinga DB feature against a Redis with an ACL user.

This was reflected in the documentation, next to the already
implemented, but undocumented Redis database.

Closes #9536.
  • Loading branch information
oxzi committed Oct 24, 2024
1 parent 57fab7f commit 98f60fd
Show file tree
Hide file tree
Showing 5 changed files with 19 additions and 9 deletions.
1 change: 1 addition & 0 deletions doc/09-object-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -1387,6 +1387,7 @@ Configuration Attributes:
host | String | **Optional.** Redis host. Defaults to `127.0.0.1`.
port | Number | **Optional.** Redis port. Defaults to `6380` since the Redis server provided by the `icingadb-redis` package listens on that port.
path | String | **Optional.** Redis unix socket path. Can be used instead of `host` and `port` attributes.
username | String | **Optional.** Redis auth username. Only possible if Redis ACLs are used. Requires `password` to be set as well.
password | String | **Optional.** Redis auth password.
db\_index | Number | **Optional.** Redis logical database by its number. Defaults to `0`.
enable\_tls | Boolean | **Optional.** Whether to use TLS.
Expand Down
9 changes: 7 additions & 2 deletions lib/icingadb/icingadb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ void IcingaDB::Validate(int types, const ValidationUtils& utils)
if (!(types & FAConfig))
return;

if (!GetUsername().IsEmpty() && GetPassword().IsEmpty()) {
BOOST_THROW_EXCEPTION(ValidationError(this, std::vector<String>(),
"Redis password must be set, if username is provided."));
}

if (GetEnableTls() && GetCertPath().IsEmpty() != GetKeyPath().IsEmpty()) {
BOOST_THROW_EXCEPTION(ValidationError(this, std::vector<String>(), "Validation failed: Either both a client certificate (cert_path) and its private key (key_path) or none of them must be given."));
}
Expand Down Expand Up @@ -77,7 +82,7 @@ void IcingaDB::Start(bool runtimeCreated)

m_WorkQueue.SetExceptionCallback([this](boost::exception_ptr exp) { ExceptionHandler(std::move(exp)); });

m_Rcon = new RedisConnection(GetHost(), GetPort(), GetPath(), GetPassword(), GetDbIndex(),
m_Rcon = new RedisConnection(GetHost(), GetPort(), GetPath(), GetUsername(), GetPassword(), GetDbIndex(),
GetEnableTls(), GetInsecureNoverify(), GetCertPath(), GetKeyPath(), GetCaPath(), GetCrlPath(),
GetTlsProtocolmin(), GetCipherList(), GetConnectTimeout(), GetDebugInfo());
m_RconLocked.store(m_Rcon);
Expand All @@ -87,7 +92,7 @@ void IcingaDB::Start(bool runtimeCreated)
if (!ctype)
continue;

RedisConnection::Ptr con = new RedisConnection(GetHost(), GetPort(), GetPath(), GetPassword(), GetDbIndex(),
RedisConnection::Ptr con = new RedisConnection(GetHost(), GetPort(), GetPath(), GetUsername(), GetPassword(), GetDbIndex(),
GetEnableTls(), GetInsecureNoverify(), GetCertPath(), GetKeyPath(), GetCaPath(), GetCrlPath(),
GetTlsProtocolmin(), GetCipherList(), GetConnectTimeout(), GetDebugInfo(), m_Rcon);

Expand Down
1 change: 1 addition & 0 deletions lib/icingadb/icingadb.ti
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class IcingaDB : ConfigObject
default {{{ return 6380; }}}
};
[config, no_user_modify] String path;
[config, no_user_modify] String username;
[config, no_user_view, no_user_modify] String password;
[config, no_user_modify] int db_index;

Expand Down
8 changes: 4 additions & 4 deletions lib/icingadb/redisconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,18 @@ 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& username, const String& password, int db,
bool useTls, bool insecure, const String& certPath, const String& keyPath, const String& caPath, const String& crlPath,
const String& tlsProtocolmin, const String& cipherList, double connectTimeout, DebugInfo di, const RedisConnection::Ptr& parent)
: RedisConnection(IoEngine::Get().GetIoContext(), host, port, path, password, db,
: RedisConnection(IoEngine::Get().GetIoContext(), host, port, path, username, password, db,
useTls, insecure, certPath, keyPath, caPath, crlPath, tlsProtocolmin, cipherList, connectTimeout, std::move(di), parent)
{
}

RedisConnection::RedisConnection(boost::asio::io_context& io, String host, int port, String path, String password,
RedisConnection::RedisConnection(boost::asio::io_context& io, String host, int port, String path, String username, String password,
int db, bool useTls, bool insecure, String certPath, String keyPath, String caPath, String crlPath,
String tlsProtocolmin, String cipherList, double connectTimeout, DebugInfo di, const RedisConnection::Ptr& parent)
: m_Host(std::move(host)), m_Port(port), m_Path(std::move(path)), m_Password(std::move(password)),
: m_Host(std::move(host)), m_Port(port), m_Path(std::move(path)), m_Username(std::move(username)), m_Password(std::move(password)),
m_DbIndex(db), m_CertPath(std::move(certPath)), m_KeyPath(std::move(keyPath)), m_Insecure(insecure),
m_CaPath(std::move(caPath)), m_CrlPath(std::move(crlPath)), m_TlsProtocolmin(std::move(tlsProtocolmin)),
m_CipherList(std::move(cipherList)), m_ConnectTimeout(connectTimeout), m_DebugInfo(std::move(di)), m_Connecting(false), m_Connected(false),
Expand Down
9 changes: 6 additions & 3 deletions lib/icingadb/redisconnection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ namespace icinga
: Config(config), State(state), History(history) { }
};

RedisConnection(const String& host, int port, const String& path, const String& password, int db,
RedisConnection(const String& host, int port, const String& path, const String& username, const String& password, int db,
bool useTls, bool insecure, const String& certPath, const String& keyPath, const String& caPath, const String& crlPath,
const String& tlsProtocolmin, const String& cipherList, double connectTimeout, DebugInfo di, const Ptr& parent = nullptr);

Expand Down Expand Up @@ -196,7 +196,7 @@ namespace icinga

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 username, String password,
int db, bool useTls, bool insecure, String certPath, String keyPath, String caPath, String crlPath,
String tlsProtocolmin, String cipherList, double connectTimeout, DebugInfo di, const Ptr& parent);

Expand Down Expand Up @@ -227,6 +227,7 @@ namespace icinga
String m_Path;
String m_Host;
int m_Port;
String m_Username;
String m_Password;
int m_DbIndex;

Expand Down Expand Up @@ -457,7 +458,9 @@ void RedisConnection::Handshake(StreamPtr& strm, boost::asio::yield_context& yc)
// Trigger NOAUTH
WriteRESP(*strm, {"PING"}, yc);
} else {
if (!m_Password.IsEmpty()) {
if (!m_Username.IsEmpty()) {
WriteRESP(*strm, {"AUTH", m_Username, m_Password}, yc);
} else if (!m_Password.IsEmpty()) {
WriteRESP(*strm, {"AUTH", m_Password}, yc);
}

Expand Down

0 comments on commit 98f60fd

Please sign in to comment.