blob: 064cbce46d73eab04876fd5fcd71801434d25271 [file] [edit]
#include "bmc/auth.h"
#include <cstdlib>
#include <filesystem> // NOLINT(build/c++17)
#include <fstream>
#include <ios>
#include <string>
#include <system_error> // NOLINT(build/c++11)
#include "file/base/path.h"
#include "gmi/machine_identity.pb.h"
#include "net/proto2/contrib/parse_proto/parse_text_proto.h"
#include "bmc/daemon_context_bmc.h"
#include "proto_reader.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
namespace auth {
namespace {
using ::security_prodid::GoogleMachineIdentityProto;
using google::protobuf::contrib::parse_proto::ParseTextProtoOrDie;
using ::safepower_agent::DaemonContextBMC;
constexpr absl::string_view test_root_cert = R"pem(
-----BEGIN CERTIFICATE-----
MIIDiDCCAnCgAwIBAgIIdwMbYuGm3akwDQYJKoZIhvcNAQELBQAwRTEXMBUGA1UE
ChMOR29vZ2xlIFRFU1RJTkcxKjAoBgNVBAMMIUdvb2dsZSBCTUNXZWIgKipUZXN0
aW5nKiogUm9vdCBDQTAgFw03MDAxMDEwMDAwMDBaGA8yMTI1MDEwMTAwMDAwMFow
RTEXMBUGA1UEChMOR29vZ2xlIFRFU1RJTkcxKjAoBgNVBAMMIUdvb2dsZSBCTUNX
ZWIgKipUZXN0aW5nKiogUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAKprOCLzYyqb6Mxo+I3n70P32PQLtUQdgjDXHF2cpwI0w3ezBYn4Dx0H
T7RCRvMMlxnAcRPCTBcU4I+7HnUX+mBZvFkJPJukttCOS3usC6GtD8UMNassFkSL
Vepy5AV1cwXweklebe9gAgC8NsFetqbHz12jTGUfngYDdqhg2haiTTwXyoal+575
c6F2S5krWMnBcm9/bhQufi8nC8AzQ2r1e4/bSe64F+vXzsLODsGs7mtzXeEDB2/N
7pEhQbXmkuBudXtE5CpLwvNa1Y3XLZfXXewlzlxjqQarihEolv6e/XOAJma3XMh8
Ip5QE1F/YvX3PICAbjsaX4wmWNTv7tECAwEAAaN6MHgwDgYDVR0PAQH/BAQDAgIE
MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAPBgNVHRMBAf8EBTADAQH/
MBkGA1UdDgQSBBCuOIbUVxFjIbMIYjmL68mVMBsGA1UdIwQUMBKAEK44htRXEWMh
swhiOYvryZUwDQYJKoZIhvcNAQELBQADggEBAAZ7KtkYl4CLzPTLUhSiisckk+9K
/dbiG9TjRoiD8iG2j6/lTtZr0dkEq2xgz7kfHTbINzDONn45ea+BojAKSgWcwjzj
TvTX0lubQcTLNBHVAZQ8Ws/7QN2+WF6QPpKWzk44O1Lw1JLhaLSe4GauIQICt0P5
zUVrgX2EP1FdbS/PxgQvwM3gbHJh7iqWr7ASVypLzA5+YfuPRIJAEOimQbrs/0Gq
UEYO7y3ijBKwLs0lm6rCzCjwacWb/tFIJK4FNTg7iTWs2t94HWopoZqp0mouAERe
qOFbsHcmv8mSUfig0AaTorZQpS8htNtcsCl5HhNgAyCQh+QzvBvesrEz5Cw=
-----END CERTIFICATE-----
)pem";
constexpr absl::string_view gmi_proto_str = R"pb(
fqdn: "test.prod.google.com"
serial_number: 1234
)pb";
GoogleMachineIdentityProto GetGmiProto() {
std::string gmi_proto_str_local = std::string(gmi_proto_str);
return ParseTextProtoOrDie(gmi_proto_str_local);
}
TEST(auth_test, WorkingCredsSuccess) {
DaemonContextBMC context;
// create the test directory in the undeclared outputs directory, so the files
// are in the artifacts for the test.
const char* undeclared_outputs_dir = getenv("TEST_UNDECLARED_OUTPUTS_DIR");
std::string test_dir = file::JoinPath(undeclared_outputs_dir,
"working_creds_test");
std::filesystem::create_directory(test_dir);
// create the file paths
std::string symlink_path = file::JoinPath(test_dir, "symlink");
std::string loas3_cert_path = file::JoinPath(test_dir, "loas3_cert");
std::string self_signed_cert_path
= file::JoinPath(test_dir, "self_signed_cert");
std::string gmi_file_path
= file::JoinPath(test_dir, "gmi_file");
std::string trust_bundle_file_path
= file::JoinPath(test_dir, "trust_bundle_file");
// create the gmi file
GoogleMachineIdentityProto gmi = GetGmiProto();
EXPECT_OK(proto_reader::WriteProto(gmi_file_path, gmi));
// create the root cert file
std::ofstream out(trust_bundle_file_path,
std::ios::out | std::ios::binary | std::ios::trunc);
out << test_root_cert;
out.flush();
out.close();
// create the self signed cert
AuthManager auth_manager(symlink_path, loas3_cert_path,
self_signed_cert_path, gmi_file_path, trust_bundle_file_path,
absl::Milliseconds(100), "");
auto creds = auth_manager.GetCredsInfo();
EXPECT_OK(creds.status()) << "call to GetCredsInfo failed";
// the symlink should point to the self signed cert, because the loas3 cert
// does not exist.
std::error_code file_error;
std::string symlink_target =
std::filesystem::read_symlink(symlink_path, file_error);
ASSERT_EQ(file_error, std::error_code()) << "unable to read symlink";
ASSERT_EQ(symlink_target,
self_signed_cert_path) << "symlink not pointing to self signed cert";
// wait 200ms s to allow the scan to not see the loas3 cert.
DaemonContextBMC::Get().get_io_context().run_for(
absl::ToChronoMilliseconds(absl::Milliseconds(200)));
// add the loas3 cert (really just copy the self signed cert)
std::filesystem::copy(self_signed_cert_path, loas3_cert_path);
ASSERT_EQ(std::filesystem::exists(loas3_cert_path), true)
<< "loas3 cert file not copied";
// wait 200ms to allow the scan to see the loas3 cert.
DaemonContextBMC::Get().get_io_context().run_for(
absl::ToChronoMilliseconds(absl::Milliseconds(200)));
// the symlink should point to the loas3 cert, because the loas3 cert exists.
std::string symlink_target_second =
std::filesystem::read_symlink(symlink_path, file_error);
ASSERT_EQ(file_error, std::error_code()) << "unable to read symlink";
ASSERT_EQ(symlink_target_second, loas3_cert_path)
<< "symlink not pointing to loas3 cert";
auto provider = auth_manager.GetProvider();
ASSERT_OK(provider.status()) << "call to GetProvider failed";
// validate the provider is able to load the loas3 cert.
EXPECT_OK((*provider)->ValidateCredentials())
<< "call to ValidateCredentials failed";
}
TEST(auth_test, NoGmiFileUnableToCreateSelfSignedCertFails) {
DaemonContextBMC context;
std::string test_dir = file::JoinPath(testing::TempDir(),
"no_gmi_file_test");
std::filesystem::create_directory(test_dir);
// create the file paths
std::string symlink_path = file::JoinPath(test_dir, "symlink");
std::string loas3_cert_path = file::JoinPath(test_dir, "loas3_cert");
std::string self_signed_cert_path =
file::JoinPath(test_dir, "self_signed_cert");
std::string gmi_file_path = file::JoinPath(test_dir, "gmi_file");
std::string trust_bundle_file_path =
file::JoinPath(test_dir, "trust_bundle_file");
// call the get creds info with a short scan interval
// self signed certs can not be crated without the gmi file
AuthManager auth_manager(symlink_path, loas3_cert_path,
self_signed_cert_path, gmi_file_path, trust_bundle_file_path,
absl::Milliseconds(100), "");
auto creds = auth_manager.GetCredsInfo();
EXPECT_OK(creds.status()) << "call to GetCredsInfo failed";
// the symlink will still point to the self signed cert, because the loas3
// cert does not exist.
std::error_code file_error;
std::string value = std::filesystem::read_symlink(symlink_path, file_error);
EXPECT_EQ(file_error, std::error_code()) << "unable to read symlink";
EXPECT_EQ(value,
self_signed_cert_path) << "symlink not pointing to self signed cert";
// add loas3 cert, and validate the symlink is updated.
// copy the self signed cert to the loas3 cert path.
std::ofstream ofs(loas3_cert_path, std::ios_base::app);
ofs.close();
EXPECT_EQ(std::filesystem::exists(loas3_cert_path), true)
<< "loas3 cert file not copied";
DaemonContextBMC::Get().get_io_context().run_for(
absl::ToChronoMilliseconds(absl::Milliseconds(400)));
EXPECT_EQ(std::filesystem::read_symlink(symlink_path),
loas3_cert_path) << "symlink not pointing to loas3 cert";
}
TEST(auth_test, UnableToCreateSymlinkFails) {
DaemonContextBMC context;
std::string test_dir = file::JoinPath(testing::TempDir(),
"replace_symlink_test");
std::filesystem::create_directory(test_dir);
std::string symlink_path = "/not/a/valid/path/symlink";
std::string self_signed_cert_path
= file::JoinPath(test_dir, "new_target_path");
std::string loas3_cert_path = file::JoinPath(test_dir, "old_target_path");
// create an empty file at the loas3 cert path, so the symlink is created.
std::ofstream ofs(loas3_cert_path, std::ios_base::app);
ofs.close();
EXPECT_EQ(std::filesystem::exists(loas3_cert_path), true)
<< "loas3 cert file not created";
AuthManager auth_manager(symlink_path, loas3_cert_path,
self_signed_cert_path, "", "", absl::Milliseconds(100), "");
EXPECT_OK(auth_manager.CreateCertsSymlinkUpdate());
DaemonContextBMC::Get().get_io_context().run_for(
absl::ToChronoMilliseconds(absl::Milliseconds(200)));
}
} // namespace
} // namespace auth