diff --git a/Code/Framework/AzNetworking/AzNetworking/Utilities/EncryptionCommon.cpp b/Code/Framework/AzNetworking/AzNetworking/Utilities/EncryptionCommon.cpp index 0903205eb8..a9a7a12315 100644 --- a/Code/Framework/AzNetworking/AzNetworking/Utilities/EncryptionCommon.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/Utilities/EncryptionCommon.cpp @@ -31,13 +31,13 @@ namespace AzNetworking { - AZ_CVAR(AZ::CVarFixedString, net_SslExternalCertificateFile, "testcert.pem", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The filename of the EXTERNAL (server to client) certificate chain in PEM format (default is for debugging purposes)"); - AZ_CVAR(AZ::CVarFixedString, net_SslExternalPrivateKeyFile, "testkey.pem", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The filename of the EXTERNAL (server to client) private key file in PEM format (default is for debugging purposes)"); - AZ_CVAR(AZ::CVarFixedString, net_SslExternalContextPassword, "12345", nullptr, AZ::ConsoleFunctorFlags::DontReplicate | AZ::ConsoleFunctorFlags::IsInvisible, "The password required for the EXTERNAL (server to client) private certificate (default is for debugging purposes)"); + AZ_CVAR(AZ::CVarFixedString, net_SslExternalCertificateFile, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The filename of the EXTERNAL (server to client) certificate chain in PEM format"); + AZ_CVAR(AZ::CVarFixedString, net_SslExternalPrivateKeyFile, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The filename of the EXTERNAL (server to client) private key file in PEM format"); + AZ_CVAR(AZ::CVarFixedString, net_SslExternalContextPassword, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate | AZ::ConsoleFunctorFlags::IsInvisible, "The password required for the EXTERNAL (server to client) private certificate"); - AZ_CVAR(AZ::CVarFixedString, net_SslInternalCertificateFile, "servercert.pem", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The filename of the INTERNAL (server to server only) certificate chain in PEM format (default is for debugging purposes)"); - AZ_CVAR(AZ::CVarFixedString, net_SslInternalPrivateKeyFile, "serverkey.pem", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The filename of the INTERNAL (server to server only) private key file in PEM format (default is for debugging purposes)"); - AZ_CVAR(AZ::CVarFixedString, net_SslInternalContextPassword, "12345", nullptr, AZ::ConsoleFunctorFlags::DontReplicate | AZ::ConsoleFunctorFlags::IsInvisible, "The password required for the INTERNAL (server to server only) private certificate (default is for debugging purposes)"); + AZ_CVAR(AZ::CVarFixedString, net_SslInternalCertificateFile, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The filename of the INTERNAL (server to server only) certificate chain in PEM format"); + AZ_CVAR(AZ::CVarFixedString, net_SslInternalPrivateKeyFile, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The filename of the INTERNAL (server to server only) private key file in PEM format"); + AZ_CVAR(AZ::CVarFixedString, net_SslInternalContextPassword, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate | AZ::ConsoleFunctorFlags::IsInvisible, "The password required for the INTERNAL (server to server only) private certificate"); AZ_CVAR(AZ::CVarFixedString, net_SslCertCiphers, "ECDHE-RSA-AES256-GCM-SHA384", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The cipher suite to use when using cert based key exchange"); @@ -100,19 +100,35 @@ namespace AzNetworking static void GetCertificatePaths(TrustZone trustZone, AZStd::string& certificatePath, AZStd::string& privateKeyPath) { - const AZ::CVarFixedString certificateFile = (trustZone == TrustZone::ExternalClientToServer) ? net_SslExternalCertificateFile : net_SslInternalCertificateFile; - const AZ::CVarFixedString privateKeyFile = (trustZone == TrustZone::ExternalClientToServer) ? net_SslExternalPrivateKeyFile : net_SslInternalPrivateKeyFile; - - AZStd::string assetDir; - if (AZ::IO::FileIOBase::GetInstance() != nullptr) + if (auto console = AZ::Interface::Get(); console != nullptr) { - char buffer[AZ_MAX_PATH_LEN]; - AZ::IO::FileIOBase::GetInstance()->ResolvePath("@products@/", buffer, sizeof(buffer)); - assetDir = AZStd::string(buffer); - } + AZ::CVarFixedString certificateFile = ""; + AZ::CVarFixedString privateKeyFile = ""; + if (trustZone == TrustZone::ExternalClientToServer) + { + console->GetCvarValue("net_SslExternalCertificateFile", certificateFile); + console->GetCvarValue("net_SslExternalPrivateKeyFile", privateKeyFile); + } + else + { + console->GetCvarValue("net_SslInternalCertificateFile", certificateFile); + console->GetCvarValue("net_SslInternalPrivateKeyFile", privateKeyFile); + } - certificatePath = assetDir + certificateFile.c_str(); - privateKeyPath = assetDir + privateKeyFile.c_str(); + // Check asset directory when provided file paths are not valid + AZStd::string assetDir = ""; + if (!AZ::IO::SystemFile::Exists(certificateFile.c_str()) && + !AZ::IO::SystemFile::Exists(privateKeyFile.c_str()) && + AZ::IO::FileIOBase::GetInstance() != nullptr) + { + char buffer[AZ_MAX_PATH_LEN]; + AZ::IO::FileIOBase::GetInstance()->ResolvePath("@products@/", buffer, sizeof(buffer)); + assetDir = AZStd::string(buffer); + } + + certificatePath = assetDir + certificateFile.c_str(); + privateKeyPath = assetDir + privateKeyFile.c_str(); + } } static bool ValidatePinnedCertificate(X509* remoteCert, TrustZone trustZone) diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftJoinSessionActivity.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftJoinSessionActivity.cpp index f93f9e5378..2bb4c60fdb 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftJoinSessionActivity.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Source/Activity/AWSGameLiftJoinSessionActivity.cpp @@ -44,14 +44,23 @@ namespace AWSGameLift { Multiplayer::SessionConnectionConfig sessionConnectionConfig; auto createPlayerSessionResult = createPlayerSessionOutcome.GetResult(); - // TODO: AWSNativeSDK needs to be updated to support this attribute, and it is a must have for TLS certificate enabled fleet - //sessionConnectionConfig.m_dnsName = createPlayerSessionResult.GetPlayerSession().GetDnsName().c_str(); - sessionConnectionConfig.m_ipAddress = createPlayerSessionResult.GetPlayerSession().GetIpAddress().c_str(); + AZStd::string_view dnsName = createPlayerSessionResult.GetPlayerSession().GetDnsName().c_str(); + AZStd::string_view ipAddress = createPlayerSessionResult.GetPlayerSession().GetIpAddress().c_str(); + // When connecting to a game session that is running on a TLS-enabled fleet, you must use the DNS name, not the IP address. + if (dnsName.ends_with(AWSGameLiftTLSEnabledDNSSuffix)) + { + sessionConnectionConfig.m_dnsName = dnsName; + } + else + { + sessionConnectionConfig.m_ipAddress = ipAddress; + } sessionConnectionConfig.m_playerSessionId = createPlayerSessionResult.GetPlayerSession().GetPlayerSessionId().c_str(); sessionConnectionConfig.m_port = static_cast(createPlayerSessionResult.GetPlayerSession().GetPort()); AZ_TracePrintf(AWSGameLiftJoinSessionActivityName, - "Built SessionConnectionConfig with IpAddress=%s, PlayerSessionId=%s and Port=%d", + "Built SessionConnectionConfig with DnsName=%s, IpAddress=%s, PlayerSessionId=%s and Port=%d", + sessionConnectionConfig.m_dnsName.c_str(), sessionConnectionConfig.m_ipAddress.c_str(), sessionConnectionConfig.m_playerSessionId.c_str(), sessionConnectionConfig.m_port); diff --git a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftJoinSessionActivityTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftJoinSessionActivityTest.cpp index 664baa55cb..14211b6dc9 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftJoinSessionActivityTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftClient/Tests/Activity/AWSGameLiftJoinSessionActivityTest.cpp @@ -28,7 +28,7 @@ TEST_F(AWSGameLiftJoinSessionActivityTest, BuildAWSGameLiftCreatePlayerSessionRe EXPECT_TRUE(strcmp(awsRequest.GetGameSessionId().c_str(), request.m_sessionId.c_str()) == 0); } -TEST_F(AWSGameLiftJoinSessionActivityTest, BuildSessionConnectionConfig_Call_GetExpectedResult) +TEST_F(AWSGameLiftJoinSessionActivityTest, BuildSessionConnectionConfig_Call_GetIpAddressAsDestinationResult) { Aws::GameLift::Model::PlayerSession playerSession; playerSession.SetIpAddress("dummyIpAddress"); @@ -39,11 +39,30 @@ TEST_F(AWSGameLiftJoinSessionActivityTest, BuildSessionConnectionConfig_Call_Get Aws::GameLift::Model::CreatePlayerSessionOutcome createPlayerSessionOutcome(createPlayerSessionResult); auto connectionConfig = JoinSessionActivity::BuildSessionConnectionConfig(createPlayerSessionOutcome); + EXPECT_TRUE(strcmp(connectionConfig.m_dnsName.c_str(), "") == 0); EXPECT_TRUE(strcmp(connectionConfig.m_ipAddress.c_str(), playerSession.GetIpAddress().c_str()) == 0); EXPECT_TRUE(strcmp(connectionConfig.m_playerSessionId.c_str(), playerSession.GetPlayerSessionId().c_str()) == 0); EXPECT_TRUE(connectionConfig.m_port == playerSession.GetPort()); } +TEST_F(AWSGameLiftJoinSessionActivityTest, BuildSessionConnectionConfig_Call_GetDNSAsDestinationResult) +{ + Aws::GameLift::Model::PlayerSession playerSession; + playerSession.SetDnsName("gameliftunittest.amazongamelift.com"); + playerSession.SetIpAddress("dummyIpAddress"); + playerSession.SetPlayerSessionId("dummyPlayerSessionId"); + playerSession.SetPort(123); + Aws::GameLift::Model::CreatePlayerSessionResult createPlayerSessionResult; + createPlayerSessionResult.SetPlayerSession(playerSession); + Aws::GameLift::Model::CreatePlayerSessionOutcome createPlayerSessionOutcome(createPlayerSessionResult); + auto connectionConfig = JoinSessionActivity::BuildSessionConnectionConfig(createPlayerSessionOutcome); + + EXPECT_TRUE(strcmp(connectionConfig.m_dnsName.c_str(), playerSession.GetDnsName().c_str()) == 0); + EXPECT_TRUE(strcmp(connectionConfig.m_ipAddress.c_str(), "") == 0); + EXPECT_TRUE(strcmp(connectionConfig.m_playerSessionId.c_str(), playerSession.GetPlayerSessionId().c_str()) == 0); + EXPECT_TRUE(connectionConfig.m_port == playerSession.GetPort()); +} + TEST_F(AWSGameLiftJoinSessionActivityTest, ValidateJoinSessionRequest_CallWithBaseType_GetFalseResult) { AZ_TEST_START_TRACE_SUPPRESSION; diff --git a/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h b/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h index e29df324d3..bc415725b4 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftCommon/Source/AWSGameLiftSessionConstants.h @@ -16,6 +16,9 @@ namespace AWSGameLift // Reference https://sdk.amazonaws.com/cpp/api/LATEST/_game_session_status_reason_8h.html static const char* AWSGameLiftSessionStatusReasons[2] = { "NotSet", "Interrupted" }; + // Reference https://docs.aws.amazon.com/gamelift/latest/apireference/API_PlayerSession.html + static constexpr const char AWSGameLiftTLSEnabledDNSSuffix[] = ".amazongamelift.com"; + static constexpr const char AWSGameLiftErrorMessageTemplate[] = "Exception: %s, Message: %s"; static constexpr const char AWSGameLiftClientMissingErrorMessage[] = "GameLift client is not configured yet."; } // namespace AWSGameLift diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp index 898aa54c98..134b9d5aa1 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.cpp @@ -406,8 +406,30 @@ namespace AWSGameLift AZ::IO::Path AWSGameLiftServerManager::GetExternalSessionCertificate() { - // TODO: Add support to get TLS cert file path - return AZ::IO::Path(); + auto certificateOutcome = m_gameLiftServerSDKWrapper->GetInstanceCertificate(); + if (certificateOutcome.IsSuccess()) + { + return AZ::IO::Path(certificateOutcome.GetResult().GetCertificatePath().c_str()); + } + else + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerInstanceCertificateErrorMessage); + return AZ::IO::Path(); + } + } + + AZ::IO::Path AWSGameLiftServerManager::GetExternalSessionPrivateKey() + { + auto certificateOutcome = m_gameLiftServerSDKWrapper->GetInstanceCertificate(); + if (certificateOutcome.IsSuccess()) + { + return AZ::IO::Path(certificateOutcome.GetResult().GetPrivateKeyPath().c_str()); + } + else + { + AZ_Error(AWSGameLiftServerManagerName, false, AWSGameLiftServerInstancePrivateKeyErrorMessage); + return AZ::IO::Path(); + } } AZ::IO::Path AWSGameLiftServerManager::GetInternalSessionCertificate() @@ -416,6 +438,12 @@ namespace AWSGameLift return AZ::IO::Path(); } + AZ::IO::Path AWSGameLiftServerManager::GetInternalSessionPrivateKey() + { + // GameLift doesn't support it, return empty path + return AZ::IO::Path(); + } + void AWSGameLiftServerManager::InitializeGameLiftServerSDK() { if (m_serverSDKInitialized) @@ -537,6 +565,10 @@ namespace AWSGameLift { UpdateGameSessionData(gameSession); Multiplayer::SessionConfig sessionConfig = BuildSessionConfig(gameSession); + if (!AZ::Interface::Get()) + { + AZ::Interface::Register(this); + } bool createSessionResult = true; AZ::EBusReduceResult> result(createSessionResult); @@ -551,11 +583,6 @@ namespace AWSGameLift if (activationOutcome.IsSuccess()) { AZ_TracePrintf(AWSGameLiftServerManagerName, "ActivateGameSession request against Amazon GameLift service succeeded."); - // Register server manager as handler once game session has been activated - if (!AZ::Interface::Get()) - { - AZ::Interface::Register(this); - } Multiplayer::SessionNotificationBus::Broadcast(&Multiplayer::SessionNotifications::OnCreateSessionEnd); } else diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h index 673172dc9a..e580cfa457 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/AWSGameLiftServerManager.h @@ -56,6 +56,10 @@ namespace AWSGameLift static constexpr const char AWSGameLiftServerPlayerConnectionMissingErrorMessage[] = "Player connection id %d does not exist."; + static constexpr const char AWSGameLiftServerInstanceCertificateErrorMessage[] = + "Failed to locate Amazon GameLift TLS certificate file."; + static constexpr const char AWSGameLiftServerInstancePrivateKeyErrorMessage[] = + "Failed to locate Amazon GameLift TLS private key file."; static constexpr const char AWSGameLiftServerInitSDKErrorMessage[] = "Failed to initialize Amazon GameLift Server SDK. ErrorMessage: %s"; static constexpr const char AWSGameLiftServerProcessReadyErrorMessage[] = @@ -120,7 +124,9 @@ namespace AWSGameLift bool ValidatePlayerJoinSession(const Multiplayer::PlayerConnectionConfig& playerConnectionConfig) override; void HandlePlayerLeaveSession(const Multiplayer::PlayerConnectionConfig& playerConnectionConfig) override; AZ::IO::Path GetExternalSessionCertificate() override; + AZ::IO::Path GetExternalSessionPrivateKey() override; AZ::IO::Path GetInternalSessionCertificate() override; + AZ::IO::Path GetInternalSessionPrivateKey() override; protected: void SetGameLiftServerSDKWrapper(AZStd::unique_ptr gameLiftServerSDKWrapper); diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.cpp b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.cpp index d51c8eb8cb..df297fbe42 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.cpp @@ -33,6 +33,11 @@ namespace AWSGameLift return Aws::GameLift::Server::InitSDK(); } + Aws::GameLift::GetInstanceCertificateOutcome GameLiftServerSDKWrapper::GetInstanceCertificate() + { + return Aws::GameLift::Server::GetInstanceCertificate(); + } + Aws::GameLift::GenericOutcome GameLiftServerSDKWrapper::ProcessReady( const Aws::GameLift::Server::ProcessParameters& processParameters) { diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.h b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.h index e56366d75a..2b3d777ee7 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Source/GameLiftServerSDKWrapper.h @@ -41,6 +41,13 @@ namespace AWSGameLift virtual Aws::GameLift::DescribePlayerSessionsOutcome DescribePlayerSessions( const Aws::GameLift::Server::Model::DescribePlayerSessionsRequest& describePlayerSessionsRequest); + //! Retrieves the file location of a pem-encoded TLS certificate that is associated with the fleet and its + //! instances. This certificate is generated when a new fleet is created with the certificate configuration set to + //! GENERATED. Use this certificate to establish a secure connection with a game client and to encrypt client server communication. + //! @return If successful, returns a GetInstanceCertificateOutcome object containing the location of the fleet's TLS certificate file, + //! which is stored on the instance. If not successful, returns an error message. + virtual Aws::GameLift::GetInstanceCertificateOutcome GetInstanceCertificate(); + //! Initializes the GameLift SDK. //! Should be called when the server starts, before any GameLift-dependent initialization happens. //! @return If successful, returns an InitSdkOutcome object indicating that the server process is ready to call ProcessReady(). diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerManagerTest.cpp b/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerManagerTest.cpp index 4f69fd7d4c..86a9871015 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerManagerTest.cpp +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerManagerTest.cpp @@ -356,10 +356,12 @@ R"({ EXPECT_CALL(handlerMock, OnCreateSessionBegin(testing::_)).Times(1).WillOnce(testing::Return(false)); EXPECT_CALL(handlerMock, OnCreateSessionEnd()).Times(0); EXPECT_CALL(handlerMock, OnDestroySessionBegin()).Times(1).WillOnce(testing::Return(true)); + EXPECT_CALL(handlerMock, OnDestroySessionEnd()).Times(1); EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), ProcessEnding()).Times(1); AZ_TEST_START_TRACE_SUPPRESSION; m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onStartGameSessionFunc(Aws::GameLift::Server::Model::GameSession()); AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(AZ::Interface::Get()); } TEST_F(GameLiftServerManagerTest, OnStartGameSession_ActivateGameSessionSucceeds_RegisterAsHandler) @@ -369,7 +371,6 @@ R"({ SessionNotificationsHandlerMock handlerMock; EXPECT_CALL(handlerMock, OnCreateSessionBegin(testing::_)).Times(1).WillOnce(testing::Return(true)); EXPECT_CALL(handlerMock, OnCreateSessionEnd()).Times(1); - EXPECT_CALL(handlerMock, OnDestroySessionBegin()).Times(1).WillOnce(testing::Return(true)); EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), ActivateGameSession()) .Times(1) .WillOnce(testing::Return(Aws::GameLift::GenericOutcome(nullptr))); @@ -380,7 +381,13 @@ R"({ testSession.AddGameProperties(testProperty); m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onStartGameSessionFunc(testSession); EXPECT_TRUE(AZ::Interface::Get()); + + // clean up + EXPECT_CALL(handlerMock, OnDestroySessionBegin()).Times(1).WillOnce(testing::Return(true)); + EXPECT_CALL(handlerMock, OnDestroySessionEnd()).Times(1); + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), ProcessEnding()).Times(1); m_serverManager->HandleDestroySession(); + EXPECT_FALSE(AZ::Interface::Get()); } TEST_F(GameLiftServerManagerTest, OnStartGameSession_ActivateGameSessionFails_TerminationNotificationSent) @@ -391,6 +398,7 @@ R"({ EXPECT_CALL(handlerMock, OnCreateSessionBegin(testing::_)).Times(1).WillOnce(testing::Return(true)); EXPECT_CALL(handlerMock, OnCreateSessionEnd()).Times(0); EXPECT_CALL(handlerMock, OnDestroySessionBegin()).Times(1).WillOnce(testing::Return(true)); + EXPECT_CALL(handlerMock, OnDestroySessionEnd()).Times(1); EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), ActivateGameSession()) .Times(1) .WillOnce(testing::Return(Aws::GameLift::GenericOutcome())); @@ -398,6 +406,7 @@ R"({ AZ_TEST_START_TRACE_SUPPRESSION; m_serverManager->m_gameLiftServerSDKWrapperMockPtr->m_onStartGameSessionFunc(Aws::GameLift::Server::Model::GameSession()); AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_FALSE(AZ::Interface::Get()); } TEST_F(GameLiftServerManagerTest, OnUpdateGameSession_TriggerWithUnknownReason_OnUpdateSessionGetCalledOnce) @@ -735,6 +744,72 @@ R"({ EXPECT_TRUE(actualResult[0].m_playerAttributes.size() == 4); } + TEST_F(GameLiftServerManagerTest, GetExternalSessionCertificate_CallWithTLSEnabled_GetExpectedResult) + { + AZStd::string expectedResult = "gameliftunittestcertificate.pem"; + Aws::GameLift::Server::Model::GetInstanceCertificateResult certificateResult; + certificateResult.SetCertificatePath(expectedResult.c_str()); + Aws::GameLift::GetInstanceCertificateOutcome certificateOutcome(certificateResult); + + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), GetInstanceCertificate()) + .Times(1) + .WillOnce(Return(certificateOutcome)); + + auto actualResult = m_serverManager->GetExternalSessionCertificate(); + EXPECT_STREQ(actualResult.c_str(), expectedResult.c_str()); + } + + TEST_F(GameLiftServerManagerTest, GetExternalSessionCertificate_CallWithTLSDisabled_GetEmptyResult) + { + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), GetInstanceCertificate()) + .Times(1) + .WillOnce(Return(Aws::GameLift::GetInstanceCertificateOutcome())); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->GetExternalSessionCertificate(); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_STREQ(actualResult.c_str(), ""); + } + + TEST_F(GameLiftServerManagerTest, GetExternalSessionPrivateKey_CallWithTLSEnabled_GetExpectedResult) + { + AZStd::string expectedResult = "gameliftunittestprivatekey.pem"; + Aws::GameLift::Server::Model::GetInstanceCertificateResult certificateResult; + certificateResult.SetPrivateKeyPath(expectedResult.c_str()); + Aws::GameLift::GetInstanceCertificateOutcome certificateOutcome(certificateResult); + + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), GetInstanceCertificate()) + .Times(1) + .WillOnce(Return(certificateOutcome)); + + auto actualResult = m_serverManager->GetExternalSessionPrivateKey(); + EXPECT_STREQ(actualResult.c_str(), expectedResult.c_str()); + } + + TEST_F(GameLiftServerManagerTest, GetExternalSessionPrivateKey_CallWithTLSDisabled_GetEmptyResult) + { + EXPECT_CALL(*(m_serverManager->m_gameLiftServerSDKWrapperMockPtr), GetInstanceCertificate()) + .Times(1) + .WillOnce(Return(Aws::GameLift::GetInstanceCertificateOutcome())); + + AZ_TEST_START_TRACE_SUPPRESSION; + auto actualResult = m_serverManager->GetExternalSessionPrivateKey(); + AZ_TEST_STOP_TRACE_SUPPRESSION(1); + EXPECT_STREQ(actualResult.c_str(), ""); + } + + TEST_F(GameLiftServerManagerTest, GetInternalSessionCertificate_Call_GetEmptyResult) + { + auto actualResult = m_serverManager->GetInternalSessionCertificate(); + EXPECT_STREQ(actualResult.c_str(), ""); + } + + TEST_F(GameLiftServerManagerTest, GetInternalSessionPrivateKey_Call_GetEmptyResult) + { + auto actualResult = m_serverManager->GetInternalSessionPrivateKey(); + EXPECT_STREQ(actualResult.c_str(), ""); + } + TEST_F(GameLiftServerManagerTest, GetActiveServerMatchBackfillPlayers_CallWithMultiDescribePlayerButError_GetEmptyResult) { m_serverManager->SetupTestMatchmakingData(TEST_SERVER_MATCHMAKING_DATA, 50); diff --git a/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerMocks.h b/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerMocks.h index 37f7d5595e..2260bae17d 100644 --- a/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerMocks.h +++ b/Gems/AWSGameLift/Code/AWSGameLiftServer/Tests/AWSGameLiftServerMocks.h @@ -47,6 +47,7 @@ namespace UnitTest MOCK_METHOD1(ProcessReady, GenericOutcome(const Server::ProcessParameters& processParameters)); MOCK_METHOD0(ProcessEnding, GenericOutcome()); MOCK_METHOD1(RemovePlayerSession, GenericOutcome(const AZStd::string& playerSessionId)); + MOCK_METHOD0(GetInstanceCertificate, Aws::GameLift::GetInstanceCertificateOutcome()); MOCK_METHOD0(GetTerminationTime, AZStd::string()); MOCK_METHOD1(StartMatchBackfill, StartMatchBackfillOutcome( const Aws::GameLift::Server::Model::StartMatchBackfillRequest&)); diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Session/ISessionHandlingRequests.h b/Gems/Multiplayer/Code/Include/Multiplayer/Session/ISessionHandlingRequests.h index 022d1743b3..04e8a78d54 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Session/ISessionHandlingRequests.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Session/ISessionHandlingRequests.h @@ -86,9 +86,19 @@ namespace Multiplayer //! empty string. virtual AZ::IO::Path GetExternalSessionCertificate() = 0; + //! Retrieves the file location of a pem-encoded TLS private key for Client to Server communication + //! @return If successful, returns the file location of TLS private key file; if not successful, returns + //! empty string. + virtual AZ::IO::Path GetExternalSessionPrivateKey() = 0; + //! Retrieves the file location of a pem-encoded TLS certificate for Server to Server communication //! @return If successful, returns the file location of TLS certificate file; if not successful, returns //! empty string. virtual AZ::IO::Path GetInternalSessionCertificate() = 0; + + //! Retrieves the file location of a pem-encoded TLS private key for Server to Server communication + //! @return If successful, returns the file location of TLS private key file; if not successful, returns + //! empty string. + virtual AZ::IO::Path GetInternalSessionPrivateKey() = 0; }; } // namespace Multiplayer diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index dad584d3e8..c66cb867a2 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -283,22 +283,28 @@ namespace Multiplayer bool MultiplayerSystemComponent::OnCreateSessionBegin(const SessionConfig& sessionConfig) { // Check if session manager has a certificate for us and pass it along if so - if (AZ::Interface::Get() != nullptr) - { - AZ::CVarFixedString externalCertPath = AZ::CVarFixedString( - AZ::Interface::Get()->GetExternalSessionCertificate().c_str()); - if (!externalCertPath.empty()) + if (auto console = AZ::Interface::Get(); console != nullptr) + { + bool tcpUseEncryption = false; + console->GetCvarValue("net_TcpUseEncryption", tcpUseEncryption); + bool udpUseEncryption = false; + console->GetCvarValue("net_UdpUseEncryption", udpUseEncryption); + auto sessionProviderHandler = AZ::Interface::Get(); + if ((tcpUseEncryption || udpUseEncryption) && sessionProviderHandler != nullptr) { - AZ::CVarFixedString commandString = "net_SslExternalCertificateFile " + externalCertPath; - AZ::Interface::Get()->PerformCommand(commandString.c_str()); - } + AZ::CVarFixedString externalCertPath = AZ::CVarFixedString(sessionProviderHandler->GetExternalSessionCertificate().c_str()); + if (!externalCertPath.empty()) + { + AZ::CVarFixedString commandString = "net_SslExternalCertificateFile " + externalCertPath; + console->PerformCommand(commandString.c_str()); + } - AZ::CVarFixedString internalCertPath = AZ::CVarFixedString( - AZ::Interface::Get()->GetInternalSessionCertificate().c_str()); - if (!internalCertPath.empty()) - { - AZ::CVarFixedString commandString = "net_SslInternalCertificateFile " + internalCertPath; - AZ::Interface::Get()->PerformCommand(commandString.c_str()); + AZ::CVarFixedString externalKeyPath = AZ::CVarFixedString(sessionProviderHandler->GetExternalSessionPrivateKey().c_str()); + if (!externalKeyPath.empty()) + { + AZ::CVarFixedString commandString = "net_SslExternalPrivateKeyFile " + externalKeyPath; + console->PerformCommand(commandString.c_str()); + } } }