Skip to content

oidc impersonation #11449

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions ydb/mvp/oidc_proxy/mvp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ void TMVP::TryGetOidcOptionsFromConfig(const YAML::Node& config) {
OpenIdConnectSettings.AuthUrlPath = oidc["auth_url_path"].as<std::string>(OpenIdConnectSettings.DEFAULT_AUTH_URL_PATH);
OpenIdConnectSettings.TokenUrlPath = oidc["token_url_path"].as<std::string>(OpenIdConnectSettings.DEFAULT_TOKEN_URL_PATH);
OpenIdConnectSettings.ExchangeUrlPath = oidc["exchange_url_path"].as<std::string>(OpenIdConnectSettings.DEFAULT_EXCHANGE_URL_PATH);
OpenIdConnectSettings.ImpersonateUrlPath = oidc["impersonate_url_path"].as<std::string>(OpenIdConnectSettings.DEFAULT_IMPERSONATE_URL_PATH);
Cout << "Started processing allowed_proxy_hosts..." << Endl;
for (const std::string& host : oidc["allowed_proxy_hosts"].as<std::vector<std::string>>()) {
Cout << host << " added to allowed_proxy_hosts" << Endl;
Expand Down
16 changes: 16 additions & 0 deletions ydb/mvp/oidc_proxy/oidc_client.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "oidc_client.h"
#include "oidc_protected_page_handler.h"
#include "oidc_session_create_handler.h"
#include "oidc_impersonate_start_page_nebius.h"
#include "oidc_impersonate_stop_page_nebius.h"

namespace NMVP {
namespace NOIDC {
Expand All @@ -14,6 +16,20 @@ void InitOIDC(NActors::TActorSystem& actorSystem,
)
);

if (settings.AccessServiceType == NMvp::nebius_v1) {
actorSystem.Send(httpProxyId, new NHttp::TEvHttpProxy::TEvRegisterHandler(
"/impersonate/start",
actorSystem.Register(new TImpersonateStartPageHandler(httpProxyId, settings))
)
);

actorSystem.Send(httpProxyId, new NHttp::TEvHttpProxy::TEvRegisterHandler(
"/impersonate/stop",
actorSystem.Register(new TImpersonateStopPageHandler(httpProxyId, settings))
)
);
}

actorSystem.Send(httpProxyId, new NHttp::TEvHttpProxy::TEvRegisterHandler(
"/",
actorSystem.Register(new TProtectedPageHandler(httpProxyId, settings))
Expand Down
148 changes: 148 additions & 0 deletions ydb/mvp/oidc_proxy/oidc_impersonate_start_page_nebius.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#include <library/cpp/string_utils/base64/base64.h>
#include <ydb/library/actors/http/http.h>
#include <ydb/library/security/util.h>
#include <ydb/mvp/core/mvp_log.h>
#include <ydb/mvp/core/mvp_tokens.h>
#include "openid_connect.h"
#include "oidc_session_create.h"
#include "oidc_impersonate_start_page_nebius.h"

namespace NMVP {
namespace NOIDC {

THandlerImpersonateStart::THandlerImpersonateStart(const NActors::TActorId& sender,
const NHttp::THttpIncomingRequestPtr& request,
const NActors::TActorId& httpProxyId,
const TOpenIdConnectSettings& settings)
: Sender(sender)
, Request(request)
, HttpProxyId(httpProxyId)
, Settings(settings)
{}

void THandlerImpersonateStart::Bootstrap(const NActors::TActorContext& ctx) {
LOG_DEBUG_S(ctx, EService::MVP, "Start impersonation process");
NHttp::TUrlParameters urlParameters(Request->URL);
TString serviceAccountId = urlParameters["service_account_id"];

NHttp::THeaders headers(Request->Headers);
NHttp::TCookies cookies(headers.Get("Cookie"));

TString sessionCookieName = CreateNameSessionCookie(Settings.ClientId);
TStringBuf sessionCookieValue = cookies.Get(sessionCookieName);
if (!sessionCookieValue.Empty()) {
LOG_DEBUG_S(ctx, EService::MVP, "Using session cookie (" << sessionCookieName << ": " << NKikimr::MaskTicket(sessionCookieValue) << ")");
}

TString sessionToken = DecodeToken(sessionCookieValue, ctx);

TString jsonError;
if (sessionToken.empty()) {
jsonError = "Wrong impersonate parameter: session cookie not found";
} else if (serviceAccountId.empty()) {
jsonError = "Wrong impersonate parameter: account_id not found";
}

if (jsonError) {
NHttp::THeadersBuilder responseHeaders;
responseHeaders.Set("Content-Type", "text/plain");
SetCORS(Request, &responseHeaders);
NHttp::THttpOutgoingResponsePtr httpResponse = Request->CreateResponse("400", "Bad Request", responseHeaders, jsonError);
ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse));
Die(ctx);
} else {
RequestImpersonatedToken(sessionToken, serviceAccountId, ctx);
}
}

void THandlerImpersonateStart::RequestImpersonatedToken(const TString& sessionToken, const TString& serviceAccountId, const NActors::TActorContext& ctx) {
LOG_DEBUG_S(ctx, EService::MVP, "Request impersonated token");
NHttp::THttpOutgoingRequestPtr httpRequest = NHttp::THttpOutgoingRequest::CreateRequestPost(Settings.GetImpersonateEndpointURL());
httpRequest->Set<&NHttp::THttpRequest::ContentType>("application/x-www-form-urlencoded");

TMvpTokenator* tokenator = MVPAppData()->Tokenator;
TString token = "";
if (tokenator) {
token = tokenator->GetToken(Settings.SessionServiceTokenName);
}
httpRequest->Set("Authorization", token); // Bearer included

TStringBuilder body;
body << "session=" << sessionToken
<< "&service_account_id=" << serviceAccountId;
httpRequest->Set<&NHttp::THttpRequest::Body>(body);

ctx.Send(HttpProxyId, new NHttp::TEvHttpProxy::TEvHttpOutgoingRequest(httpRequest));
Become(&THandlerImpersonateStart::StateWork);
}

void THandlerImpersonateStart::ProcessImpersonatedToken(const TString& impersonatedToken, const NActors::TActorContext& ctx) {
TString impersonatedCookieName = CreateNameImpersonatedCookie(Settings.ClientId);
TString impersonatedCookieValue = Base64Encode(impersonatedToken);
LOG_DEBUG_S(ctx, EService::MVP, "Set impersonated cookie: (" << impersonatedCookieName << ": " << NKikimr::MaskTicket(impersonatedCookieValue) << ")");

NHttp::THeadersBuilder responseHeaders;
responseHeaders.Set("Set-Cookie", CreateSecureCookie(impersonatedCookieName, impersonatedCookieValue));
SetCORS(Request, &responseHeaders);
NHttp::THttpOutgoingResponsePtr httpResponse;
httpResponse = Request->CreateResponse("200", "OK", responseHeaders);
ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse));
Die(ctx);
}

void THandlerImpersonateStart::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx) {
NHttp::THttpOutgoingResponsePtr httpResponse;
if (event->Get()->Error.empty() && event->Get()->Response) {
NHttp::THttpIncomingResponsePtr response = event->Get()->Response;
LOG_DEBUG_S(ctx, EService::MVP, "Incoming response from authorization server: " << response->Status);
if (response->Status == "200") {
TStringBuf jsonError;
NJson::TJsonValue jsonValue;
NJson::TJsonReaderConfig jsonConfig;
if (NJson::ReadJsonTree(response->Body, &jsonConfig, &jsonValue)) {
const NJson::TJsonValue* jsonImpersonatedToken;
if (jsonValue.GetValuePointer("impersonation", &jsonImpersonatedToken)) {
TString impersonatedToken = jsonImpersonatedToken->GetStringRobust();
ProcessImpersonatedToken(impersonatedToken, ctx);
return;
} else {
jsonError = "Wrong OIDC provider response: impersonated token not found";
}
} else {
jsonError = "Wrong OIDC response";
}
NHttp::THeadersBuilder responseHeaders;
responseHeaders.Set("Content-Type", "text/plain");
SetCORS(Request, &responseHeaders);
httpResponse = Request->CreateResponse("400", "Bad Request", responseHeaders, jsonError);
} else {
NHttp::THeadersBuilder responseHeaders;
NHttp::THeaders headers(response->Headers);
if (headers.Has("Content-Type")) {
responseHeaders.Set("Content-Type", headers.Get("Content-Type"));
}
SetCORS(Request, &responseHeaders);
httpResponse = Request->CreateResponse(response->Status, response->Message, responseHeaders, response->Body);
}
} else {
NHttp::THeadersBuilder responseHeaders;
responseHeaders.Set("Content-Type", "text/plain");
SetCORS(Request, &responseHeaders);
httpResponse = Request->CreateResponse("400", "Bad Request", responseHeaders, event->Get()->Error);
}
ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse));
Die(ctx);
}

TImpersonateStartPageHandler::TImpersonateStartPageHandler(const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings)
: TBase(&TImpersonateStartPageHandler::StateWork)
, HttpProxyId(httpProxyId)
, Settings(settings)
{}

void TImpersonateStartPageHandler::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, const NActors::TActorContext& ctx) {
ctx.Register(new THandlerImpersonateStart(event->Sender, event->Get()->Request, HttpProxyId, Settings));
}

} // NOIDC
} // NMVP
55 changes: 55 additions & 0 deletions ydb/mvp/oidc_proxy/oidc_impersonate_start_page_nebius.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#pragma once

#include "oidc_settings.h"
#include "context.h"

namespace NMVP {
namespace NOIDC {

class THandlerImpersonateStart : public NActors::TActorBootstrapped<THandlerImpersonateStart> {
private:
using TBase = NActors::TActorBootstrapped<THandlerImpersonateStart>;

protected:
const NActors::TActorId Sender;
const NHttp::THttpIncomingRequestPtr Request;
NActors::TActorId HttpProxyId;
const TOpenIdConnectSettings Settings;

public:
THandlerImpersonateStart(const NActors::TActorId& sender,
const NHttp::THttpIncomingRequestPtr& request,
const NActors::TActorId& httpProxyId,
const TOpenIdConnectSettings& settings);
void RequestImpersonatedToken(const TString&, const TString&, const NActors::TActorContext&);
void ProcessImpersonatedToken(const TString& impersonatedToken, const NActors::TActorContext& ctx);

void Bootstrap(const NActors::TActorContext& ctx);
void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event, const NActors::TActorContext& ctx);

STFUNC(StateWork) {
switch (ev->GetTypeRewrite()) {
HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, Handle);
}
}
};

class TImpersonateStartPageHandler : public NActors::TActor<TImpersonateStartPageHandler> {
using TBase = NActors::TActor<TImpersonateStartPageHandler>;

const NActors::TActorId HttpProxyId;
const TOpenIdConnectSettings Settings;

public:
TImpersonateStartPageHandler(const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings);
void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, const NActors::TActorContext& ctx);

STFUNC(StateWork) {
switch (ev->GetTypeRewrite()) {
HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle);
}
}
};

} // NOIDC
} // NMVP
43 changes: 43 additions & 0 deletions ydb/mvp/oidc_proxy/oidc_impersonate_stop_page_nebius.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include "openid_connect.h"
#include "oidc_session_create.h"
#include "oidc_impersonate_stop_page_nebius.h"

namespace NMVP {
namespace NOIDC {

THandlerImpersonateStop::THandlerImpersonateStop(const NActors::TActorId& sender,
const NHttp::THttpIncomingRequestPtr& request,
const NActors::TActorId& httpProxyId,
const TOpenIdConnectSettings& settings)
: Sender(sender)
, Request(request)
, HttpProxyId(httpProxyId)
, Settings(settings)
{}

void THandlerImpersonateStop::Bootstrap(const NActors::TActorContext& ctx) {
TString impersonatedCookieName = CreateNameImpersonatedCookie(Settings.ClientId);
LOG_DEBUG_S(ctx, EService::MVP, "Clear impersonated cookie: (" << impersonatedCookieName << ")");

NHttp::THeadersBuilder responseHeaders;
responseHeaders.Set("Set-Cookie", ClearSecureCookie(impersonatedCookieName));
SetCORS(Request, &responseHeaders);

NHttp::THttpOutgoingResponsePtr httpResponse;
httpResponse = Request->CreateResponse("200", "OK", responseHeaders);
ctx.Send(Sender, new NHttp::TEvHttpProxy::TEvHttpOutgoingResponse(httpResponse));
Die(ctx);
}

TImpersonateStopPageHandler::TImpersonateStopPageHandler(const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings)
: TBase(&TImpersonateStopPageHandler::StateWork)
, HttpProxyId(httpProxyId)
, Settings(settings)
{}

void TImpersonateStopPageHandler::Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, const NActors::TActorContext& ctx) {
ctx.Register(new THandlerImpersonateStop(event->Sender, event->Get()->Request, HttpProxyId, Settings));
}

} // NOIDC
} // NMVP
46 changes: 46 additions & 0 deletions ydb/mvp/oidc_proxy/oidc_impersonate_stop_page_nebius.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma once

#include "oidc_settings.h"
#include "context.h"

namespace NMVP {
namespace NOIDC {

class THandlerImpersonateStop : public NActors::TActorBootstrapped<THandlerImpersonateStop> {
private:
using TBase = NActors::TActorBootstrapped<THandlerImpersonateStop>;

protected:
const NActors::TActorId Sender;
const NHttp::THttpIncomingRequestPtr Request;
NActors::TActorId HttpProxyId;
const TOpenIdConnectSettings Settings;

public:
THandlerImpersonateStop(const NActors::TActorId& sender,
const NHttp::THttpIncomingRequestPtr& request,
const NActors::TActorId& httpProxyId,
const TOpenIdConnectSettings& settings);

void Bootstrap(const NActors::TActorContext& ctx);
};

class TImpersonateStopPageHandler : public NActors::TActor<TImpersonateStopPageHandler> {
using TBase = NActors::TActor<TImpersonateStopPageHandler>;

const NActors::TActorId HttpProxyId;
const TOpenIdConnectSettings Settings;

public:
TImpersonateStopPageHandler(const NActors::TActorId& httpProxyId, const TOpenIdConnectSettings& settings);
void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingRequest::TPtr event, const NActors::TActorContext& ctx);

STFUNC(StateWork) {
switch (ev->GetTypeRewrite()) {
HFunc(NHttp::TEvHttpProxy::TEvHttpIncomingRequest, Handle);
}
}
};

} // NOIDC
} // NMVP
Loading