#s4v2 - Add simple authorization in highlanderd agent
This implementation allows mutating requests from only the "safepower" user. Ideally, we should use RpcSp to achieve this, but this is not easily supported with a non-framework grpc service without using command line args. We can't use command line args for this in highlanderd since it hosts multiple services and the args affect the entire process.
Tested:
go/hs4log/5bfeFEnNvs worked, but manual requests are rejected:
```
grpc_cli call dns:///$M-n1:20120 StartAction ""
connecting to dns:///yscwj17-n1:20120
Rpc failed with status code 7, error message: Peer is not authorized
```
PiperOrigin-RevId: 831461171
Change-Id: Ie324d8a1ac091d617844597c3bdbd313c0441c4f
diff --git a/safepower_agent.cc b/safepower_agent.cc
index a933816..8882b76 100644
--- a/safepower_agent.cc
+++ b/safepower_agent.cc
@@ -1,18 +1,23 @@
#include "safepower_agent.h"
#include <memory>
+#include <string>
#include <utility>
+#include <vector>
#include "action_context.h"
#include "daemon_context.h"
#include "bmc/convert_status.h"
#include "safepower_agent.pb.h"
#include "state_change_reactor.h"
+#include "absl/algorithm/container.h"
#include "absl/base/nullability.h"
#include "absl/log/log.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/str_join.h"
#include "absl/time/time.h"
+#include "grpcpp/security/auth_context.h"
#include "grpcpp/server_context.h"
#include "grpcpp/support/server_callback.h"
#include "grpcpp/support/status.h"
@@ -28,6 +33,37 @@
}
template <typename Reactor>
+bool CheckAuthorization(const grpc::AuthContext* context,
+ const std::vector<std::string>& allowed_callers,
+ Reactor& reactor) {
+ if (allowed_callers.empty()) {
+ return true;
+ }
+ if (context == nullptr) {
+ LOG(ERROR) << "Context is null";
+ reactor->Finish({grpc::StatusCode::PERMISSION_DENIED, "Context is null"});
+ return false;
+ }
+ if (!context->IsPeerAuthenticated()) {
+ LOG(ERROR) << "Peer is not authenticated";
+ reactor->Finish(
+ {grpc::StatusCode::PERMISSION_DENIED, "Peer is not authenticated"});
+ return false;
+ }
+ for (const auto& peer : context->GetPeerIdentity()) {
+ if (absl::c_linear_search(allowed_callers, peer)) {
+ return true;
+ }
+ }
+ LOG(ERROR) << "Peer is not authorized: "
+ << absl::StrJoin(context->GetPeerIdentity(), "|",
+ absl::StreamFormatter());
+ reactor->Finish(
+ {grpc::StatusCode::PERMISSION_DENIED, "Peer is not authorized"});
+ return false;
+}
+
+template <typename Reactor>
bool AbortWhileDisruptionExpected(Reactor& reactor) {
absl::Duration disruption =
DaemonContext::Get().disruption_manager().PendingDisruption();
@@ -49,6 +85,10 @@
const safepower_agent_proto::StartActionRequest* request,
safepower_agent_proto::StartActionResponse* /*response*/) {
grpc::ServerUnaryReactor* reactor = context->DefaultReactor();
+ if (!CheckAuthorization(context->auth_context().get(), allowed_callers_,
+ reactor)) {
+ return reactor;
+ }
if (AbortWhileDisruptionExpected(reactor)) return reactor;
absl::StatusOr<ActionContext* > action_context =
action_context_manager_->StartAction(*request);
@@ -63,23 +103,27 @@
grpc::ServerWriteReactor<safepower_agent_proto::ActionStateLog>*
SafepowerLocalAgentImpl::MonitorAction(
- grpc::CallbackServerContext* /*context*/,
+ grpc::CallbackServerContext* context,
const safepower_agent_proto::MonitorActionRequest* request) {
auto reactor = std::make_unique<StateChangeWriteReactor<ActionStateLog>>();
+ if (!CheckAuthorization(context->auth_context().get(), allowed_callers_,
+ reactor)) {
+ return DetachReactor(std::move(reactor));
+ }
+
if (AbortWhileDisruptionExpected(reactor)) {
return DetachReactor(std::move(reactor));
}
ActionContext* action_context =
action_context_manager_->GetActionContext(request->flight_record());
if (action_context == nullptr) {
- LOG(ERROR) << "Action flight_record: " <<
- request->flight_record().DebugString() << " not found";
+ LOG(ERROR) << "Action flight_record: "
+ << request->flight_record().DebugString() << " not found";
reactor->Finish(
- {grpc::StatusCode::NOT_FOUND,
- absl::StrCat("Action flight_id: ",
- request->flight_record().flight_name(),
- " step_id: ", request->flight_record().step_id(),
- " not found")});
+ {grpc::StatusCode::NOT_FOUND,
+ absl::StrCat(
+ "Action flight_id: ", request->flight_record().flight_name(),
+ " step_id: ", request->flight_record().step_id(), " not found")});
return DetachReactor(std::move(reactor));
}
reactor->Connect(action_context->action_state_updater());
@@ -103,6 +147,10 @@
const safepower_agent_proto::SystemState* request,
safepower_agent_proto::SystemStateUpdateResponse* /*response*/) {
grpc::ServerUnaryReactor* reactor = context->DefaultReactor();
+ if (!CheckAuthorization(context->auth_context().get(), allowed_callers_,
+ reactor)) {
+ return reactor;
+ }
if (AbortWhileDisruptionExpected(reactor)) return reactor;
global_system_state_updater_->UpdateState(*request);
reactor->Finish(grpc::Status::OK);
diff --git a/safepower_agent.h b/safepower_agent.h
index 412d64b..ec9022a 100644
--- a/safepower_agent.h
+++ b/safepower_agent.h
@@ -2,7 +2,9 @@
#define PRODUCTION_BORG_MGMT_NODE_PROXY_SAFEPOWER_SAFEPOWER_AGENT_SAFEPOWER_AGENT_H_
#include <memory>
+#include <string>
#include <utility>
+#include <vector>
#include "action_context.h"
#include "safepower_agent.grpc.pb.h"
@@ -57,12 +59,19 @@
const safepower_agent_proto::SystemState* request,
safepower_agent_proto::SystemStateUpdateResponse* response) override;
+ template <typename StringContainer>
+ void AllowCallers(const StringContainer& caller) {
+ allowed_callers_.insert(allowed_callers_.end(), caller.begin(),
+ caller.end());
+ }
+
private:
std::shared_ptr<StateUpdater<safepower_agent_proto::SystemState>>
system_state_updater_;
std::shared_ptr<StateUpdater<safepower_agent_proto::SystemState>>
global_system_state_updater_;
std::unique_ptr<ActionContextManager> action_context_manager_;
+ std::vector<std::string> allowed_callers_;
};
} // namespace safepower_agent