Skip to content

Commit 985db97

Browse files
committed
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).
1 parent aca67f6 commit 985db97

File tree

4 files changed

+38
-0
lines changed

4 files changed

+38
-0
lines changed

lib/remote/apilistener.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "remote/apifunction.hpp"
99
#include "remote/configpackageutility.hpp"
1010
#include "remote/configobjectutility.hpp"
11+
#include "remote/httputility.hpp"
1112
#include "base/atomic-file.hpp"
1213
#include "base/convert.hpp"
1314
#include "base/defer.hpp"
@@ -2008,6 +2009,31 @@ void ApiListener::ValidateTlsHandshakeTimeout(const Lazy<double>& lvalue, const
20082009
BOOST_THROW_EXCEPTION(ValidationError(this, { "tls_handshake_timeout" }, "Value must be greater than 0."));
20092010
}
20102011

2012+
void ApiListener::ValidateHttpResponseHeaders(const Lazy<Dictionary::Ptr>& lvalue, const ValidationUtils& utils)
2013+
{
2014+
ObjectImpl::ValidateHttpResponseHeaders(lvalue, utils);
2015+
2016+
if (Dictionary::Ptr headers = lvalue(); headers) {
2017+
ObjectLock lock(headers);
2018+
for (auto& [name, value] : headers) {
2019+
if (!HttpUtility::IsValidHeaderName(name.GetData())) {
2020+
BOOST_THROW_EXCEPTION(ValidationError(this, { "http_response_headers", name },
2021+
"Header name is invalid."));
2022+
}
2023+
2024+
if (!value.IsString()) {
2025+
BOOST_THROW_EXCEPTION(ValidationError(this, { "http_response_headers", name },
2026+
"Header value must be a string."));
2027+
}
2028+
2029+
if (!HttpUtility::IsValidHeaderValue(value.Get<String>().GetData())) {
2030+
BOOST_THROW_EXCEPTION(ValidationError(this, { "http_response_headers", name },
2031+
"Header value is invalid."));
2032+
}
2033+
}
2034+
}
2035+
}
2036+
20112037
bool ApiListener::IsHACluster()
20122038
{
20132039
Zone::Ptr zone = Zone::GetLocalZone();

lib/remote/apilistener.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ class ApiListener final : public ObjectImpl<ApiListener>
170170
protected:
171171
void ValidateTlsProtocolmin(const Lazy<String>& lvalue, const ValidationUtils& utils) override;
172172
void ValidateTlsHandshakeTimeout(const Lazy<double>& lvalue, const ValidationUtils& utils) override;
173+
void ValidateHttpResponseHeaders(const Lazy<Dictionary::Ptr>& lvalue, const ValidationUtils& utils) override;
173174

174175
private:
175176
Shared<boost::asio::ssl::context>::Ptr m_SSLContext;

lib/remote/apilistener.ti

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class ApiListener : ConfigObject
5555
[config, deprecated] String access_control_allow_headers;
5656
[config, deprecated] String access_control_allow_methods;
5757

58+
[config] Dictionary::Ptr http_response_headers;
5859

5960
[state, no_user_modify] Timestamp log_message_timestamp;
6061

lib/remote/httpserverconnection.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,16 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc)
466466
request.Parser().body_limit(-1);
467467

468468
response.set(http::field::server, l_ServerHeader);
469+
if (auto listener (ApiListener::GetInstance()); listener) {
470+
if (Dictionary::Ptr headers = listener->GetHttpResponseHeaders(); headers) {
471+
ObjectLock lock(headers);
472+
for (auto& [header, value] : headers) {
473+
if (value.IsString()) {
474+
response.set(header, value.Get<String>());
475+
}
476+
}
477+
}
478+
}
469479

470480
if (!EnsureValidHeaders(buf, request, response, m_ShuttingDown, yc)) {
471481
break;

0 commit comments

Comments
 (0)