/* * TODO: currently AP safe config deployment is not supported to update configurations. * The only way to update the configurations is via an application upgrade. */ public static bool GetCurrentConfigurations() { IConfiguration configuration = APConfiguration.GetConfiguration(); CertificateManagerRunIntervalSeconds = configuration.GetInt32Value(StringConstants.CertificateManagerConfigurationSectionName, "CertificateManagerRunIntervalSeconds", 3600); CertificateManagerRetryIntervalSeconds = configuration.GetInt32Value(StringConstants.CertificateManagerConfigurationSectionName, "CertificateManagerRetryIntervalSeconds", 300); EnableSecretStorePersistentCaching = configuration.GetBoolValue(StringConstants.CertificateManagerConfigurationSectionName, "EnableSecretStoreClientSidePersistentCaching", true); string logLevelString = configuration.GetStringValue(StringConstants.EnvironmentSyncConfigurationSectionName, "LogLevel", "Info"); LogLevel logLevel; if (!Enum.TryParse <LogLevel>(logLevelString, out logLevel)) { TextLogger.LogError("Log level is invalid : {0}.", logLevelString); return(false); } LogLevel = logLevel; return(true); }
/* * TODO: currently AP safe config deployment is not supported to update configurations. * The only way to update the configurations is via an application upgrade. * This is OK since none of the configurations are critical. * To update all things critical in Service Fabric Autopilot Agent, an AP application upgrade is needed. */ public static bool GetCurrentConfigurations(bool useTestMode = false) { if (useTestMode) { GetTestConfigurations(); } else { IConfiguration configuration = APConfiguration.GetConfiguration(); BackgroundStateMachineRunIntervalSeconds = configuration.GetInt32Value(StringConstants.AgentConfigurationSectionName, "BackgroundStateMachineRunIntervalSeconds", 10); DeploymentRetryIntervalSeconds = configuration.GetInt32Value(StringConstants.AgentConfigurationSectionName, "DeploymentRetryIntervalSeconds", 120); string bootstrapClusterManifestLocationString = configuration.GetStringValue(StringConstants.AgentConfigurationSectionName, "BootstrapClusterManifestLocation", string.Empty); BootstrapClusterManifestLocationType bootstrapClusterManifestLocation; if (!Enum.TryParse <BootstrapClusterManifestLocationType>(bootstrapClusterManifestLocationString, out bootstrapClusterManifestLocation)) { TextLogger.LogError("Bootstrap cluster manifest location is invalid or not specified : {0}.", bootstrapClusterManifestLocationString); return(false); } BootstrapClusterManifestLocation = bootstrapClusterManifestLocation; DynamicPortRangeStart = configuration.GetInt32Value(StringConstants.AgentConfigurationSectionName, "DynamicPortRangeStart", -1); DynamicPortRangeEnd = configuration.GetInt32Value(StringConstants.AgentConfigurationSectionName, "DynamicPortRangeEnd", -1); // please refer to http://support.microsoft.com/kb/929851 for valid dynamic port range in Windows. if (DynamicPortRangeStart < 1025 || DynamicPortRangeEnd > 65535 || DynamicPortRangeEnd - DynamicPortRangeStart + 1 < 255) { TextLogger.LogError("Dynamic port range is invalid or not specified : [{0}, {1}].", DynamicPortRangeStart, DynamicPortRangeEnd); return(false); } DynamicTopologyUpdateMode = (DynamicTopologyUpdateMode)configuration.GetInt32Value(StringConstants.AgentConfigurationSectionName, "DynamicTopologyUpdateMode", 0); string logLevelString = configuration.GetStringValue(StringConstants.AgentConfigurationSectionName, "LogLevel", "Info"); LogLevel logLevel; if (!Enum.TryParse <LogLevel>(logLevelString, out logLevel)) { TextLogger.LogError("Log level is invalid : {0}.", logLevelString); return(false); } LogLevel = logLevel; PortFowardingRules = new List <PortFowardingRule>(); if (configuration.SectionExists(StringConstants.PortForwardingConfigurationSectionName)) { string[] portFowardingRuleNames = configuration.GetSectionKeys(StringConstants.PortForwardingConfigurationSectionName); if (portFowardingRuleNames != null) { foreach (string portFowardingRuleName in portFowardingRuleNames) { string[] portFowardingRuleComponents = configuration.GetStringValueAndSplit(StringConstants.PortForwardingConfigurationSectionName, portFowardingRuleName, ","); if (portFowardingRuleComponents == null || portFowardingRuleComponents.Length != 3) { TextLogger.LogError("Port forwarding rule {0} is invalid. Valid rule format : RuleName=ListenPort,ConnectPort,MachineFunction", portFowardingRuleName); return(false); } int listenPort; if (!int.TryParse(portFowardingRuleComponents[0], out listenPort) || listenPort <= 0 || listenPort > 65535) { TextLogger.LogError("Port forwarding rule {0} is invalid with invalid listen port {1}. Valid rule format : RuleName=ListenPort,ConnectPort,MachineFunction", portFowardingRuleName, portFowardingRuleComponents[0]); return(false); } int connectPort; if (!int.TryParse(portFowardingRuleComponents[1], out connectPort) || connectPort <= 0 || connectPort > 65535) { TextLogger.LogError("Port forwarding rule {0} is invalid with invalid connect port {1}. Valid rule format : RuleName=ListenPort,ConnectPort,MachineFunction", portFowardingRuleName, portFowardingRuleComponents[1]); return(false); } PortFowardingRules.Add(new PortFowardingRule { RuleName = portFowardingRuleName, ListenPort = listenPort, ConnectPort = connectPort, MachineFunction = portFowardingRuleComponents[2] }); } } } } return(true); }
/* * Private key certificate files (each private key certificate has an encrypted private key file (.pfx.encr) and an encrypted password file (.pfx.password.encr) ) as well as public key certificate files (i.e. cer files) are part of ServiceFabricEnvironmentSync application packages. * * All certificates to be installed by ServiceFabricEnvironmentSync need to be listed in the form of "CertificateFriendlyName=CertificateDataDirectory" in certificate section of ServiceFabricEnvironmentSync configuration file. * 1) CertificateDataDirectory refers to a relative path from application package root directory. * 2) For a public key certificate, CertificateFriendlyName.cer should be present in CertificateDataDirectory. * 3) For a private key certificate, CertificateFriendlyName.pfx.encr + CertificateFriendlyName.password.encr should be present in CertificateDataDirectory. * * All certificates listed would be installed in LocalMachine\My or LocalMachine\CA certificate store. * * Sample certificate configuration sections * [ServiceFabricEnvironmentSync.Certificate.LocalMachine.My] * certA=data\certificates * certB=data\certificates * * [ServiceFabricEnvironmentSync.Certificate.LocalMachine.CertificateAuthority] * certC=data\certificates * certD=data\certificates */ public bool GetEnvironmentCertificatesFromCurrentConfigurations(StoreName storeName) { try { if (storeName != StoreName.My && storeName != StoreName.CertificateAuthority) { TextLogger.LogError("Certificate store name {0} is not supported. Supported store names : {1}, {2}.", storeName, StoreName.My, StoreName.CertificateAuthority); return(false); } string certificateListConfigurationSectionName = string.Format(CultureInfo.InvariantCulture, StringConstants.CertificateListConfigurationSectionNameTemplate, storeName); IConfiguration configuration = APConfiguration.GetConfiguration(); if (configuration != null && configuration.SectionExists(certificateListConfigurationSectionName)) { string[] certificateFriendlyNames = configuration.GetSectionKeys(certificateListConfigurationSectionName); if (certificateFriendlyNames != null) { foreach (string certificateFriendlyName in certificateFriendlyNames) { string certificateDataDirectoryName = configuration.GetStringValue(certificateListConfigurationSectionName, certificateFriendlyName); string certificateDataDirectory = Path.Combine(this.applicationDirectory, certificateDataDirectoryName); string encryptedPrivateKeyFile = Path.Combine( certificateDataDirectory, string.Format( CultureInfo.InvariantCulture, StringConstants.EncryptedPrivateKeyFileNameTemplate, certificateFriendlyName)); bool encryptedPrivateKeyFileExists = File.Exists(encryptedPrivateKeyFile); string encryptedPasswordFile = Path.Combine( certificateDataDirectory, string.Format( CultureInfo.InvariantCulture, StringConstants.EncryptedPasswordFileNameTemplate, certificateFriendlyName)); bool encryptedPasswordFileExists = File.Exists(encryptedPasswordFile); string publicKeyFile = Path.Combine( certificateDataDirectory, string.Format( CultureInfo.InvariantCulture, StringConstants.PublicKeyFileNameTemplate, certificateFriendlyName)); bool publicKeyFileExists = File.Exists(publicKeyFile); // TODO: report health if (!encryptedPrivateKeyFileExists && !encryptedPasswordFileExists && !publicKeyFileExists) { TextLogger.LogError("Neither encrypted private key & encrypted password files nor public key file exist for certificate {0} in certificate data directory {1}. A certificate to be installed needs to be either a public key certificate or a private key certificate.", certificateFriendlyName, certificateDataDirectory); return(false); } if ((encryptedPrivateKeyFileExists || encryptedPasswordFileExists) && publicKeyFileExists) { TextLogger.LogError("Both encrypted private key & encrypted password files and public key file exist for certificate {0} in certificate data directory {1}. A certificate to be installed needs to be either a public key certificate or a private key certificate but not both.", certificateFriendlyName, certificateDataDirectory); return(false); } if (encryptedPrivateKeyFileExists ^ encryptedPasswordFileExists) { TextLogger.LogError("Only one of encrypted private key file and encrypted password file exist for certificate {0} in certificate data directory {1}. Both are required to install a private key certificate.", certificateFriendlyName, certificateDataDirectory); return(false); } if (publicKeyFileExists) { PublicKeyCertificate publicKeyCertificate; if (!PublicKeyCertificate.TryLoadFromFile(publicKeyFile, out publicKeyCertificate)) { return(false); } if (this.environmentCertificates[storeName].ContainsKey(publicKeyCertificate.Thumbprint) && this.environmentCertificates[storeName][publicKeyCertificate.Thumbprint].HasPrivateKey) { TextLogger.LogError("Certificate with thumbprint {0}, subject {1} are requested to be installed as both public key certificate and private key certificate in configuration under different entries.", publicKeyCertificate.Thumbprint, publicKeyCertificate.Certificate.Subject); return(false); } this.environmentCertificates[storeName][publicKeyCertificate.Thumbprint] = publicKeyCertificate; TextLogger.LogInfo("Public key certificate with thumbprint {0}, subject {1} needs to be present in certificate store {2}\\{3} in local environment based on current configurations.", publicKeyCertificate.Thumbprint, publicKeyCertificate.Certificate.Subject, StoreLocation.LocalMachine, storeName); } else { if (storeName == StoreName.CertificateAuthority) { TextLogger.LogError("Private key certificate {0} cannot be installed in certificate store with name {1}.", certificateFriendlyName, storeName); return(false); } PrivateKeyCertificate privateKeyCertificate; if (!PrivateKeyCertificate.TryLoadFromFile(encryptedPrivateKeyFile, encryptedPasswordFile, out privateKeyCertificate)) { return(false); } if (this.environmentCertificates[storeName].ContainsKey(privateKeyCertificate.Thumbprint) && !this.environmentCertificates[storeName][privateKeyCertificate.Thumbprint].HasPrivateKey) { TextLogger.LogError("Certificate with thumbprint {0}, subject {1} are requested to be installed as both public key certificate and private key certificate in configuration under different entries.", privateKeyCertificate.Thumbprint, privateKeyCertificate.Certificate.Subject); return(false); } this.environmentCertificates[storeName][privateKeyCertificate.Thumbprint] = privateKeyCertificate; TextLogger.LogInfo("Private key certificate with thumbprint {0}, subject {1} needs to be present in certificate store {2}\\{3} in local environment based on current configurations.", privateKeyCertificate.Thumbprint, privateKeyCertificate.Certificate.Subject, StoreLocation.LocalMachine, storeName); } } } } return(true); } catch (Exception e) { TextLogger.LogError("Failed to get environment certificates based on current configurations : {0}", e); return(false); } }