Skip to content

Commit

Permalink
Merge pull request #35 from smhmhmd/mainline
Browse files Browse the repository at this point in the history
Add Domainless gMSA
  • Loading branch information
smhmhmd authored Oct 27, 2022
2 parents 5d524ed + 45d3321 commit 999cff0
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 23 deletions.
39 changes: 27 additions & 12 deletions api/src/gmsa_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class CredentialsFetcherImpl final
* @param cf_logger : log to systemd
*/
void RunServer( std::string unix_socket_dir, std::string krb_files_dir,
creds_fetcher::CF_logger& cf_logger )
creds_fetcher::CF_logger& cf_logger, std::string aws_sm_secret_name )
{
std::string unix_socket_address =
std::string( "unix:" ) + unix_socket_dir + "/" + std::string( UNIX_SOCKET_NAME );
Expand All @@ -52,7 +52,7 @@ class CredentialsFetcherImpl final
std::cout << "Server listening on " << server_address << std::endl;

// Proceed to the server's main loop.
HandleRpcs( krb_files_dir, cf_logger );
HandleRpcs( krb_files_dir, cf_logger, aws_sm_secret_name );
}

private:
Expand All @@ -78,7 +78,8 @@ class CredentialsFetcherImpl final
Proceed();
}

void Proceed( std::string krb_files_dir, creds_fetcher::CF_logger& cf_logger )
void Proceed( std::string krb_files_dir, creds_fetcher::CF_logger& cf_logger,
std::string aws_sm_secret_name )
{
if ( cookie.compare( CLASS_NAME_CallDataCreateKerberosLease ) != 0 )
{
Expand Down Expand Up @@ -146,7 +147,16 @@ class CredentialsFetcherImpl final
for ( auto krb_ticket : krb_ticket_info_list )
{
// invoke to get machine ticket
int status = get_machine_krb_ticket( krb_ticket->domain_name, cf_logger );
int status = 0;
if ( aws_sm_secret_name.length() != 0 )
{
status = get_user_krb_ticket( krb_ticket->domain_name,
aws_sm_secret_name, cf_logger );
}
else
{
status = get_machine_krb_ticket( krb_ticket->domain_name, cf_logger );
}
if ( status < 0 )
{
cf_logger.logger( LOG_ERR, "Error %d: Cannot get machine krb ticket",
Expand Down Expand Up @@ -311,6 +321,8 @@ class CredentialsFetcherImpl final
{
public:
std::string cookie;
std::string aws_sm_secret_name;

#define CLASS_NAME_CallDataDeleteKerberosLease "CallDataDeleteKerberosLease"
// Take in the "service" instance (in this case representing an asynchronous
// server) and the completion queue "cq" used for asynchronous communication
Expand All @@ -328,7 +340,8 @@ class CredentialsFetcherImpl final
Proceed();
}

void Proceed( std::string krb_files_dir, creds_fetcher::CF_logger& cf_logger )
void Proceed( std::string krb_files_dir, creds_fetcher::CF_logger& cf_logger,
std::string aws_sm_secret_name )
{
if ( cookie.compare( CLASS_NAME_CallDataDeleteKerberosLease ) != 0 )
{
Expand Down Expand Up @@ -479,7 +492,8 @@ class CredentialsFetcherImpl final
};

// This can be run in multiple threads if needed.
void HandleRpcs( std::string krb_files_dir, creds_fetcher::CF_logger& cf_logger )
void HandleRpcs( std::string krb_files_dir, creds_fetcher::CF_logger& cf_logger,
std::string aws_sm_secret_name )
{
void* got_tag; // uniquely identifies a request.
bool ok;
Expand All @@ -498,10 +512,10 @@ class CredentialsFetcherImpl final
GPR_ASSERT( cq_->Next( &got_tag, &ok ) );
GPR_ASSERT( ok );

static_cast<CallDataCreateKerberosLease*>( got_tag )->Proceed( krb_files_dir,
cf_logger );
static_cast<CallDataDeleteKerberosLease*>( got_tag )->Proceed( krb_files_dir,
cf_logger );
static_cast<CallDataCreateKerberosLease*>( got_tag )->Proceed( krb_files_dir, cf_logger,
aws_sm_secret_name );
static_cast<CallDataDeleteKerberosLease*>( got_tag )->Proceed( krb_files_dir, cf_logger,
aws_sm_secret_name );
}
}

Expand All @@ -518,13 +532,14 @@ class CredentialsFetcherImpl final
* @return - return 0 when server exits
*/
int RunGrpcServer( std::string unix_socket_dir, std::string krb_files_dir,
creds_fetcher::CF_logger& cf_logger, volatile sig_atomic_t* shutdown_signal )
creds_fetcher::CF_logger& cf_logger, volatile sig_atomic_t* shutdown_signal,
std::string aws_sm_secret_name )
{
CredentialsFetcherImpl creds_fetcher_grpc;

pthread_shutdown_signal = shutdown_signal;

creds_fetcher_grpc.RunServer( unix_socket_dir, krb_files_dir, cf_logger );
creds_fetcher_grpc.RunServer( unix_socket_dir, krb_files_dir, cf_logger, aws_sm_secret_name );

// TBD:: Add return status for errors
return 0;
Expand Down
77 changes: 77 additions & 0 deletions auth/kerberos/src/krb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

static const std::string install_path_for_decode_exe =
"/usr/sbin/credentials_fetcher_utf16_private.exe";
static const std::string install_path_for_aws_cli = "/usr/bin/aws";

/**
* Check if binary is writable other than root
Expand Down Expand Up @@ -172,6 +173,82 @@ int get_machine_krb_ticket( std::string domain_name, creds_fetcher::CF_logger& c
return result.first;
}

/**
* This function generates kerberos ticket with user credentials
* User credentials must have adequate privileges to read gMSA passwords
* This is an alternative to the machine credentials approach above
* @param cf_daemon - parent daemon object
* @return error-code - 0 if successful
*/
int get_user_krb_ticket( std::string domain_name, std::string aws_sm_secret_name,
creds_fetcher::CF_logger& cf_logger )
{
std::pair<int, std::string> result;

std::pair<int, std::string> cmd = exec_shell_cmd( "which hostname" );
rtrim( cmd.second );
if ( !check_file_permissions( cmd.second ) )
{
return -1;
}

cmd = exec_shell_cmd( "which realm" );
rtrim( cmd.second );
if ( !check_file_permissions( cmd.second ) )
{
return -1;
}

cmd = exec_shell_cmd( "which kinit" );
rtrim( cmd.second );
if ( !check_file_permissions( cmd.second ) )
{
return -1;
}

cmd = exec_shell_cmd( "which ldapsearch" );
rtrim( cmd.second );
if ( !check_file_permissions( cmd.second ) )
{
return -1;
}

if ( !check_file_permissions( install_path_for_decode_exe ) )
{
return -1;
}

if ( !check_file_permissions( install_path_for_aws_cli ) )
{
return -1;
}

std::string command =
install_path_for_aws_cli + std::string( " secretsmanager get-secret-value --secret-id " ) + aws_sm_secret_name + " --query 'SecretString' --output text";
// /usr/bin/aws secretsmanager get-secret-value --secret-id aws/directoryservices/d-xxxxxxxxxx/gmsa --query 'SecretString' --output text
result = exec_shell_cmd( command );

// deserialize json to krb_ticket_info object
namespace pt = boost::property_tree;
pt::ptree root;
// {"username":"user","password":"passw0rd"}
std::stringstream ss;
ss << result.second;
pt::read_json( ss, root );
std::string username = root.get<std::string>( "username" );
std::string password = root.get<std::string>( "password" );

std::transform( domain_name.begin(), domain_name.end(), domain_name.begin(),
[]( unsigned char c ) { return std::toupper( c ); } );
std::string kinit_cmd = "echo " + password + " | kinit -V " + username + "@" + domain_name;
username = "xxxx";
password = "xxxx";
result = exec_shell_cmd( kinit_cmd );
kinit_cmd = "xxxx";

return result.first;
}

/**
* base64_decode - Decodes base64 encoded string
* @param password - base64 encoded password
Expand Down
10 changes: 7 additions & 3 deletions common/daemon.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "config.h"
#include <boost/algorithm/string.hpp>
#include <boost/program_options.hpp>
#include <boost/property_tree/json_parser.hpp>
Expand All @@ -16,7 +17,6 @@
#include <thread>
#include <unistd.h>
#include <vector>
#include "config.h"

#ifndef _daemon_h_
#define _daemon_h_
Expand Down Expand Up @@ -81,6 +81,7 @@ namespace creds_fetcher
std::string gmsa_account_name;
CF_logger cf_logger;
bool run_diagnostic = false;
std::string aws_sm_secret_name; /* TBD:: Extend to other secret stores */
// run ticket renewal every 10 minutes
uint64_t krb_ticket_handle_interval = 10;
volatile sig_atomic_t got_systemd_shutdown_signal;
Expand Down Expand Up @@ -111,6 +112,8 @@ namespace creds_fetcher
int generate_host_machine_krb_ticket( const char* krb_ccname = "" );

int get_machine_krb_ticket( std::string domain_name, creds_fetcher::CF_logger& cf_logger );
int get_user_krb_ticket( std::string domain_name, std::string aws_sm_secret_name,
creds_fetcher::CF_logger& cf_logger );

std::pair<int, std::string> get_gmsa_krb_ticket( std::string domain_name,
const std::string& gmsa_account_name,
Expand Down Expand Up @@ -149,7 +152,8 @@ int parse_config_file( creds_fetcher::Daemon& cf_daemon );
* Methods in api module
*/
int RunGrpcServer( std::string unix_socket_dir, std::string krb_file_path,
creds_fetcher::CF_logger& cf_logger, volatile sig_atomic_t* shutdown_signal );
creds_fetcher::CF_logger& cf_logger, volatile sig_atomic_t* shutdown_signal,
std::string aws_sm_secret_name );

int parse_cred_spec( std::string credspec_data, creds_fetcher::krb_ticket_info* krb_ticket_info );

Expand All @@ -163,7 +167,7 @@ int krb_ticket_renew_handler( creds_fetcher::Daemon cf_daemon );
/**
* Methods in metadata module
*/
bool contains_invalid_characters(const std::string & path);
bool contains_invalid_characters( const std::string& path );
std::list<creds_fetcher::krb_ticket_info*> read_meta_data_json( std::string file_path );

int write_meta_data_json( std::list<creds_fetcher::krb_ticket_info*> krb_ticket_info_list,
Expand Down
37 changes: 35 additions & 2 deletions config/src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,24 @@
* @param cf_daemon - credentials fetcher parent object
* @return status - 0 if successful
*/

int parse_options( int argc, const char* argv[], creds_fetcher::Daemon& cf_daemon )
{
const char* ecs_config_file_name = "/etc/ecs/ecs.config"; // TBD:: Add commandline if needed
std::string domainless_gmsa_field( "CREDENTIALS_FETCHER_SECRET_NAME_FOR_DOMAINLESS_GMSA" );

try
{
namespace po = boost::program_options;

/* Declare the supported options */
po::options_description desc( "Allowed options" );
desc.add_options()( "help", "produce help message" ) /* TBD: Add help message description */
("self_test", "Run tests such as utf16 decode" )
("verbosity", po::value<int>(), "set verbosity level" );
( "self_test", "Run tests such as utf16 decode" )( "verbosity", po::value<int>(),
"set verbosity level" )(
"aws_sm_secret_name", po::value<std::string>(), // TBD:: Extend to other stores
"Name of secret containing username/password in AWS Secrets Manager (in same "
"region)" );

/**
* Calls to store, parse_command_line and notify functions
Expand All @@ -44,6 +51,32 @@ int parse_options( int argc, const char* argv[], creds_fetcher::Daemon& cf_daemo
std::cout << "run diagnostic set" << std::endl;
cf_daemon.run_diagnostic = true;
}

if ( vm.count( "aws_sm_secret_name" ) ) // TBD:: Extend to other stores
{
cf_daemon.aws_sm_secret_name = vm["aws_sm_secret_name"].as<std::string>();
std::cout
<< "Option selected for domainless operation, AWS secrets manager secret-name = "
<< cf_daemon.aws_sm_secret_name << std::endl;
}

std::ifstream config_file( ecs_config_file_name );
std::string line;
std::vector<std::string> results;

while ( std::getline( config_file, line ) )
{
// TBD: Error handling for incorrectly formatted /etc/ecs/ecs.config
boost::split( results, line, []( char c ) { return c == '='; } );
std::string key = results[0];
std::string value = results[1];
if ( domainless_gmsa_field.compare( key ) == 0 )
{
value.erase( std::remove( value.begin(), value.end(), '"' ), value.end() );
std::cout << "Using " << value << " for domainless gMSA" << std::endl;
cf_daemon.aws_sm_secret_name = value;
}
}
}
catch ( const boost::program_options::error& ex )
{
Expand Down
12 changes: 7 additions & 5 deletions daemon/src/daemon.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#include "daemon.h"
#include <iostream>
#include <libgen.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <libgen.h>

creds_fetcher::Daemon cf_daemon;

Expand Down Expand Up @@ -46,7 +46,7 @@ void* grpc_thread_start( void* arg )
tinfo->argv_string );

RunGrpcServer( cf_daemon.unix_socket_dir, cf_daemon.krb_files_dir, cf_daemon.cf_logger,
&cf_daemon.got_systemd_shutdown_signal );
&cf_daemon.got_systemd_shutdown_signal, cf_daemon.aws_sm_secret_name );

return tinfo->argv_string;
}
Expand Down Expand Up @@ -152,7 +152,7 @@ int main( int argc, const char* argv[] )
/**
* Domain name and gmsa account are usually set in APIs.
* The options below can be used as a test.
*/
*/
cf_daemon.domain_name = CF_TEST_DOMAIN_NAME;
cf_daemon.gmsa_account_name = CF_TEST_GMSA_ACCOUNT;

Expand Down Expand Up @@ -187,7 +187,8 @@ int main( int argc, const char* argv[] )
create_pthread( grpc_thread_start, grpc_thread_name, -1 );
if ( pthread_status.first < 0 )
{
cf_daemon.cf_logger.logger( LOG_ERR, "Error %d: Cannot create pthreads", pthread_status.first );
cf_daemon.cf_logger.logger( LOG_ERR, "Error %d: Cannot create pthreads",
pthread_status.first );
exit( EXIT_FAILURE );
}
grpc_pthread = pthread_status.second;
Expand All @@ -198,7 +199,8 @@ int main( int argc, const char* argv[] )
create_pthread( refresh_krb_tickets_thread_start, "krb_ticket_refresh_thread", -1 );
if ( pthread_status.first < 0 )
{
cf_daemon.cf_logger.logger( LOG_ERR, "Error %d: Cannot create pthreads", pthread_status.first );
cf_daemon.cf_logger.logger( LOG_ERR, "Error %d: Cannot create pthreads",
pthread_status.first );
exit( EXIT_FAILURE );
}
krb_refresh_pthread = pthread_status.second;
Expand Down
4 changes: 3 additions & 1 deletion package/credentials-fetcher.spec
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ BuildRequires: cmake3 make chrpath openldap-clients grpc-devel gcc-c++ glib2-de
BuildRequires: openssl-devel zlib-devel protobuf-devel re2-devel krb5-devel systemd-devel
BuildRequires: systemd-rpm-macros dotnet grpc-plugins

Requires: bind-utils openldap openldap-clients
Requires: bind-utils openldap openldap-clients awscli

# No one likes you i686
ExcludeArch: i686 armv7hl ppc64le
Expand Down Expand Up @@ -62,6 +62,8 @@ ctest3
%attr(0700, -, -) %{_sbindir}/credentials_fetcher_utf16_private.runtimeconfig.json

%changelog
* Mon Oct 24 2022 Samiullah Mohammed <samiull@amazon.com> - 1.0.0
- Add domainless gmsa
* Wed Oct 12 2022 Sai Kiran Akula <saakla@amazon.com> - 1.0.0
- Create 1.0 release
* Mon Sep 19 2022 Tom Callaway <spotaws@amazon.com> - 0.0.94-2
Expand Down

0 comments on commit 999cff0

Please sign in to comment.