diff --git a/cloud/filestore/config/server.proto b/cloud/filestore/config/server.proto index 64c285df5bb..34d1b39debe 100644 --- a/cloud/filestore/config/server.proto +++ b/cloud/filestore/config/server.proto @@ -67,6 +67,10 @@ message TServerConfig // Regions that have not been pinged within this timeout will be // invalidated and unmapped. optional uint32 SharedMemoryRegionTimeout = 23; + + // Certificate refresh period (in milliseconds) for automatic TLS certificate + // reloading. Zero disables periodic certificate reloading. + optional uint32 RefreshCertsPeriod = 24; } //////////////////////////////////////////////////////////////////////////////// diff --git a/cloud/filestore/libs/daemon/common/bootstrap.h b/cloud/filestore/libs/daemon/common/bootstrap.h index 8a7c471f4ed..08394d09882 100644 --- a/cloud/filestore/libs/daemon/common/bootstrap.h +++ b/cloud/filestore/libs/daemon/common/bootstrap.h @@ -80,6 +80,7 @@ class TBootstrapCommon IActorSystemPtr ActorSystem; NStorage::NFastShard::IServerPtr FastShardServer; NCloud::NStorage::IStatsFetcherPtr StatsFetcher; + ITaskQueuePtr LongRunningTaskExecutor; ICertificateProviderPtr CertificateProvider; public: diff --git a/cloud/filestore/libs/daemon/server/bootstrap.cpp b/cloud/filestore/libs/daemon/server/bootstrap.cpp index 64d8a220e60..31e53a61ef4 100644 --- a/cloud/filestore/libs/daemon/server/bootstrap.cpp +++ b/cloud/filestore/libs/daemon/server/bootstrap.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -139,10 +140,28 @@ void TBootstrapServer::InitComponents() }); } - if (!certPathList.empty()) { - CertificateProvider = CreateStaticCertificateProvider( + if (Configs->ServerConfig->GetRefreshCertsPeriod()) { + LongRunningTaskExecutor = CreateLongRunningTaskExecutor("CertRefresh"); + } + + if (certPathList.empty()) { + if (Configs->ServerConfig->GetSecurePort()) { + ythrow yexception() + << "Secure port is configured without certificates"; + } + + CertificateProvider = CreateCertificateProviderStub(); + } else { + CertificateProvider = CreateCertificateProvider( + Logging, + GetComponentName( + NStorage::TFileStoreComponents::TLS_CERTIFICATE_PROVIDER), + Scheduler, + LongRunningTaskExecutor, + serverCounters, Configs->ServerConfig->GetRootCertsFile(), - std::move(certPathList)); + std::move(certPathList), + Configs->ServerConfig->GetRefreshCertsPeriod()); } Server = NServer::CreateServer( diff --git a/cloud/filestore/libs/daemon/vhost/bootstrap.cpp b/cloud/filestore/libs/daemon/vhost/bootstrap.cpp index ec7db4276d3..f1d1cad2430 100644 --- a/cloud/filestore/libs/daemon/vhost/bootstrap.cpp +++ b/cloud/filestore/libs/daemon/vhost/bootstrap.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -85,8 +86,51 @@ const TString ServerMetricsComponent = "server"; //////////////////////////////////////////////////////////////////////////////// +struct TClientCertificateProviderFactory +{ + ILoggingServicePtr Logging; + TString LogComponent; + ISchedulerPtr Scheduler; + ITaskQueuePtr LongRunningTaskExecutor; + NMonitoring::TDynamicCountersPtr ServerGroup; + TDuration RefreshInterval; + + TClientCertificateProviderFactory( + ILoggingServicePtr logging, + TString logComponent, + ISchedulerPtr scheduler, + ITaskQueuePtr longRunningTaskExecutor, + NMonitoring::TDynamicCountersPtr serverGroup, + TDuration refreshInterval) + : Logging(std::move(logging)) + , LogComponent(std::move(logComponent)) + , Scheduler(std::move(scheduler)) + , LongRunningTaskExecutor(std::move(longRunningTaskExecutor)) + , ServerGroup(std::move(serverGroup)) + , RefreshInterval(refreshInterval) + {} + + ICertificateProviderPtr Build( + TString rootCertPath, + TVector certificates) const + { + return CreateCertificateProvider( + Logging, + LogComponent, + Scheduler, + LongRunningTaskExecutor, + ServerGroup, + std::move(rootCertPath), + std::move(certificates), + RefreshInterval); + } +}; + +//////////////////////////////////////////////////////////////////////////////// + ICertificateProviderPtr CreateClientCertificateProvider( - const NClient::TClientConfigPtr& config) + const NClient::TClientConfigPtr& config, + const TClientCertificateProviderFactory& factory) { TVector certPathList { { @@ -95,7 +139,12 @@ ICertificateProviderPtr CreateClientCertificateProvider( } }; - return CreateStaticCertificateProvider( + if (config->GetSecurePort() && certPathList.empty()) { + ythrow yexception() + << "Secure client port is configured without certificates"; + } + + return factory.Build( config->GetRootCertsFile(), std::move(certPathList)); } @@ -107,8 +156,10 @@ class TFileStoreEndpoints final { private: using TEndpointsMap = TMap; + using TCertificateProvidersMap = TMap; private: + TClientCertificateProviderFactory CertificateProviderFactory; ITimerPtr Timer; ISchedulerPtr Scheduler; ILoggingServicePtr Logging; @@ -116,15 +167,18 @@ class TFileStoreEndpoints final IActorSystemPtr ActorSystem; TEndpointsMap Endpoints; + TCertificateProvidersMap CertificateProviders; public: TFileStoreEndpoints( + TClientCertificateProviderFactory certificateProviderFactory, ITimerPtr timer, ISchedulerPtr scheduler, ILoggingServicePtr logging, IFileStoreServicePtr localService, IActorSystemPtr actorSystem) - : Timer(std::move(timer)) + : CertificateProviderFactory(std::move(certificateProviderFactory)) + , Timer(std::move(timer)) , Scheduler(std::move(scheduler)) , Logging(std::move(logging)) , LocalService(std::move(localService)) @@ -133,6 +187,11 @@ class TFileStoreEndpoints final void Start() override { + for (const auto& [name, certificateProvider]: CertificateProviders) { + Y_UNUSED(name); + certificateProvider->Start(); + } + for (const auto& [name, endpoint]: Endpoints) { endpoint->Start(); } @@ -143,6 +202,11 @@ class TFileStoreEndpoints final for (const auto& [name, endpoint]: Endpoints) { endpoint->Stop(); } + + for (const auto& [name, certificateProvider]: CertificateProviders) { + Y_UNUSED(name); + certificateProvider->Stop(); + } } IFileStoreServicePtr GetEndpoint(const TString& name) override @@ -159,6 +223,7 @@ class TFileStoreEndpoints final ui32 permanentActorCount) { auto clientConfig = std::make_shared(config); + ICertificateProviderPtr certificateProvider; IFileStoreServicePtr fileStore; switch (kind) { case NDaemon::EServiceKind::Null: { @@ -178,19 +243,28 @@ class TFileStoreEndpoints final if (LocalService) { fileStore = LocalService; } else { + certificateProvider = CreateClientCertificateProvider( + clientConfig, + CertificateProviderFactory); fileStore = NClient::CreateFileStoreClient( clientConfig, Logging, - CreateClientCertificateProvider(clientConfig)); + certificateProvider); } break; } } - return AddEndpoint( + const bool inserted = AddEndpoint( name, std::move(clientConfig), std::move(fileStore)); + + if (inserted && certificateProvider) { + CertificateProviders.emplace(name, std::move(certificateProvider)); + } + + return inserted; } bool Empty() const @@ -367,6 +441,10 @@ void TBootstrapVhost::InitComponents() Configs->VhostServiceConfig->GetEndpointStorageType()); } + if (Configs->ServerConfig->GetRefreshCertsPeriod()) { + LongRunningTaskExecutor = CreateLongRunningTaskExecutor("CertRefresh"); + } + switch (Configs->Options->Service) { case NDaemon::EServiceKind::Local: case NDaemon::EServiceKind::Kikimr: @@ -394,10 +472,24 @@ void TBootstrapVhost::InitComponents() }); } - if (!certPathList.empty()) { - CertificateProvider = CreateStaticCertificateProvider( + if (certPathList.empty()) { + if (Configs->ServerConfig->GetSecurePort()) { + ythrow yexception() + << "Secure port is configured without certificates"; + } + + CertificateProvider = CreateCertificateProviderStub(); + } else { + CertificateProvider = CreateCertificateProvider( + Logging, + GetComponentName( + NStorage::TFileStoreComponents::TLS_CERTIFICATE_PROVIDER), + Scheduler, + LongRunningTaskExecutor, + serverCounters, Configs->ServerConfig->GetRootCertsFile(), - std::move(certPathList)); + std::move(certPathList), + Configs->ServerConfig->GetRefreshCertsPeriod()); } Server = CreateServer( @@ -459,7 +551,17 @@ void TBootstrapVhost::InitEndpoints() localServiceConfig->Utf8DebugString().Quote().c_str()); } + TClientCertificateProviderFactory certFactory( + Logging, + GetComponentName( + NStorage::TFileStoreComponents::TLS_CERTIFICATE_PROVIDER), + Scheduler, + LongRunningTaskExecutor, + FilestoreCounters->GetSubgroup("component", VhostMetricsComponent), + Configs->ServerConfig->GetRefreshCertsPeriod()); + auto endpoints = std::make_shared( + std::move(certFactory), Timer, Scheduler, Logging, diff --git a/cloud/filestore/libs/server/config.cpp b/cloud/filestore/libs/server/config.cpp index 64f2b0c8725..be1a31516e7 100644 --- a/cloud/filestore/libs/server/config.cpp +++ b/cloud/filestore/libs/server/config.cpp @@ -48,6 +48,7 @@ constexpr TDuration Seconds(int s) xxx(SharedMemoryRegionTimeout, \ TDuration, \ TDuration::Max()) \ + xxx(RefreshCertsPeriod, TDuration, Seconds(0) )\ // FILESTORE_SERVER_CONFIG #define FILESTORE_SERVER_DECLARE_CONFIG(name, type, value) \ diff --git a/cloud/filestore/libs/server/config.h b/cloud/filestore/libs/server/config.h index 1fc590742bf..9f895486d3c 100644 --- a/cloud/filestore/libs/server/config.h +++ b/cloud/filestore/libs/server/config.h @@ -62,6 +62,8 @@ class TServerConfig TString GetSharedMemoryBasePath() const; TDuration GetSharedMemoryRegionTimeout() const; + TDuration GetRefreshCertsPeriod() const; + const NProto::TServerConfig& GetProto() const { return ProtoConfig; diff --git a/cloud/filestore/libs/storage/api/components.h b/cloud/filestore/libs/storage/api/components.h index d8de97ce3f8..3eadbe6d9d8 100644 --- a/cloud/filestore/libs/storage/api/components.h +++ b/cloud/filestore/libs/storage/api/components.h @@ -36,6 +36,7 @@ namespace NCloud::NFileStore::NStorage { xxx(CLIENT) \ xxx(AUTH) \ xxx(USER_STATS) \ + xxx(TLS_CERTIFICATE_PROVIDER) \ // FILESTORE_COMPONENTS ////////////////////////////////////////////////////////////////////////////////