diff options
author | Wouter Tinus <win.acme.simple@gmail.com> | 2019-12-14 05:20:13 +0100 |
---|---|---|
committer | Wouter Tinus <win.acme.simple@gmail.com> | 2019-12-14 05:20:13 +0100 |
commit | f084f256ed0fda699acdb86c51f95b1e5478c34f (patch) | |
tree | 82d8e8bdfe1733a729e9035a35a27a4e029c4603 /src | |
parent | 29d18063ceeac8f88191ab6f846a7145cb0c8341 (diff) | |
download | letsencrypt-win-simple-f084f256ed0fda699acdb86c51f95b1e5478c34f.zip letsencrypt-win-simple-f084f256ed0fda699acdb86c51f95b1e5478c34f.tar.gz letsencrypt-win-simple-f084f256ed0fda699acdb86c51f95b1e5478c34f.tar.bz2 |
extra user-friendlyness around wildcards in simple mode
Diffstat (limited to 'src')
-rw-r--r-- | src/main.lib/Clients/IIS/IISHelper.cs | 1 | ||||
-rw-r--r-- | src/main.lib/Plugins/TargetPlugins/IIS/IISOptionsFactory.cs | 93 | ||||
-rw-r--r-- | src/main.lib/Services/DomainParseService.cs | 89 | ||||
-rw-r--r-- | src/main.lib/Services/ProxyService.cs | 8 |
4 files changed, 147 insertions, 44 deletions
diff --git a/src/main.lib/Clients/IIS/IISHelper.cs b/src/main.lib/Clients/IIS/IISHelper.cs index bfb5e58..e6f62c3 100644 --- a/src/main.lib/Clients/IIS/IISHelper.cs +++ b/src/main.lib/Clients/IIS/IISHelper.cs @@ -21,6 +21,7 @@ namespace PKISharp.WACS.Clients.IIS public long SiteId { get; set; } public bool Https { get; set; } + public bool Wildcard => HostUnicode.StartsWith("*."); public string HostUnicode { get; private set; } public string HostPunycode { get; private set; } public int Port { get; set; } diff --git a/src/main.lib/Plugins/TargetPlugins/IIS/IISOptionsFactory.cs b/src/main.lib/Plugins/TargetPlugins/IIS/IISOptionsFactory.cs index 98cfb68..53bcb53 100644 --- a/src/main.lib/Plugins/TargetPlugins/IIS/IISOptionsFactory.cs +++ b/src/main.lib/Plugins/TargetPlugins/IIS/IISOptionsFactory.cs @@ -61,7 +61,7 @@ namespace PKISharp.WACS.Plugins.TargetPlugins var allSites = _iisHelper.GetSites(true).Where(x => x.Hosts.Any()).ToList(); if (!allSites.Any()) { - _log.Error($"No sites with named bindings have been configured in IIS. " + + _log.Error($"No sites with host bindings have been configured in IIS. " + $"Add one in the IIS Manager or choose the plugin '{ManualOptions.DescriptionText}' " + $"instead."); return null; @@ -70,22 +70,40 @@ namespace PKISharp.WACS.Plugins.TargetPlugins var visibleSites = allSites.Where(x => !_arguments.MainArguments.HideHttps || x.Https == false).ToList(); if (!visibleSites.Any()) { - _log.Error("No sites with named bindings remain after applying the --{hidehttps} filter. " + + _log.Error("No sites with host bindings remain after applying the --{hidehttps} filter. " + "It looks like all your websites are already configured for https!", "hidehttps"); return null; } + // Remove sites with only wildcard bindings because they cannot be validated in simple mode + if (!runLevel.HasFlag(RunLevel.Advanced)) + { + visibleSites = visibleSites.Where(x => x.Hosts.Any(h => !h.StartsWith("*"))).ToList(); + if (!visibleSites.Any()) + { + _log.Error("No sites with host bindings remain after discarding wildcard domains. To " + + "create certificates including wildcards, please use the 'Full options' mode, as " + + "this requires DNS validation."); + return null; + } + } + // Repeat the process until the user is happy with their settings do { var allBindings = _iisHelper.GetBindings(); var visibleBindings = allBindings.Where(x => !_arguments.MainArguments.HideHttps || x.Https == false).ToList(); + if (!runLevel.HasFlag(RunLevel.Advanced)) + { + // Hide bindings with wildcards because they cannot be validated in simple mode + visibleBindings = visibleBindings.Where(x => !x.Wildcard).ToList(); + } var ret = await TryAquireSettings(input, allBindings, visibleBindings, allSites, visibleSites, runLevel); if (ret != null) { var filtered = _iisHelper.FilterBindings(allBindings, ret); - await ListBindings(input, filtered, ret.CommonName); - if (await input.PromptYesNo("Apply these settings?", true)) + await ListBindings(input, runLevel, filtered, ret.CommonName); + if (await input.PromptYesNo("Continue with this selection?", true)) { return ret; } @@ -134,8 +152,10 @@ namespace PKISharp.WACS.Plugins.TargetPlugins } var filtered = _iisHelper.FilterBindings(visibleBindings, options); - await ListBindings(input, filtered); - input.Show(null, "You may either choose to include all listed bindings, or apply an additional filter", true); + await ListBindings(input, runLevel, filtered); + input.Show(null, + "You may either choose to include all listed bindings as host names in your certificate, " + + "or apply an additional filter. Different types of filters are available.", true); var askExclude = true; var filters = new List<Choice<Func<Task>>> { @@ -144,31 +164,42 @@ namespace PKISharp.WACS.Plugins.TargetPlugins return InputHosts( "Include bindings", input, allBindings, filtered, options, () => options.IncludeHosts, x => options.IncludeHosts = x); - }, "Pick specific bindings from a list"), + }, "Pick specific bindings from the list"), Choice.Create<Func<Task>>(() => { return InputPattern(input, options); - }, "Use simple pattern matching with * and ?"), + }, "Pick bindings based on a search pattern"), Choice.Create<Func<Task>>(() => { askExclude = false; return Task.CompletedTask; - }, "None", @default: true) + }, "Pick *all* bindings", @default: true) }; if (runLevel.HasFlag(RunLevel.Advanced)) { filters.Insert(2, Choice.Create<Func<Task>>(() => { askExclude = true; return InputRegex(input, options); - }, "Use a regular expression")); + }, "Pick bindings based on a regular expression")); } - var chosen = await input.ChooseFromList("Binding filter", filters); + var chosen = await input.ChooseFromList("How do you want to pick the bindings?", filters); await chosen.Invoke(); filtered = _iisHelper.FilterBindings(allBindings, options); - var listForCommon = false; + + // Check for wildcards in simple mode + if (!runLevel.HasFlag(RunLevel.Advanced) && filtered.Any(x => x.Wildcard)) + { + await ListBindings(input, runLevel, filtered); + input.Show(null, "The pattern that you've chosen matches a wildcard binding, which " + + "is not supported by the 'simple' mode of this program because it requires DNS " + + "validation. Please try again with a different pattern or use the 'full options' " + + "mode instead.", true); + return null; + } // Exclude specific bindings + var listForCommon = false; if (askExclude && filtered.Count > 1 && runLevel.HasFlag(RunLevel.Advanced)) { - await ListBindings(input, filtered); + await ListBindings(input, runLevel, filtered); input.Show(null, "The listed bindings match your current filter settings. " + "If you wish to exclude one or more of them from the certificate, please " + "input those bindings now. Press <ENTER> to include all listed bindings.", true); @@ -193,7 +224,7 @@ namespace PKISharp.WACS.Plugins.TargetPlugins // the previously printed list if (listForCommon) { - await ListBindings(input, filtered); + await ListBindings(input, runLevel, filtered); } await InputCommonName(input, filtered, options); } @@ -251,6 +282,9 @@ namespace PKISharp.WACS.Plugins.TargetPlugins string raw; do { + input.Show(null, "Please pick the most important host name from the list. " + + "This will be displayed to your users as the subject of the certificate.", + true); raw = await input.RequestString("Common name"); if (!string.IsNullOrEmpty(raw)) { @@ -351,17 +385,36 @@ namespace PKISharp.WACS.Plugins.TargetPlugins /// <param name="bindings"></param> /// <param name="highlight"></param> /// <returns></returns> - private async Task ListBindings(IInputService input, List<IISHelper.IISBindingOption> bindings, string? highlight = null) + private async Task ListBindings(IInputService input, RunLevel runLevel, List<IISHelper.IISBindingOption> bindings, string? highlight = null) { var sortedBindings = SortBindings(bindings); await input.WritePagedList( sortedBindings.Select(x => Choice.Create( item: x, - color: x.HostUnicode == highlight ? - ConsoleColor.Green : - x.Https ? - ConsoleColor.DarkGray : - default))); + color: BindingColor(x, runLevel, highlight)))); + } + + private ConsoleColor? BindingColor(IISHelper.IISBindingOption binding, RunLevel runLevel, string? highlight = null) + { + if (!runLevel.HasFlag(RunLevel.Advanced) && binding.Wildcard) + { + return ConsoleColor.Red; + } + if (binding.HostUnicode == highlight) + { + return ConsoleColor.Green; + } + else + { + if (binding.Https) + { + return ConsoleColor.DarkGray; + } + else + { + return default(ConsoleColor); + } + } } public override async Task<IISOptions?> Default() diff --git a/src/main.lib/Services/DomainParseService.cs b/src/main.lib/Services/DomainParseService.cs index e3fa80a..e16f5b2 100644 --- a/src/main.lib/Services/DomainParseService.cs +++ b/src/main.lib/Services/DomainParseService.cs @@ -15,15 +15,26 @@ namespace PKISharp.WACS.Services public DomainParseService(ILogService log, ProxyService proxy, ISettingsService settings) { var path = Path.Combine(Path.GetDirectoryName(settings.ExePath), "public_suffix_list.dat"); - _parser = new DomainParser(new FileTldRuleProvider(path)); try { - _parser = new DomainParser(new WebTldRuleProvider(proxy, settings)); + _parser = new DomainParser(new FileTldRuleProvider(path)); + } + catch (Exception ex) + { + log.Warning("Error loading static public suffix list from {path}: {ex}", path, ex.Message); + } + try + { + _parser = new DomainParser(new WebTldRuleProvider(proxy, log, settings)); } catch (Exception ex) { log.Warning("Error updating public suffix list from {source}: {ex}", Source, ex.Message); } + if (_parser == null) + { + throw new Exception(); + } } public string GetTLD(string fulldomain) => _parser.Get(fulldomain).TLD; @@ -35,19 +46,63 @@ namespace PKISharp.WACS.Services /// </summary> private class FileCacheProvider : ICacheProvider { - private readonly FileInfo _file; - - public FileCacheProvider(ISettingsService settings) + private readonly FileInfo? _file; + private string? _memoryCache; + private readonly ILogService _log; + + public FileCacheProvider(ILogService log, ISettingsService settings) { - var path = Path.Combine(settings.Client.ConfigurationPath, "public_suffix_list.dat"); - _file = new FileInfo(path); + _log = log; + if (settings?.Client?.ConfigurationPath != null) + { + var path = Path.Combine(settings.Client.ConfigurationPath, "public_suffix_list.dat"); + _file = new FileInfo(path); + } } - public Task<string> GetAsync() => File.ReadAllTextAsync(_file.FullName); + public async Task<string> GetAsync() + { + if (_file != null) + { + try + { + _memoryCache = await File.ReadAllTextAsync(_file.FullName); + } + catch (Exception ex) + { + _log.Warning("Unable to read public suffix list cache from {path}: {ex}", _file.FullName, ex.Message); + }; + } + return _memoryCache ?? ""; + } - public bool IsCacheValid() => _file.Exists && _file.LastWriteTimeUtc > DateTime.UtcNow.AddDays(-30); + public bool IsCacheValid() + { + if (_file != null) + { + return _file.Exists && _file.LastWriteTimeUtc > DateTime.UtcNow.AddDays(-30); + } + else + { + return !string.IsNullOrEmpty(_memoryCache); + } + } - public Task SetAsync(string val) => File.WriteAllTextAsync(_file.FullName, val); + public async Task SetAsync(string val) + { + if (_file != null) + { + try + { + await File.WriteAllTextAsync(_file.FullName, val); + } + catch (Exception ex) + { + _log.Warning("Unable to write public suffix list cache to {path}: {ex}", _file.FullName, ex.Message); + } + } + _memoryCache = val; + } } /// <summary> @@ -60,10 +115,10 @@ namespace PKISharp.WACS.Services private readonly ProxyService _proxy; private readonly ICacheProvider _cache; - public WebTldRuleProvider(ProxyService proxy, ISettingsService settings) + public WebTldRuleProvider(ProxyService proxy, ILogService log, ISettingsService settings) { _fileUrl = "https://publicsuffix.org/list/public_suffix_list.dat"; - _cache = new FileCacheProvider(settings); + _cache = new FileCacheProvider(log, settings); _proxy = proxy; } @@ -73,12 +128,12 @@ namespace PKISharp.WACS.Services string ruleData; if (!_cache.IsCacheValid()) { - ruleData = await LoadFromUrl(_fileUrl).ConfigureAwait(false); - await _cache.SetAsync(ruleData).ConfigureAwait(false); + ruleData = await LoadFromUrl(_fileUrl); + await _cache.SetAsync(ruleData); } else { - ruleData = await _cache.GetAsync().ConfigureAwait(false); + ruleData = await _cache.GetAsync(); } var rules = ruleParser.ParseRules(ruleData); return rules; @@ -87,12 +142,12 @@ namespace PKISharp.WACS.Services public async Task<string> LoadFromUrl(string url) { using var httpClient = _proxy.GetHttpClient(); - using var response = await httpClient.GetAsync(url).ConfigureAwait(false); + using var response = await httpClient.GetAsync(url); if (!response.IsSuccessStatusCode) { throw new RuleLoadException($"Cannot load from {url} {response.StatusCode}"); } - return await response.Content.ReadAsStringAsync().ConfigureAwait(false); + return await response.Content.ReadAsStringAsync(); } } diff --git a/src/main.lib/Services/ProxyService.cs b/src/main.lib/Services/ProxyService.cs index 49bf293..10ba47d 100644 --- a/src/main.lib/Services/ProxyService.cs +++ b/src/main.lib/Services/ProxyService.cs @@ -19,13 +19,7 @@ namespace PKISharp.WACS.Services /// <summary> /// Is the user requesting the system proxy /// </summary> - public bool UseSystemProxy - { - get - { - return _settings.Proxy.Url.Equals("[System]", StringComparison.OrdinalIgnoreCase); ; - } - } + public bool UseSystemProxy => string.Equals(_settings.Proxy.Url, "[System]", StringComparison.OrdinalIgnoreCase); /// <summary> /// Get prepared HttpClient with correct system proxy settings |