/// <summary> /// Write contents of a web.config. /// /// Takes into consideration maximum web.config file size as defined at the OS level. /// /// Throws an exception if size is exceeded. /// </summary> /// <param name="webconfigfilepath"></param> /// <param name="webConfigContents"></param> /// <returns></returns> public static void WriteWebConfig(string webconfigfilepath, string webConfigContents) { long requiredSizeKb = UtilsSystem.GetStringSizeInDiskBytes(webConfigContents) / 1024; int maxSizeKb = UtilsIis.GetMaxWebConfigFileSizeInKb() - 1; if (requiredSizeKb >= maxSizeKb) { throw new Exception($"Required web.config size of {requiredSizeKb}Kb for CDN chef feature exceeds current limit of {maxSizeKb}Kb. Please, review this in 'HKLM\\SOFTWARE\\Microsoft\\InetStp\\Configuration\\MaxWebConfigFileSizeInKB'"); } File.WriteAllText(webconfigfilepath, webConfigContents); }
/// <summary> /// Set specific account anonymous authentication for an IIS application /// /// The default behaviour for IIS is to have the ANONYMOUS user (that is /// used for all request) identified as IUSR. When using FAST-CGI impersonation, /// we WANT all permissions to be based on the application pool identity... /// </summary> /// <param name="siteName"></param> /// <param name="username"></param> /// <param name="password"></param> public static void ConfigureAnonymousAuthForIisApplication( string siteName, string username, string password) { using (ServerManager serverManager = new ServerManager()) { // fastCgi settings in IIS can only be set at the HOSTS level // we found no way to set this at a web.config level. Configuration config = serverManager.GetApplicationHostConfiguration(); ConfigurationSection section; // TODO: The type of authentication and it's configuration should be configurable here... // see https://www.iis.net/configreference/system.webserver/security/authentication section = config.GetSection("system.webServer/security/authentication/anonymousAuthentication", siteName); section["enabled"] = true; section["password"] = password; section["username"] = username; UtilsIis.CommitChanges(serverManager); } }
/// <summary> /// Find a certificate in IIS central certificate store /// </summary> /// <param name="hostName"></param> /// <param name="logger"></param> /// <param name="certificatePath"></param> /// <returns></returns> public static X509Certificate2 FindCertificateInCentralCertificateStore( string hostName, ILoggerInterface logger, out string certificatePath) { string centralStorePath = UtilsIis.CentralStorePath(logger); FileInfo certificateFile = null; // Look for a certificate file that includes wildcard matching logic // https://serverfault.com/questions/901494/iis-wildcard-https-binding-with-centralized-certificate-store var hostNameParts = hostName.Split(".".ToCharArray()).Reverse().ToList(); foreach (var f in new DirectoryInfo(centralStorePath).EnumerateFiles()) { // Check if this certificate file is valid for the hostname... var certNameParts = Path.GetFileNameWithoutExtension(f.FullName).Split(".".ToCharArray()).Reverse() .ToList(); // This won't allow for nested subdomain with wildcards, but it's a good starting point // i.e. a hostname such as "a.mytest.mydomain.com" won't be matched to a certifica // such as "_.mydomain.com" // but "mytest.mydomain.com" will match to "_.mydomain.com". if (certNameParts.Count != hostNameParts.Count) { continue; } bool isMatch = true; for (int x = 0; x < hostNameParts.Count; x++) { if (hostNameParts[x] == "*" || certNameParts[x] == "_") { continue; } if (hostNameParts[x] != certNameParts[x]) { isMatch = false; break; } } if (isMatch) { certificateFile = f; break; } } certificatePath = certificateFile?.FullName; // This is null on purpose. string certificatePassword = null; X509Certificate2Collection collection = new X509Certificate2Collection(); if (certificateFile != null) { logger.LogInfo(true, "Found potential certificate matching file at {0}", certificateFile.FullName); try { // Usamos ephemeral keyset para que no almacene claves en la máquina todo el tiempo... collection.Import(certificateFile.FullName, certificatePassword, X509KeyStorageFlags.EphemeralKeySet); var originalCert = collection[0]; logger.LogInfo(true, "Certificate IssuerName '{0}'", originalCert.IssuerName.Name); logger.LogInfo(true, "Certificate FriendlyName '{0}'", originalCert.FriendlyName); logger.LogInfo(true, "Certificate SubjectName '{0}'", originalCert.SubjectName.Name); logger.LogInfo(true, "Certificate NotBefore '{0}'", originalCert.NotBefore.ToString("HH:mm:ss yyyy/MM/dd")); return(originalCert); } catch (Exception e) { logger.LogWarning(false, $"Error importing certificate: '{certificateFile.FullName}'." + e.Message); } } return(null); }
/// <summary> /// Changes the state of a site (start/stop/reset) including it's application pools. /// /// The changes are made persistent through the ServerAutoStart option. /// </summary> /// <param name="sitename"></param> /// <param name="action"></param> /// <param name="skipApplicationPools"></param> /// <returns></returns> public void WebsiteAction( string sitename, AppPoolActionType action, bool skipApplicationPools = false) { using (ServerManager manager = new ServerManager()) { // Buscamos el site.... var site = UtilsIis.FindSiteWithName(manager, sitename, this.Logger).SingleOrDefault(); if (site == null) { return; } // Cargamos TODOS los application pools de ese site... List <ApplicationPool> pools = new List <ApplicationPool>(); if (!skipApplicationPools) { foreach (var s in site.Applications) { var applicationPool = manager.ApplicationPools.SingleOrDefault(i => i.Name == s.ApplicationPoolName); if (applicationPool == null) { throw new Exception( string.Format( "Could not find application pool with name '{3}' for application with path '{0}' and site '{1}' ({2}).", s.Path, site.Name, site.Id, s.ApplicationPoolName)); } pools.Add(applicationPool); } } switch (action) { case AppPoolActionType.Start: // Start all pools, then the site foreach (var p in pools) { this.StartAppPool(p); } this.StartSite(site); site.ServerAutoStart = true; break; case AppPoolActionType.Stop: // Stop site, then pools this.StopSite(site, 10000); foreach (var p in pools) { this.StopAppPool(p, 10000); } site.ServerAutoStart = false; break; case AppPoolActionType.Reset: // Stop site this.StopSite(site, 10000); // Stop pools foreach (var p in pools) { this.StopAppPool(p, 10000); } // Start pools foreach (var p in pools) { this.StartAppPool(p); } // Start site this.StartSite(site); break; } // Commit because we changed the autostart property UtilsIis.CommitChanges(manager); } }