summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorWouter Tinus <win.acme.simple@gmail.com>2019-12-14 05:20:13 +0100
committerWouter Tinus <win.acme.simple@gmail.com>2019-12-14 05:20:13 +0100
commitf084f256ed0fda699acdb86c51f95b1e5478c34f (patch)
tree82d8e8bdfe1733a729e9035a35a27a4e029c4603 /src
parent29d18063ceeac8f88191ab6f846a7145cb0c8341 (diff)
downloadletsencrypt-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.cs1
-rw-r--r--src/main.lib/Plugins/TargetPlugins/IIS/IISOptionsFactory.cs93
-rw-r--r--src/main.lib/Services/DomainParseService.cs89
-rw-r--r--src/main.lib/Services/ProxyService.cs8
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