in api/src/gmsa_service.cpp [340:696]
void Proceed( std::string krb_files_dir, CF_logger& cf_logger,
std::string aws_sm_secret_name )
{
if ( cookie.compare( CLASS_NAME_CallDataCreateKerberosArnLease ) != 0 )
{
return;
}
// Note: This code-path is only for Fargate
std::cerr << Util::getCurrentTime() << '\t' << "INFO: CallDataCreateKerberosArnLease "
<< this << "status: " << status_ << std::endl;
if ( status_ == CREATE )
{
// Make this instance progress to the PROCESS state.
status_ = PROCESS;
// As part of the initial CREATE state, we *request* that the system
// start processing RequestAddKerberosLease requests. In this request, "this" acts
// are the tag uniquely identifying the request (so that different CallData
// instances can serve different requests concurrently), in this case
// the memory address of this CallData instance.
service_->RequestAddKerberosArnLease( &add_krb_ctx_, &create_arn_krb_request_,
&create_arn_krb_responder_, cq_, cq_, this );
}
else if ( status_ == PROCESS )
{
// Spawn a new CallData instance to serve new clients while we process
// the one for this CallData. The instance will deallocate itself as
// part of its FINISH state.
new CallDataCreateKerberosArnLease( service_, cq_ );
// The actual processing.
std::string lease_id = "";
std::list<krb_ticket_info_t*> krb_ticket_info_list;
std::list<krb_ticket_arn_mapping_t*> krb_ticket_arn_mapping_list;
std::unordered_set<std::string> krb_ticket_dirs;
std::string accessId = create_arn_krb_request_.access_key_id();
std::string secretKey = create_arn_krb_request_.secret_access_key();
std::string sessionToken = create_arn_krb_request_.session_token();
std::string region = create_arn_krb_request_.region();
std::string username = "";
std::string password = "";
std::string domain = "";
std::string distinguished_name = "";
bool isTest = false;
std::string err_msg;
int credspecSize = create_arn_krb_request_.credspec_arns_size();
if ( !accessId.empty() && !secretKey.empty() && !sessionToken.empty() &&
!region.empty() && credspecSize > 0 )
{
for ( int i = 0; i < create_arn_krb_request_.credspec_arns_size(); i++ )
{
krb_ticket_info_t* krb_ticket_info = new krb_ticket_info_t;
krb_ticket_arn_mapping_t* krb_ticket_arns = new krb_ticket_arn_mapping_t;
std::string credspecarn = create_arn_krb_request_.credspec_arns( i );
if ( credspecarn.empty() )
{
err_msg = "ERROR: credentialspec arn should not be empty";
std::cerr << Util::getCurrentTime() << '\t' << err_msg << std::endl;
break;
}
std::vector<std::string> results =
Util::split_string( create_arn_krb_request_.credspec_arns( i ), '#' );
if ( results.size() != 2 )
{
err_msg = "ERROR: credentialspec arn is not valid";
std::cerr << Util::getCurrentTime() << '\t' << err_msg << std::endl;
break;
}
std::vector<std::string> pathResults =
Util::split_string( results[1], '/' );
if ( pathResults.size() != 2 ||
contains_invalid_characters_in_credentials( results[1] ) )
{
err_msg = "ERROR: mount path is invalid";
std::cerr << Util::getCurrentTime() << '\t' << err_msg << std::endl;
break;
}
isTest = IsTestInvocationForUnitTests( results[0] );
if ( !isTest )
{
// get credentialspec contents:
Aws::Auth::AWSCredentials creds =
get_credentials( accessId, secretKey, sessionToken );
bool isObjectValid =
check_file_size_s3( results[0], region, creds, false );
if ( !isObjectValid )
{
err_msg = "ERROR: invalid object for credentialspec in s3";
std::cerr << Util::getCurrentTime() << '\t' << err_msg << std::endl;
break;
}
std::string response =
retrieve_credspec_from_s3( results[0], region, creds, false );
if ( response.empty() )
{
err_msg = "ERROR: credentialspec cannot be retrieved from s3";
std::cerr << Util::getCurrentTime() << '\t' << err_msg << std::endl;
break;
}
krb_ticket_arns->credential_spec_arn = results[0];
int parse_result = parse_cred_spec_domainless(
response, krb_ticket_info, krb_ticket_arns );
if ( parse_result != 0 )
{
err_msg = "ERROR: invalid credentialspec fields";
std::cerr << Util::getCurrentTime() << '\t' << err_msg << std::endl;
break;
}
// only add the ticket info if the parsing is successful
if ( parse_result == 0 )
{
std::string secretsArn =
krb_ticket_arns->credential_domainless_user_arn;
if ( secretsArn.empty() )
{
err_msg = "ERROR: invalid secrets manager arn";
std::cerr << Util::getCurrentTime() << '\t' << err_msg
<< std::endl;
break;
}
// retrieve domainless user credentials
std::tuple<std::string, std::string, std::string, std::string>
userCreds = retrieve_credspec_from_secrets_manager(
krb_ticket_arns->credential_domainless_user_arn, region,
creds );
username = std::get<0>( userCreds );
password = std::get<1>( userCreds );
domain = std::get<2>( userCreds );
distinguished_name = std::get<3>( userCreds );
if ( isValidDomain( domain ) &&
!Util::contains_invalid_characters_in_ad_account_name( username ) )
{
if ( !username.empty() && !password.empty() &&
!domain.empty() &&
username.length() < INPUT_CREDENTIALS_LENGTH &&
password.length() < INPUT_CREDENTIALS_LENGTH &&
domain.length() < DOMAIN_LENGTH )
{
std::string krb_files_path =
krb_files_dir + "/" + results[1];
std::vector<std::string> mountpath =
Util::split_string( results[1], '/' );
// get taskid information
lease_id = mountpath[0];
krb_ticket_info->krb_file_path = krb_files_path;
krb_ticket_info->domainless_user = username;
krb_ticket_arns->krb_file_path = krb_files_path;
krb_ticket_info->distinguished_name = distinguished_name;
// handle duplicate service accounts
if ( !krb_ticket_dirs.count( krb_files_path ) )
{
krb_ticket_dirs.insert( krb_files_path );
krb_ticket_info_list.push_back( krb_ticket_info );
krb_ticket_arn_mapping_list.push_back(
krb_ticket_arns );
}
else
{
err_msg = "ERROR: found duplicate mount paths";
std::cerr << Util::getCurrentTime() << '\t' << err_msg
<< std::endl;
break;
}
}
else
{
err_msg =
"ERROR: domainless AD user credentials is not valid/ "
"credentials should not be more than 256 charaters";
std::cerr << Util::getCurrentTime() << '\t' << err_msg
<< std::endl;
break;
}
}
else
{
err_msg = "ERROR: invalid domainName/username";
std::cerr << Util::getCurrentTime() << '\t' << err_msg
<< std::endl;
break;
}
}
}
else
{
std::string krb_files_path = krb_files_dir + "/" + results[1];
std::vector<std::string> mountpath =
Util::split_string( results[1], '/' );
// get taskid information
lease_id = mountpath[0];
std::filesystem::create_directories( krb_files_path );
std::string dummyFile = krb_files_path + "/krb5cc";
std::ofstream o( dummyFile );
}
}
}
else
{
err_msg = "Error: access credentials should not be empty";
std::cerr << Util::getCurrentTime() << '\t' << err_msg << std::endl;
}
create_arn_krb_reply_.set_lease_id( lease_id );
if ( err_msg.empty() && !isTest )
{
// create the kerberos tickets for the service accounts
for ( auto krb_ticket : krb_ticket_info_list )
{
// invoke to get machine ticket
std::pair<int, std::string> status;
if ( username.empty() || password.empty() )
{
std::string log_message =
"Invalid credentials for domainless user " + username;
cf_logger.logger( LOG_ERR, log_message.c_str() );
err_msg = "ERROR: Invalid credentials for domainless user";
std::cerr << Util::getCurrentTime() << '\t' << err_msg << std::endl;
break;
}
status = Util::generate_krb_ticket_using_username_and_password(
domain, username, password, cf_logger );
if ( status.first < 0 )
{
err_msg = "ERROR :" + std::to_string( status.first ) +
": Cannot retrieve domainless user kerberos tickets";
std::string log_message = err_msg +
" Status: " + std::to_string( status.first ) +
" " + status.second;
cf_logger.logger( LOG_ERR, log_message.c_str() );
std::cerr << Util::getCurrentTime() << '\t' << err_msg << std::endl;
break;
}
std::string krb_file_path = krb_ticket->krb_file_path;
std::filesystem::create_directories( krb_file_path );
std::string krb_ccname_str = krb_ticket->krb_file_path + "/krb5cc";
if ( !std::filesystem::exists( krb_ccname_str ) )
{
std::ofstream file( krb_ccname_str );
file.close();
krb_ticket->krb_file_path = krb_ccname_str;
}
std::pair<int, std::string> gmsa_ticket_result =
fetch_gmsa_password_and_create_krb_ticket( domain, krb_ticket,
krb_ccname_str, cf_logger );
if ( gmsa_ticket_result.first != 0 )
{
err_msg = "ERROR: " + std::to_string( status.first ) +
": Cannot get gMSA krb ticket";
std::cerr << Util::getCurrentTime() << '\t' << err_msg.c_str()
<< std::endl;
cf_logger.logger( LOG_ERR, err_msg.c_str() );
break;
}
else
{
std::string log_message =
"gMSA ticket is at " + gmsa_ticket_result.second;
cf_logger.logger( LOG_INFO, log_message.c_str() );
std::cerr << Util::getCurrentTime() << '\t'
<< "INFO: gMSA ticket is "
"created"
<< std::endl;
}
}
}
// And we are done! Let the gRPC runtime know we've finished, using the
// memory address of this instance as the uniquely identifying tag for
// the event.
if ( !err_msg.empty() && !isTest )
{
secureClearString( username );
secureClearString( password );
secureClearString( accessId );
secureClearString( sessionToken );
secureClearString( secretKey );
// remove the directories on failure
for ( auto krb_ticket : krb_ticket_info_list )
{
std::filesystem::remove_all( krb_ticket->krb_file_path );
}
status_ = FINISH;
create_arn_krb_responder_.Finish(
create_arn_krb_reply_, grpc::Status( grpc::StatusCode::INTERNAL, err_msg ),
this );
}
else
{
if ( !isTest )
{
for ( auto arn_mapping : krb_ticket_arn_mapping_list )
{
credentialsfetcher::KerberosTicketArnResponse krb_ticket_response;
krb_ticket_response.set_credspec_arns(
arn_mapping->credential_spec_arn );
krb_ticket_response.set_created_kerberos_file_paths(
arn_mapping->krb_file_path );
create_arn_krb_reply_.add_krb_ticket_response_map()->CopyFrom(
krb_ticket_response );
}
secureClearString( username );
secureClearString( password );
secureClearString( accessId );
secureClearString( sessionToken );
secureClearString( secretKey );
// write the ticket information to meta data file
write_meta_data_json( krb_ticket_info_list, lease_id, krb_files_dir );
}
status_ = FINISH;
create_arn_krb_responder_.Finish( create_arn_krb_reply_, grpc::Status::OK,
this );
}
}
else
{
GPR_ASSERT( status_ == FINISH );
// Once in the FINISH state, deallocate ourselves (CallData).
delete this;
}
return;
}