Allow to set extra headers in HTTP responses

Use case: Allow settings headers like Strict-Transport-Security if one likes.
How this headers would benefit the Icinga 2 API is questionable, but there are
security scanners that see HTTPS and complain about it, so this gives an easy
way to make them happy (with this probably being the only benefit).
This commit is contained in:
Julian Brost 2025-09-16 15:17:28 +02:00
parent aca67f6d88
commit 985db970bb
4 changed files with 38 additions and 0 deletions

View file

@ -8,6 +8,7 @@
#include "remote/apifunction.hpp"
#include "remote/configpackageutility.hpp"
#include "remote/configobjectutility.hpp"
#include "remote/httputility.hpp"
#include "base/atomic-file.hpp"
#include "base/convert.hpp"
#include "base/defer.hpp"
@ -2008,6 +2009,31 @@ void ApiListener::ValidateTlsHandshakeTimeout(const Lazy<double>& lvalue, const
BOOST_THROW_EXCEPTION(ValidationError(this, { "tls_handshake_timeout" }, "Value must be greater than 0."));
}
void ApiListener::ValidateHttpResponseHeaders(const Lazy<Dictionary::Ptr>& lvalue, const ValidationUtils& utils)
{
ObjectImpl::ValidateHttpResponseHeaders(lvalue, utils);
if (Dictionary::Ptr headers = lvalue(); headers) {
ObjectLock lock(headers);
for (auto& [name, value] : headers) {
if (!HttpUtility::IsValidHeaderName(name.GetData())) {
BOOST_THROW_EXCEPTION(ValidationError(this, { "http_response_headers", name },
"Header name is invalid."));
}
if (!value.IsString()) {
BOOST_THROW_EXCEPTION(ValidationError(this, { "http_response_headers", name },
"Header value must be a string."));
}
if (!HttpUtility::IsValidHeaderValue(value.Get<String>().GetData())) {
BOOST_THROW_EXCEPTION(ValidationError(this, { "http_response_headers", name },
"Header value is invalid."));
}
}
}
}
bool ApiListener::IsHACluster()
{
Zone::Ptr zone = Zone::GetLocalZone();

View file

@ -170,6 +170,7 @@ public:
protected:
void ValidateTlsProtocolmin(const Lazy<String>& lvalue, const ValidationUtils& utils) override;
void ValidateTlsHandshakeTimeout(const Lazy<double>& lvalue, const ValidationUtils& utils) override;
void ValidateHttpResponseHeaders(const Lazy<Dictionary::Ptr>& lvalue, const ValidationUtils& utils) override;
private:
Shared<boost::asio::ssl::context>::Ptr m_SSLContext;

View file

@ -55,6 +55,7 @@ class ApiListener : ConfigObject
[config, deprecated] String access_control_allow_headers;
[config, deprecated] String access_control_allow_methods;
[config] Dictionary::Ptr http_response_headers;
[state, no_user_modify] Timestamp log_message_timestamp;

View file

@ -466,6 +466,16 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc)
request.Parser().body_limit(-1);
response.set(http::field::server, l_ServerHeader);
if (auto listener (ApiListener::GetInstance()); listener) {
if (Dictionary::Ptr headers = listener->GetHttpResponseHeaders(); headers) {
ObjectLock lock(headers);
for (auto& [header, value] : headers) {
if (value.IsString()) {
response.set(header, value.Get<String>());
}
}
}
}
if (!EnsureValidHeaders(buf, request, response, m_ShuttingDown, yc)) {
break;