| #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 |