Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions bazel/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,11 @@ selects.config_setting_group(
],
)

config_setting(
name = "disable_admin_html",
values = {"define": "admin_html=disabled"},
)

Comment on lines +321 to +325

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config_setting(
name = "disable_hot_restart_setting",
values = {"define": "hot_restart=disabled"},
Expand Down
4 changes: 4 additions & 0 deletions bazel/envoy_build_system.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ load(
load(":envoy_pch.bzl", _envoy_pch_library = "envoy_pch_library")
load(
":envoy_select.bzl",
_envoy_select_admin_html = "envoy_select_admin_html",
_envoy_select_admin_no_html = "envoy_select_admin_no_html",
_envoy_select_boringssl = "envoy_select_boringssl",
_envoy_select_enable_http3 = "envoy_select_enable_http3",
_envoy_select_google_grpc = "envoy_select_google_grpc",
Expand Down Expand Up @@ -206,6 +208,8 @@ def envoy_google_grpc_external_deps():
# from the other bzl files (e.g. envoy_select.bzl, envoy_binary.bzl, etc.)

# Select wrappers (from envoy_select.bzl)
envoy_select_admin_html = _envoy_select_admin_html
envoy_select_admin_no_html = _envoy_select_admin_no_html
envoy_select_boringssl = _envoy_select_boringssl
envoy_select_google_grpc = _envoy_select_google_grpc
envoy_select_enable_http3 = _envoy_select_enable_http3
Expand Down
3 changes: 2 additions & 1 deletion bazel/envoy_internal.bzl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# DO NOT LOAD THIS FILE. Targets from this file should be considered private
# and not used outside of the @envoy//bazel package.
load(":envoy_select.bzl", "envoy_select_enable_http3", "envoy_select_google_grpc", "envoy_select_hot_restart")
load(":envoy_select.bzl", "envoy_select_admin_html", "envoy_select_enable_http3", "envoy_select_google_grpc", "envoy_select_hot_restart")

# Compute the final copts based on various options.
def envoy_copts(repository, test = False):
Expand Down Expand Up @@ -122,6 +122,7 @@ def envoy_copts(repository, test = False):
repository + "//bazel:uhv_enabled": ["-DENVOY_ENABLE_UHV"],
"//conditions:default": [],
}) + envoy_select_hot_restart(["-DENVOY_HOT_RESTART"], repository) + \
envoy_select_admin_html(["-DENVOY_ADMIN_HTML"], repository) + \
envoy_select_enable_http3(["-DENVOY_ENABLE_QUIC"], repository) + \
_envoy_select_perf_annotation(["-DENVOY_PERF_ANNOTATION"]) + \
_envoy_select_perfetto(["-DENVOY_PERFETTO"]) + \
Expand Down
13 changes: 13 additions & 0 deletions bazel/envoy_select.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@ def envoy_select_google_grpc(xs, repository = ""):
"//conditions:default": xs,
})

# Selects the given values if admin HTML is enabled in the current build.
def envoy_select_admin_html(xs, repository = ""):
return select({
repository + "//bazel:disable_admin_html": [],
"//conditions:default": xs,
})

def envoy_select_admin_no_html(xs, repository = ""):
return select({
repository + "//bazel:disable_admin_html": xs,
"//conditions:default": [],
})

# Selects the given values if http3 is enabled in the current build.
def envoy_select_enable_http3(xs, repository = ""):
return select({
Expand Down
1 change: 1 addition & 0 deletions ci/do_ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ elif [[ "$CI_TARGET" == "bazel.compile_time_options" ]]; then
# Right now, none of the available compile-time options conflict with each other. If this
# changes, this build type may need to be broken up.
COMPILE_TIME_OPTIONS=(
"--define" "admin_html=disabled"
"--define" "signal_trace=disabled"
"--define" "hot_restart=disabled"
"--define" "google_grpc=disabled"
Expand Down
9 changes: 7 additions & 2 deletions source/server/admin/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_package",
"envoy_select_admin_html",
"envoy_select_admin_no_html",
)

licenses(["notice"]) # Apache 2
Expand All @@ -10,7 +12,11 @@ envoy_package()

envoy_cc_library(
name = "admin_lib",
srcs = ["admin.cc"],
srcs = ["admin.cc"] + envoy_select_admin_html([
"admin_html.cc",
]) + envoy_select_admin_no_html([
"admin_no_html.cc",
]),
hdrs = ["admin.h"],
deps = [
":admin_filter_lib",
Expand Down Expand Up @@ -45,7 +51,6 @@ envoy_cc_library(
"//source/common/common:mutex_tracer_lib",
"//source/common/common:utility_lib",
"//source/common/formatter:substitution_formatter_lib",
"//source/common/html:utility_lib",
"//source/common/http:codes_lib",
"//source/common/http:conn_manager_lib",
"//source/common/http:date_provider_lib",
Expand Down
114 changes: 0 additions & 114 deletions source/server/admin/admin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
#include "source/common/common/mutex_tracer_impl.h"
#include "source/common/common/utility.h"
#include "source/common/formatter/substitution_formatter.h"
#include "source/common/html/utility.h"
#include "source/common/http/codes.h"
#include "source/common/http/conn_manager_utility.h"
#include "source/common/http/header_map_impl.h"
Expand All @@ -45,72 +44,6 @@
namespace Envoy {
namespace Server {

namespace {

/**
* Favicon base64 image was harvested by screen-capturing the favicon from a Chrome tab
* while visiting https://www.envoyproxy.io/. The resulting PNG was translated to base64
* by dropping it into https://www.base64-image.de/ and then pasting the resulting string
* below.
*
* The actual favicon source for that, https://www.envoyproxy.io/img/favicon.ico is nicer
* because it's transparent, but is also 67646 bytes, which is annoying to inline. We could
* just reference that rather than inlining it, but then the favicon won't work when visiting
* the admin page from a network that can't see the internet.
*/
const char EnvoyFavicon[] =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1"
"BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAH9SURBVEhL7ZRdTttAFIUrUFaAX5w9gIhgUfzshFRK+gIbaVbA"
"zwaqCly1dSpKk5A485/YCdXpHTB4BsdgVe0bD0cZ3Xsm38yZ8byTUuJ/6g3wqqoBrBhPTzmmLfptMbAzttJTpTKAF2MWC"
"7ADCdNIwXZpvMMwayiIwwS874CcOc9VuQPR1dBBChPMITpFXXU45hukIIH6kHhzVqkEYB8F5HYGvZ5B7EvwmHt9K/59Cr"
"U3QbY2RNYaQPYmJc+jPIBICNCcg20ZsAsCPfbcrFlRF+cJZpvXSJt9yMTxO/IAzJrCOfhJXiOgFEX/SbZmezTWxyNk4Q9"
"anHMmjnzAhEyhAW8LCE6wl26J7ZFHH1FMYQxh567weQBOO1AW8D7P/UXAQySq/QvL8Fu9HfCEw4SKALm5BkC3bwjwhSKr"
"A5hYAMXTJnPNiMyRBVzVjcgCyHiSm+8P+WGlnmwtP2RzbCMiQJ0d2KtmmmPorRHEhfMROVfTG5/fYrF5iWXzE80tfy9WP"
"sCqx5Buj7FYH0LvDyHiqd+3otpsr4/fa5+xbEVQPfrYnntylQG5VGeMLBhgEfyE7o6e6qYzwHIjwl0QwXSvvTmrVAY4D5"
"ddvT64wV0jRrr7FekO/XEjwuwwhuw7Ef7NY+dlfXpLb06EtHUJdVbsxvNUqBrwj/QGeEUSfwBAkmWHn5Bb/gAAAABJRU5";

const char AdminHtmlStart[] = R"(
<head>
<title>Envoy Admin</title>
<link rel='shortcut icon' type='image/png' href='@FAVICON@'/>
<style>
.home-table {
font-family: sans-serif;
font-size: medium;
border-collapse: collapse;
}

.home-row:nth-child(even) {
background-color: #dddddd;
}

.home-data {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}

.home-form {
margin-bottom: 0;
}
</style>
</head>
<body>
<table class='home-table'>
<thead>
<th class='home-data'>Command</th>
<th class='home-data'>Description</th>
</thead>
<tbody>
)";

const char AdminHtmlEnd[] = R"(
</tbody>
</table>
</body>
)";
} // namespace

ConfigTracker& AdminImpl::getConfigTracker() { return config_tracker_; }

AdminImpl::NullRouteConfigProvider::NullRouteConfigProvider(TimeSource& time_source)
Expand Down Expand Up @@ -389,53 +322,6 @@ void AdminImpl::getHelp(Buffer::Instance& response) {
}
}

Http::Code AdminImpl::handlerAdminHome(absl::string_view, Http::ResponseHeaderMap& response_headers,
Buffer::Instance& response, AdminStream&) {
response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Html);

response.add(absl::StrReplaceAll(AdminHtmlStart, {{"@FAVICON@", EnvoyFavicon}}));

// Prefix order is used during searching, but for printing do them in alpha order.
for (const UrlHandler* handler : sortedHandlers()) {
absl::string_view path = handler->prefix_;

if (path == "/") {
continue; // No need to print self-link to index page.
}

// Remove the leading slash from the link, so that the admin page can be
// rendered as part of another console, on a sub-path.
//
// E.g. consider a downstream dashboard that embeds the Envoy admin console.
// In that case, the "/stats" endpoint would be at
// https://DASHBOARD/envoy_admin/stats. If the links we present on the home
// page are absolute (e.g. "/stats") they won't work in the context of the
// dashboard. Removing the leading slash, they will work properly in both
// the raw admin console and when embedded in another page and URL
// hierarchy.
ASSERT(!path.empty());
ASSERT(path[0] == '/');
path = path.substr(1);

// For handlers that mutate state, render the link as a button in a POST form,
// rather than an anchor tag. This should discourage crawlers that find the /
// page from accidentally mutating all the server state by GETting all the hrefs.
const char* link_format =
handler->mutates_server_state_
? "<form action='{}' method='post' class='home-form'><button>{}</button></form>"
: "<a href='{}'>{}</a>";
const std::string link = fmt::format(link_format, path, path);

// Handlers are all specified by statically above, and are thus trusted and do
// not require escaping.
response.add(fmt::format("<tr class='home-row'><td class='home-data'>{}</td>"
"<td class='home-data'>{}</td></tr>\n",
link, Html::Utility::sanitize(handler->help_text_)));
}
response.add(AdminHtmlEnd);
return Http::Code::OK;
}

const Network::Address::Instance& AdminImpl::localAddress() {
return *server_.localInfo().address();
}
Expand Down
122 changes: 122 additions & 0 deletions source/server/admin/admin_html.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#include "source/common/html/utility.h"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note to reviewers: this code is unchanged; just moved.

#include "source/server/admin/admin.h"

namespace Envoy {
namespace Server {

namespace {

/**
* Favicon base64 image was harvested by screen-capturing the favicon from a Chrome tab
* while visiting https://www.envoyproxy.io/. The resulting PNG was translated to base64
* by dropping it into https://www.base64-image.de/ and then pasting the resulting string
* below.
*
* The actual favicon source for that, https://www.envoyproxy.io/img/favicon.ico is nicer
* because it's transparent, but is also 67646 bytes, which is annoying to inline. We could
* just reference that rather than inlining it, but then the favicon won't work when visiting
* the admin page from a network that can't see the internet.
*/
const char EnvoyFavicon[] =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1"
"BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAH9SURBVEhL7ZRdTttAFIUrUFaAX5w9gIhgUfzshFRK+gIbaVbA"
"zwaqCly1dSpKk5A485/YCdXpHTB4BsdgVe0bD0cZ3Xsm38yZ8byTUuJ/6g3wqqoBrBhPTzmmLfptMbAzttJTpTKAF2MWC"
"7ADCdNIwXZpvMMwayiIwwS874CcOc9VuQPR1dBBChPMITpFXXU45hukIIH6kHhzVqkEYB8F5HYGvZ5B7EvwmHt9K/59Cr"
"U3QbY2RNYaQPYmJc+jPIBICNCcg20ZsAsCPfbcrFlRF+cJZpvXSJt9yMTxO/IAzJrCOfhJXiOgFEX/SbZmezTWxyNk4Q9"
"anHMmjnzAhEyhAW8LCE6wl26J7ZFHH1FMYQxh567weQBOO1AW8D7P/UXAQySq/QvL8Fu9HfCEw4SKALm5BkC3bwjwhSKr"
"A5hYAMXTJnPNiMyRBVzVjcgCyHiSm+8P+WGlnmwtP2RzbCMiQJ0d2KtmmmPorRHEhfMROVfTG5/fYrF5iWXzE80tfy9WP"
"sCqx5Buj7FYH0LvDyHiqd+3otpsr4/fa5+xbEVQPfrYnntylQG5VGeMLBhgEfyE7o6e6qYzwHIjwl0QwXSvvTmrVAY4D5"
"ddvT64wV0jRrr7FekO/XEjwuwwhuw7Ef7NY+dlfXpLb06EtHUJdVbsxvNUqBrwj/QGeEUSfwBAkmWHn5Bb/gAAAABJRU5";

const char AdminHtmlStart[] = R"(
<head>
<title>Envoy Admin</title>
<link rel='shortcut icon' type='image/png' href='@FAVICON@'/>
<style>
.home-table {
font-family: sans-serif;
font-size: medium;
border-collapse: collapse;
}

.home-row:nth-child(even) {
background-color: #dddddd;
}

.home-data {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}

.home-form {
margin-bottom: 0;
}
</style>
</head>
<body>
<table class='home-table'>
<thead>
<th class='home-data'>Command</th>
<th class='home-data'>Description</th>
</thead>
<tbody>
)";

const char AdminHtmlEnd[] = R"(
</tbody>
</table>
</body>
)";

} // namespace

Http::Code AdminImpl::handlerAdminHome(absl::string_view, Http::ResponseHeaderMap& response_headers,
Buffer::Instance& response, AdminStream&) {
response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Html);

response.add(absl::StrReplaceAll(AdminHtmlStart, {{"@FAVICON@", EnvoyFavicon}}));

// Prefix order is used during searching, but for printing do them in alpha order.
for (const UrlHandler* handler : sortedHandlers()) {
absl::string_view path = handler->prefix_;

if (path == "/") {
continue; // No need to print self-link to index page.
}

// Remove the leading slash from the link, so that the admin page can be
// rendered as part of another console, on a sub-path.
//
// E.g. consider a downstream dashboard that embeds the Envoy admin console.
// In that case, the "/stats" endpoint would be at
// https://DASHBOARD/envoy_admin/stats. If the links we present on the home
// page are absolute (e.g. "/stats") they won't work in the context of the
// dashboard. Removing the leading slash, they will work properly in both
// the raw admin console and when embedded in another page and URL
// hierarchy.
ASSERT(!path.empty());
ASSERT(path[0] == '/');
path = path.substr(1);

// For handlers that mutate state, render the link as a button in a POST form,
// rather than an anchor tag. This should discourage crawlers that find the /
// page from accidentally mutating all the server state by GETting all the hrefs.
const char* link_format =
handler->mutates_server_state_
? "<form action='{}' method='post' class='home-form'><button>{}</button></form>"
: "<a href='{}'>{}</a>";
const std::string link = fmt::format(link_format, path, path);

// Handlers are all specified by statically above, and are thus trusted and do
// not require escaping.
response.add(fmt::format("<tr class='home-row'><td class='home-data'>{}</td>"
"<td class='home-data'>{}</td></tr>\n",
link, Html::Utility::sanitize(handler->help_text_)));
}
response.add(AdminHtmlEnd);
return Http::Code::OK;
}

} // namespace Server
} // namespace Envoy
15 changes: 15 additions & 0 deletions source/server/admin/admin_no_html.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "source/common/html/utility.h"
#include "source/server/admin/admin.h"

namespace Envoy {
namespace Server {

Http::Code AdminImpl::handlerAdminHome(absl::string_view, Http::ResponseHeaderMap& response_headers,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this block here is the only new code in the PR.

Buffer::Instance& response, AdminStream&) {
response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Text);
response.add("HTML output was disabled by building with --define=admin_html=disabled");
return Http::Code::OK;
}

} // namespace Server
} // namespace Envoy
5 changes: 5 additions & 0 deletions test/integration/integration_admin_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,13 @@ TEST_P(IntegrationAdminTest, Admin) {
EXPECT_THAT(response->body(), HasSubstr("admin commands are:"));

EXPECT_EQ("200", request("admin", "GET", "/", response));
#ifdef ENVOY_ADMIN_HTML

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: in other cases I did not need a -D for this, but it seemed painful to tweak this particular test without it.

I have this gut feel that this define is costly from a build perspective since it is passed on every compile-command, even though it's needed only by one test.

LMK if you think I should re-work this somehow.

EXPECT_EQ("text/html; charset=UTF-8", contentType(response));
EXPECT_THAT(response->body(), HasSubstr("<title>Envoy Admin</title>"));
#else
EXPECT_EQ("text/plain", contentType(response));
EXPECT_THAT(response->body(), HasSubstr("HTML output was disabled"));
#endif

EXPECT_EQ("200", request("admin", "GET", "/server_info", response));
EXPECT_EQ("application/json", contentType(response));
Expand Down
Loading