public virtual bool SupportsRunLevel(RunLevel runLevel) { foreach (var attribute in attributes) { foreach (string s in attribute.RunLevels) { if (AnyRunLevel.Default.Equals(s) || runLevel.Equals(s)) { return true; } } } return false; }
public async Task <RenewResult?> Execute(Renewal renewal, RunLevel runLevel) { using var ts = _scopeBuilder.Target(_container, renewal, runLevel); using var es = _scopeBuilder.Execution(ts, renewal, runLevel); // Generate the target var targetPlugin = es.Resolve <ITargetPlugin>(); var(disabled, disabledReason) = targetPlugin.Disabled; if (disabled) { throw new Exception($"Target plugin is not available. {disabledReason}"); } var target = await targetPlugin.Generate(); if (target is INull) { throw new Exception($"Target plugin did not generate a target"); } if (!target.IsValid(_log)) { throw new Exception($"Target plugin generated an invalid target"); } // Check if our validation plugin is (still) up to the task var validationPlugin = es.Resolve <IValidationPluginOptionsFactory>(); if (!validationPlugin.CanValidate(target)) { throw new Exception($"Validation plugin is unable to validate the target. A wildcard host was introduced into a HTTP validated renewal."); } // Check if renewal is needed if (!runLevel.HasFlag(RunLevel.ForceRenew) && !renewal.Updated) { _log.Verbose("Checking {renewal}", renewal.LastFriendlyName); if (!renewal.IsDue()) { var cs = es.Resolve <ICertificateService>(); var cache = cs.CachedInfo(renewal, target); if (cache != null) { _log.Information("Renewal for {renewal} is due after {date}", renewal.LastFriendlyName, renewal.GetDueDate()); return(null); } else if (!renewal.New) { _log.Information(LogType.All, "Renewal for {renewal} running prematurely due to detected target change", renewal.LastFriendlyName); } } else if (!renewal.New) { _log.Information(LogType.All, "Renewing certificate for {renewal}", renewal.LastFriendlyName); } } else if (runLevel.HasFlag(RunLevel.ForceRenew)) { _log.Information(LogType.All, "Force renewing certificate for {renewal}", renewal.LastFriendlyName); } // Create the order var orderManager = es.Resolve <OrderManager>(); var order = await orderManager.GetOrCreate(renewal, target, runLevel); if (order == null) { return(OnRenewFail(new Challenge() { Error = "Unable to create order" })); } // Answer the challenges var client = es.Resolve <AcmeClient>(); foreach (var authUrl in order.Payload.Authorizations) { // Get authorization details _log.Verbose("Handle authorization {n}/{m}", order.Payload.Authorizations.ToList().IndexOf(authUrl) + 1, order.Payload.Authorizations.Length + 1); var authorization = await client.GetAuthorizationDetails(authUrl); // Find a targetPart that matches the challenge var targetPart = target.Parts. FirstOrDefault(tp => tp.GetHosts(false). Any(h => authorization.Identifier.Value == h.Replace("*.", ""))); if (targetPart == null) { return(OnRenewFail(new Challenge() { Error = "Unable to match challenge to target" })); } // Run the validation plugin var challenge = await Authorize(es, runLevel, renewal.ValidationPluginOptions, targetPart, authorization); if (challenge.Status != AcmeClient.AuthorizationValid) { return(OnRenewFail(challenge)); } } return(await OnValidationSuccess(es, renewal, target, order, runLevel)); }
/// <summary> /// Steps to take on succesful (re)authorization /// </summary> /// <param name="target"></param> private async Task <RenewResult?> OnValidationSuccess(ILifetimeScope renewalScope, Renewal renewal, Target target, OrderDetails order, RunLevel runLevel) { RenewResult?result = null; try { var certificateService = renewalScope.Resolve <ICertificateService>(); var csrPlugin = target.CsrBytes == null?renewalScope.Resolve <ICsrPlugin>() : null; if (csrPlugin != null) { var(disabled, disabledReason) = csrPlugin.Disabled; if (disabled) { return(new RenewResult($"CSR plugin is not available. {disabledReason}")); } } var oldCertificate = certificateService.CachedInfo(renewal); var newCertificate = await certificateService.RequestCertificate(csrPlugin, runLevel, renewal, target, order); // Test if a new certificate has been generated if (newCertificate == null) { return(new RenewResult("No certificate generated")); } else { result = new RenewResult(newCertificate); } // Early escape for testing validation only if (renewal.New && runLevel.HasFlag(RunLevel.Test) && !await _input.PromptYesNo($"[--test] Do you want to install the certificate?", true)) { return(null); } // Run store plugin(s) var storePluginOptions = new List <StorePluginOptions>(); var storePlugins = new List <IStorePlugin>(); try { var steps = renewal.StorePluginOptions.Count(); for (var i = 0; i < steps; i++) { var storeOptions = renewal.StorePluginOptions[i]; var storePlugin = (IStorePlugin)renewalScope.Resolve(storeOptions.Instance); if (!(storePlugin is INull)) { if (steps > 1) { _log.Information("Store step {n}/{m}: {name}...", i + 1, steps, storeOptions.Name); } else { _log.Information("Store with {name}...", storeOptions.Name); } var(disabled, disabledReason) = storePlugin.Disabled; if (disabled) { return(new RenewResult($"Store plugin is not available. {disabledReason}")); } await storePlugin.Save(newCertificate); storePlugins.Add(storePlugin); storePluginOptions.Add(storeOptions); } } } catch (Exception ex) { var reason = _exceptionHandler.HandleException(ex, "Unable to store certificate"); result.ErrorMessage = $"Store failed: {reason}"; result.Success = false; return(result); } // Run installation plugin(s) try { var steps = renewal.InstallationPluginOptions.Count(); for (var i = 0; i < steps; i++) { var installOptions = renewal.InstallationPluginOptions[i]; var installPlugin = (IInstallationPlugin)renewalScope.Resolve( installOptions.Instance, new TypedParameter(installOptions.GetType(), installOptions)); if (!(installPlugin is INull)) { if (steps > 1) { _log.Information("Installation step {n}/{m}: {name}...", i + 1, steps, installOptions.Name); } else { _log.Information("Installing with {name}...", installOptions.Name); } var(disabled, disabledReason) = installPlugin.Disabled; if (disabled) { return(new RenewResult($"Installation plugin is not available. {disabledReason}")); } await installPlugin.Install(storePlugins, newCertificate, oldCertificate); } } } catch (Exception ex) { var reason = _exceptionHandler.HandleException(ex, "Unable to install certificate"); result.Success = false; result.ErrorMessage = $"Install failed: {reason}"; } // Delete the old certificate if not forbidden, found and not re-used for (var i = 0; i < storePluginOptions.Count; i++) { if (!storePluginOptions[i].KeepExisting && oldCertificate != null && newCertificate.Certificate.Thumbprint != oldCertificate.Certificate.Thumbprint) { try { await storePlugins[i].Delete(oldCertificate); } catch (Exception ex) { _log.Error(ex, "Unable to delete previous certificate"); //result.Success = false; // not a show-stopper, consider the renewal a success result.ErrorMessage = $"Delete failed: {ex.Message}"; } } } if ((renewal.New || renewal.Updated) && !_args.NoTaskScheduler) { if (runLevel.HasFlag(RunLevel.Test) && !await _input.PromptYesNo($"[--test] Do you want to automatically renew this certificate?", true)) { // Early out for test runs return(null); } else { // Make sure the Task Scheduler is configured await renewalScope.Resolve <TaskSchedulerService>().EnsureTaskScheduler(runLevel, false); } } return(result); } catch (Exception ex) { _exceptionHandler.HandleException(ex); while (ex.InnerException != null) { ex = ex.InnerException; } // Result might still contain the Thumbprint of the certificate // that was requested and (partially? installed, which might help // with debugging if (result == null) { result = new RenewResult(ex.Message); } else { result.Success = false; result.ErrorMessage = ex.Message; } } return(result); }
public override FileSystemOptions Aquire(Target target, IArgumentsService arguments, IInputService inputService, RunLevel runLevel) { // Choose alternative site for validation var ret = new FileSystemOptions(BaseAquire(target, arguments, inputService, runLevel)); if (target.IIS && _iisClient.HasWebSites && string.IsNullOrEmpty(ret.Path)) { if (inputService.PromptYesNo("Use different site for validation?", false)) { var site = inputService.ChooseFromList("Validation site, must receive requests for all hosts on port 80", _iisClient.WebSites, x => Choice.Create(x, x.Name, x.Id.ToString()), "Automatic (target site)"); if (site != null) { ret.Path = site.Path; ret.SiteId = site.Id; } } } return(ret); }
Target ITargetPlugin.Aquire(IOptionsService optionsService, IInputService inputService, RunLevel runLevel) { var targets = GetSites(optionsService.Options.HideHttps, true).Where(x => x.Hidden == false).ToList(); inputService.WritePagedList(targets.Select(x => Choice.Create(x, $"{x.Host} ({x.AlternativeNames.Count()} bindings) [@{x.WebRootPath}]", x.TargetSiteId.ToString())).ToList()); var sanInput = inputService.RequestString("Enter a comma separated list of site IDs, or 'S' to run for all sites").ToLower().Trim(); var totalTarget = GetCombinedTarget(targets, sanInput); inputService.WritePagedList(totalTarget.AlternativeNames.Select(x => Choice.Create(x, ""))); totalTarget.ExcludeBindings = inputService.RequestString("Press enter to include all listed hosts, or type a comma-separated lists of exclusions"); if (runLevel >= RunLevel.Advanced) { totalTarget.AskForCommonNameChoice(inputService); } return(totalTarget); }
/// <summary> /// For renewal and creating scheduled task /// </summary> /// <param name="target"></param> /// <param name="renewal"></param> /// <param name="runLevel"></param> /// <returns></returns> public ILifetimeScope Execution(ILifetimeScope target, Renewal renewal, RunLevel runLevel) { return(target.BeginLifetimeScope(builder => { builder.Register(c => runLevel).As <RunLevel>(); builder.RegisterType <FindPrivateKey>().SingleInstance(); // Used to configure TaskScheduler without renewal if (renewal != null) { builder.RegisterInstance(renewal); builder.RegisterInstance(renewal.StorePluginOptions).As(renewal.StorePluginOptions.GetType()); if (renewal.CsrPluginOptions != null) { builder.RegisterInstance(renewal.CsrPluginOptions).As(renewal.CsrPluginOptions.GetType()); } if (renewal.OrderPluginOptions != null) { builder.RegisterInstance(renewal.OrderPluginOptions).As(renewal.OrderPluginOptions.GetType()); } builder.RegisterInstance(renewal.ValidationPluginOptions).As(renewal.ValidationPluginOptions.GetType()); builder.RegisterInstance(renewal.TargetPluginOptions).As(renewal.TargetPluginOptions.GetType()); // Find factory based on options builder.Register(x => { var plugin = x.Resolve <IPluginService>(); var match = plugin.GetFactories <IValidationPluginOptionsFactory>(target).First(vp => vp.OptionsType.PluginId() == renewal.ValidationPluginOptions.Plugin); if (match == null) { return new NullValidationFactory(); } return match; }).As <IValidationPluginOptionsFactory>().SingleInstance(); if (renewal.CsrPluginOptions != null) { builder.RegisterType(renewal.CsrPluginOptions.Instance).As <ICsrPlugin>().SingleInstance(); } if (renewal.OrderPluginOptions != null) { builder.RegisterType(renewal.OrderPluginOptions.Instance).As <IOrderPlugin>().SingleInstance(); } else { builder.RegisterType <Plugins.OrderPlugins.Single>().As <IOrderPlugin>().SingleInstance(); } builder.RegisterType(renewal.ValidationPluginOptions.Instance).As <IValidationPlugin>().SingleInstance(); builder.RegisterType(renewal.TargetPluginOptions.Instance).As <ITargetPlugin>().SingleInstance(); foreach (var i in renewal.InstallationPluginOptions) { builder.RegisterInstance(i).As(i.GetType()); } foreach (var i in renewal.StorePluginOptions) { builder.RegisterInstance(i).As(i.GetType()); } } })); }
public override async Task <AcmeOptions?> Aquire(Target target, IInputService input, RunLevel runLevel) { var ret = new AcmeOptions(); Uri?uri = null; while (ret.BaseUri == null) { try { var userInput = await input.RequestString("URL of the acme-dns server"); uri = new Uri(userInput); ret.BaseUri = uri.ToString(); } catch { } } if (uri == null) { return(null); } var acmeDnsClient = new AcmeDnsClient(_dnsClient, _proxy, _log, _settings, input, uri); var identifiers = target.Parts.SelectMany(x => x.Identifiers).Distinct(); foreach (var identifier in identifiers) { var registrationResult = await acmeDnsClient.EnsureRegistration(identifier.Replace("*.", ""), true); if (!registrationResult) { return(null); } } return(ret); }
/// <summary> /// Request certificate from the ACME server /// </summary> /// <param name="csrPlugin">Plugin used to generate CSR if it has not been provided in the target</param> /// <param name="runLevel"></param> /// <param name="renewal"></param> /// <param name="target"></param> /// <param name="order"></param> /// <returns></returns> public async Task <CertificateInfo> RequestCertificate( ICsrPlugin?csrPlugin, RunLevel runLevel, Renewal renewal, Target target, OrderDetails order) { // What are we going to get? var cacheKey = CacheKey(renewal, target); var pfxFileInfo = new FileInfo(GetPath(renewal, $"-{cacheKey}{PfxPostFix}")); // Determine/check the common name var identifiers = target.GetHosts(false); var commonNameUni = target.CommonName; var commonNameAscii = string.Empty; if (!string.IsNullOrWhiteSpace(commonNameUni)) { var idn = new IdnMapping(); commonNameAscii = idn.GetAscii(commonNameUni); if (!identifiers.Contains(commonNameAscii, StringComparer.InvariantCultureIgnoreCase)) { _log.Warning($"Common name {commonNameUni} provided is invalid."); commonNameAscii = identifiers.First(); commonNameUni = idn.GetUnicode(commonNameAscii); } } // Determine the friendly name var friendlyNameBase = renewal.FriendlyName; if (string.IsNullOrEmpty(friendlyNameBase)) { friendlyNameBase = target.FriendlyName; } if (string.IsNullOrEmpty(friendlyNameBase)) { friendlyNameBase = commonNameUni; } var friendyName = $"{friendlyNameBase} @ {_inputService.FormatDate(DateTime.Now)}"; // Try using cached certificate first to avoid rate limiting during // (initial?) deployment troubleshooting. Real certificate requests // will only be done once per day maximum unless the --force parameter // is used. var cache = CachedInfo(renewal, target); if (cache != null && cache.CacheFile != null) { if (cache.CacheFile.LastWriteTime > DateTime.Now.AddDays(_settings.Cache.ReuseDays * -1)) { if (runLevel.HasFlag(RunLevel.IgnoreCache)) { _log.Warning("Cached certificate available but not used with the --{switch} switch. " + "Use 'Manage renewals > Run renewal' in the main menu to run unscheduled " + "renewals without hitting rate limits.", nameof(MainArguments.Force).ToLower()); } else { _log.Warning("Using cached certificate for {friendlyName}. To force issue of a " + "new certificate within {days} days, delete the .pfx file from the CertificatePath " + "or run with the --{switch} switch. Be ware that you might run into rate " + "limits doing so.", friendlyNameBase, _settings.Cache.ReuseDays, nameof(MainArguments.Force).ToLower()); return(cache); } } } // Clear cache and write new cert ClearCache(renewal, postfix: CsrPostFix); if (target.CsrBytes == null) { if (csrPlugin == null) { throw new InvalidOperationException("Missing csrPlugin"); } var keyFile = GetPath(renewal, ".keys"); var csr = await csrPlugin.GenerateCsr(keyFile, commonNameAscii, identifiers); var keySet = await csrPlugin.GetKeys(); target.CsrBytes = csr.GetDerEncoded(); target.PrivateKey = keySet.Private; var csrPath = GetPath(renewal, CsrPostFix); File.WriteAllText(csrPath, _pemService.GetPem("CERTIFICATE REQUEST", target.CsrBytes)); _log.Debug("CSR stored at {path} in certificate cache folder {folder}", Path.GetFileName(csrPath), Path.GetDirectoryName(csrPath)); } _log.Verbose("Submitting CSR"); order = await _client.SubmitCsr(order, target.CsrBytes); if (order.Payload.Status != AcmeClient.OrderValid) { _log.Error("Unexpected order status {status}", order.Payload.Status); throw new Exception($"Unable to complete order"); } _log.Information("Requesting certificate {friendlyName}", friendlyNameBase); var rawCertificate = await _client.GetCertificate(order); if (rawCertificate == null) { throw new Exception($"Unable to get certificate"); } // Build pfx archive including any intermediates provided var text = Encoding.UTF8.GetString(rawCertificate); var pfx = new bc.Pkcs.Pkcs12Store(); var startIndex = 0; var endIndex = 0; const string startString = "-----BEGIN CERTIFICATE-----"; const string endString = "-----END CERTIFICATE-----"; while (true) { startIndex = text.IndexOf(startString, startIndex); if (startIndex < 0) { break; } endIndex = text.IndexOf(endString, startIndex); if (endIndex < 0) { break; } endIndex += endString.Length; var pem = text[startIndex..endIndex];
Target ITargetPlugin.Aquire(IOptionsService optionsService, IInputService inputService, RunLevel runLevel) { return(inputService.ChooseFromList("Choose site", GetBindings(optionsService.Options.HideHttps, true).Where(x => x.Hidden == false), x => Choice.Create(x, description: $"{x.Host} (SiteId {x.TargetSiteId}) [@{x.WebRootPath}]"), true)); }
public abstract TOptions Aquire(Target target, IOptionsService optionsService, IInputService inputService, RunLevel runLevel);
InstallationPluginOptions IInstallationPluginOptionsFactory.Aquire(Target target, IOptionsService optionsService, IInputService inputService, RunLevel runLevel) { return(Aquire(target, optionsService, inputService, runLevel)); }
/// <summary> /// Single round of aquiring settings /// </summary> /// <param name="input"></param> /// <param name="allBindings"></param> /// <param name="visibleBindings"></param> /// <param name="allSites"></param> /// <param name="visibleSites"></param> /// <param name="runLevel"></param> /// <returns></returns> private async Task <IISOptions?> TryAquireSettings( IInputService input, List <IISHelper.IISBindingOption> allBindings, List <IISHelper.IISBindingOption> visibleBindings, List <IISHelper.IISSiteOption> allSites, List <IISHelper.IISSiteOption> visibleSites, RunLevel runLevel) { input.CreateSpace(); input.Show(null, "Please select which website(s) should be scanned for host names. " + "You may input one or more site identifiers (comma-separated) to filter by those sites, " + "or alternatively leave the input empty to scan *all* websites."); var options = new IISOptions(); await input.WritePagedList( visibleSites.Select(x => Choice.Create( item: x, description: $"{x.Name} ({x.Hosts.Count()} binding{(x.Hosts.Count() == 1 ? "" : "s")})", command: x.Id.ToString(), color: x.Https ? ConsoleColor.DarkGray : (ConsoleColor?)null))); var raw = await input.RequestString("Site identifier(s) or <Enter> to choose all"); if (!ParseSiteOptions(raw, allSites, options)) { return(null); } var filtered = _iisHelper.FilterBindings(visibleBindings, options); await ListBindings(input, filtered); input.CreateSpace(); input.Show(null, "Listed above are the bindings found on the selected site(s). By default all of " + "them will be included, but you may either pick specific ones by typing the host names " + "or identifiers (comma-separated) or filter them using one of the options from the " + "menu."); var askExclude = true; var filters = new List <Choice <Func <Task> > > { Choice.Create <Func <Task> >(() => { return(InputPattern(input, options)); }, "Pick bindings based on a search pattern", command: "P"), Choice.Create <Func <Task> >(() => { askExclude = false; return(Task.CompletedTask); }, "Pick *all* bindings", @default: true, command: "A") }; if (runLevel.HasFlag(RunLevel.Advanced)) { filters.Insert(1, Choice.Create <Func <Task> >(() => { askExclude = true; return(InputRegex(input, options)); }, "Pick bindings based on a regular expression", command: "R")); } // Handle undefined input Choice <Func <Task> > processUnkown(string unknown) { return(Choice.Create <Func <Task> >(() => { askExclude = false; return ProcessInputHosts( unknown, allBindings, filtered, options, () => options.IncludeHosts, x => options.IncludeHosts = x); })); } var chosen = await input.ChooseFromMenu( "Binding identifiers(s) or menu option", filters, processUnkown); await chosen.Invoke(); filtered = _iisHelper.FilterBindings(allBindings, options); // Exclude specific bindings if (askExclude && filtered.Count > 1 && runLevel.HasFlag(RunLevel.Advanced)) { await ListBindings(input, filtered); input.CreateSpace(); 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."); await InputHosts("Exclude bindings", input, allBindings, filtered, options, () => options.ExcludeHosts, x => options.ExcludeHosts = x); if (options.ExcludeHosts != null) { filtered = _iisHelper.FilterBindings(allBindings, options); } } // Now the common name if (filtered.Select(x => x.HostUnicode).Distinct().Count() > 1) { await InputCommonName(input, filtered, options); } return(options); }
Task <ValidationPluginOptions?> IValidationPluginOptionsFactory.Aquire(Target target, IInputService inputService, RunLevel runLevel) => Task.FromResult <ValidationPluginOptions?>(default);
public override Task <DomainOptions> Aquire(IInputService inputService, RunLevel runLevel) => Default();
/// <summary> /// Make sure we have authorization for every host in target /// </summary> /// <param name="target"></param> /// <returns></returns> private async Task <Challenge> Authorize( ILifetimeScope execute, RunLevel runLevel, ValidationPluginOptions options, TargetPart targetPart, Authorization authorization) { var invalid = new Challenge { Status = AcmeClient.AuthorizationInvalid }; var valid = new Challenge { Status = AcmeClient.AuthorizationValid }; var client = execute.Resolve <AcmeClient>(); var identifier = authorization.Identifier.Value; try { _log.Information("Authorize identifier: {identifier}", identifier); if (authorization.Status == AcmeClient.AuthorizationValid && !runLevel.HasFlag(RunLevel.Test) && !runLevel.HasFlag(RunLevel.IgnoreCache)) { _log.Information("Cached authorization result: {Status}", authorization.Status); return(valid); } else { using var validation = _scopeBuilder.Validation(execute, options, targetPart, identifier); IValidationPlugin?validationPlugin = null; try { validationPlugin = validation.Resolve <IValidationPlugin>(); } catch (Exception ex) { _log.Error(ex, "Error resolving validation plugin"); } if (validationPlugin == null) { _log.Error("Validation plugin not found or not created."); return(invalid); } if (validationPlugin.Disabled) { _log.Error("Validation plugin is not available to the current user, try running as administrator."); return(invalid); } var challenge = authorization.Challenges.FirstOrDefault(c => c.Type == options.ChallengeType); if (challenge == null) { _log.Error("Expected challenge type {type} not available for {identifier}.", options.ChallengeType, authorization.Identifier.Value); return(invalid); } if (challenge.Status == AcmeClient.AuthorizationValid && !runLevel.HasFlag(RunLevel.Test) && !runLevel.HasFlag(RunLevel.IgnoreCache)) { _log.Information("{dnsIdentifier} already validated by {challengeType} validation ({name})", authorization.Identifier.Value, options.ChallengeType, options.Name); return(valid); } _log.Information("Authorizing {dnsIdentifier} using {challengeType} validation ({name})", identifier, options.ChallengeType, options.Name); try { var details = await client.DecodeChallengeValidation(authorization, challenge); await validationPlugin.PrepareChallenge(details); } catch (Exception ex) { _log.Error(ex, "Error preparing for challenge answer"); return(invalid); } _log.Debug("Submitting challenge answer"); challenge = await client.AnswerChallenge(challenge); if (challenge.Status != AcmeClient.AuthorizationValid) { if (challenge.Error != null) { _log.Error(challenge.Error.ToString()); } _log.Error("Authorization result: {Status}", challenge.Status); return(invalid); } else { _log.Information("Authorization result: {Status}", challenge.Status); return(valid); } } } catch (Exception ex) { _log.Error("Error authorizing {renewal}", targetPart); _exceptionHandler.HandleException(ex); return(invalid); } }
ValidationPluginOptions IValidationPluginOptionsFactory.Aquire(Target target, IArgumentsService arguments, IInputService inputService, RunLevel runLevel) { return(Aquire(target, arguments, inputService, runLevel)); }
Task <InstallationPluginOptions> IInstallationPluginOptionsFactory.Aquire(Target target, IInputService inputService, RunLevel runLevel) => Task.FromResult <InstallationPluginOptions>(new NullInstallationOptions());
/// <summary> /// Setup a new scheduled renewal /// </summary> /// <param name="runLevel"></param> internal async Task SetupRenewal(RunLevel runLevel) { if (_args.Test) { runLevel |= RunLevel.Test; } if (_args.Force) { runLevel |= RunLevel.IgnoreCache; } _log.Information(LogType.All, "Running in mode: {runLevel}", runLevel); var tempRenewal = Renewal.Create(_args.Id, _settings.ScheduledTask.RenewalDays, _passwordGenerator); using var configScope = _scopeBuilder.Configuration(_container, tempRenewal, runLevel); // Choose target plugin var targetPluginOptionsFactory = configScope.Resolve <ITargetPluginOptionsFactory>(); if (targetPluginOptionsFactory is INull) { _exceptionHandler.HandleException(message: $"No target plugin could be selected"); return; } if (targetPluginOptionsFactory.Disabled) { _exceptionHandler.HandleException(message: $"Target plugin {targetPluginOptionsFactory.Name} is not available to the current user, try running as administrator"); return; } var targetPluginOptions = runLevel.HasFlag(RunLevel.Unattended) ? await targetPluginOptionsFactory.Default() : await targetPluginOptionsFactory.Aquire(_input, runLevel); if (targetPluginOptions == null) { _exceptionHandler.HandleException(message: $"Target plugin {targetPluginOptionsFactory.Name} aborted or failed"); return; } tempRenewal.TargetPluginOptions = targetPluginOptions; // Generate Target and validation plugin choice Target initialTarget = null; IValidationPluginOptionsFactory validationPluginOptionsFactory = null; using (var targetScope = _scopeBuilder.Target(_container, tempRenewal, runLevel)) { initialTarget = targetScope.Resolve <Target>(); if (initialTarget == null) { _exceptionHandler.HandleException(message: $"Target plugin {targetPluginOptionsFactory.Name} was unable to generate a target"); return; } if (!initialTarget.IsValid(_log)) { _exceptionHandler.HandleException(message: $"Target plugin {targetPluginOptionsFactory.Name} generated an invalid target"); return; } _log.Information("Target generated using plugin {name}: {target}", targetPluginOptions.Name, initialTarget); // Choose FriendlyName if (runLevel.HasFlag(RunLevel.Advanced) && runLevel.HasFlag(RunLevel.Interactive) && string.IsNullOrEmpty(_args.FriendlyName)) { var alt = await _input.RequestString($"Suggested FriendlyName is '{initialTarget.FriendlyName}', press enter to accept or type an alternative"); if (!string.IsNullOrEmpty(alt)) { tempRenewal.FriendlyName = alt; } } tempRenewal.LastFriendlyName = initialTarget.FriendlyName; // Choose validation plugin validationPluginOptionsFactory = targetScope.Resolve <IValidationPluginOptionsFactory>(); if (validationPluginOptionsFactory is INull) { _exceptionHandler.HandleException(message: $"No validation plugin could be selected"); return; } } // Configure validation try { var validationOptions = runLevel.HasFlag(RunLevel.Unattended) ? await validationPluginOptionsFactory.Default(initialTarget) : await validationPluginOptionsFactory.Aquire(initialTarget, _input, runLevel); if (validationOptions == null) { _exceptionHandler.HandleException(message: $"Validation plugin {validationPluginOptionsFactory.Name} was unable to generate options"); return; } tempRenewal.ValidationPluginOptions = validationOptions; } catch (Exception ex) { _exceptionHandler.HandleException(ex, $"Validation plugin {validationPluginOptionsFactory.Name} aborted or failed"); return; } // Choose CSR plugin if (initialTarget.CsrBytes == null) { var csrPluginOptionsFactory = configScope.Resolve <ICsrPluginOptionsFactory>(); if (csrPluginOptionsFactory is INull) { _exceptionHandler.HandleException(message: $"No CSR plugin could be selected"); return; } // Configure CSR try { var csrOptions = runLevel.HasFlag(RunLevel.Unattended) ? await csrPluginOptionsFactory.Default() : await csrPluginOptionsFactory.Aquire(_input, runLevel); if (csrOptions == null) { _exceptionHandler.HandleException(message: $"CSR plugin {csrPluginOptionsFactory.Name} was unable to generate options"); return; } tempRenewal.CsrPluginOptions = csrOptions; } catch (Exception ex) { _exceptionHandler.HandleException(ex, $"CSR plugin {csrPluginOptionsFactory.Name} aborted or failed"); return; } } // Choose and configure store plugins var resolver = configScope.Resolve <IResolver>(); var storePluginOptionsFactories = new List <IStorePluginOptionsFactory>(); try { while (true) { var storePluginOptionsFactory = await resolver.GetStorePlugin(configScope, storePluginOptionsFactories); if (storePluginOptionsFactory == null) { _exceptionHandler.HandleException(message: $"Store could not be selected"); } if (storePluginOptionsFactory is NullStoreOptionsFactory) { if (storePluginOptionsFactories.Count == 0) { throw new Exception(); } break; } StorePluginOptions storeOptions; try { storeOptions = runLevel.HasFlag(RunLevel.Unattended) ? await storePluginOptionsFactory.Default() : await storePluginOptionsFactory.Aquire(_input, runLevel); } catch (Exception ex) { _exceptionHandler.HandleException(ex, $"Store plugin {storePluginOptionsFactory.Name} aborted or failed"); return; } if (storeOptions == null) { _exceptionHandler.HandleException(message: $"Store plugin {storePluginOptionsFactory.Name} was unable to generate options"); return; } tempRenewal.StorePluginOptions.Add(storeOptions); storePluginOptionsFactories.Add(storePluginOptionsFactory); } } catch (Exception ex) { _exceptionHandler.HandleException(ex, "Invalid selection of store plugins"); return; } // Choose and configure installation plugins var installationPluginFactories = new List <IInstallationPluginOptionsFactory>(); try { while (true) { var installationPluginFactory = await resolver.GetInstallationPlugin(configScope, tempRenewal.StorePluginOptions.Select(x => x.Instance), installationPluginFactories); if (installationPluginFactory == null) { _exceptionHandler.HandleException(message: $"Installation plugin could not be selected"); } InstallationPluginOptions installOptions; try { installOptions = runLevel.HasFlag(RunLevel.Unattended) ? await installationPluginFactory.Default(initialTarget) : await installationPluginFactory.Aquire(initialTarget, _input, runLevel); } catch (Exception ex) { _exceptionHandler.HandleException(ex, $"Installation plugin {installationPluginFactory.Name} aborted or failed"); return; } if (installOptions == null) { _exceptionHandler.HandleException(message: $"Installation plugin {installationPluginFactory.Name} was unable to generate options"); return; } if (installationPluginFactory is NullInstallationOptionsFactory) { if (installationPluginFactories.Count == 0) { tempRenewal.InstallationPluginOptions.Add(installOptions); installationPluginFactories.Add(installationPluginFactory); } break; } tempRenewal.InstallationPluginOptions.Add(installOptions); installationPluginFactories.Add(installationPluginFactory); } } catch (Exception ex) { _exceptionHandler.HandleException(ex, "Invalid selection of installation plugins"); return; } // Try to run for the first time var renewal = await CreateRenewal(tempRenewal, runLevel); var result = await _renewalExecution.Renew(renewal, runLevel); if (!result.Success) { _exceptionHandler.HandleException(message: $"Create certificate failed: {result.ErrorMessage}"); } else { _renewalStore.Save(renewal, result); } }
/// <summary> /// Get webroot path manually /// </summary> public HttpValidationOptions <TPlugin> BaseAquire(Target target, IArgumentsService options, IInputService input, RunLevel runLevel) { var allowEmtpy = AllowEmtpy(target); string path = options.TryGetArgument(null, input, WebrootHint(allowEmtpy)); while ( (!string.IsNullOrEmpty(path) && !PathIsValid(path)) || (string.IsNullOrEmpty(path) && !allowEmtpy)) { path = options.TryGetArgument(null, input, WebrootHint(allowEmtpy)); } return(new TOptions { Path = path, CopyWebConfig = target.IIS || input.PromptYesNo("Copy default web.config before validation?", false) }); }
public override void Aquire(ScheduledRenewal renewal, IOptionsService optionsService, IInputService inputService, RunLevel runLevel, int?siteID = null) { inputService.Show("Full instructions", "https://github.com/PKISharp/win-acme/wiki/Install-Script"); do { renewal.Script = optionsService.TryGetOption(optionsService.Options.Script, inputService, "Enter the path to the script that you want to run after renewal"); }while (!renewal.Script.ValidFile(_log)); inputService.Show("{0}", "Hostname"); inputService.Show("{1}", ".pfx password"); inputService.Show("{2}", ".pfx path"); inputService.Show("{3}", "Certificate store name"); inputService.Show("{4}", "Certificate friendly name"); inputService.Show("{5}", "Certificate thumbprint"); inputService.Show("{6}", "Central SSL store path"); renewal.ScriptParameters = optionsService.TryGetOption(optionsService.Options.ScriptParameters, inputService, "Enter the parameter format string for the script, e.g. \"--hostname {0}\""); }
private void RunLevelChanged(RunLevel oldLevel, RunLevel newLevel) { RunLevel = newLevel; }
/// <summary> /// Request certificate from the ACME server /// </summary> /// <param name="csrPlugin">Plugin used to generate CSR if it has not been provided in the target</param> /// <param name="runLevel"></param> /// <param name="renewal"></param> /// <param name="target"></param> /// <param name="order"></param> /// <returns></returns> public async Task <CertificateInfo> RequestCertificate(ICsrPlugin?csrPlugin, RunLevel runLevel, Order order) { if (order.Details == null) { throw new InvalidOperationException("No order details found"); } // What are we going to get? var cacheKey = CacheKey(order); var pfxFileInfo = new FileInfo(GetPath(order.Renewal, $"-{cacheKey}{PfxPostFix}")); // Determine/check the common name var identifiers = order.Target.GetHosts(false); var commonNameUni = order.Target.CommonName; var commonNameAscii = string.Empty; if (!string.IsNullOrWhiteSpace(commonNameUni)) { var idn = new IdnMapping(); commonNameAscii = idn.GetAscii(commonNameUni); if (!identifiers.Contains(commonNameAscii, StringComparer.InvariantCultureIgnoreCase)) { _log.Warning($"Common name {commonNameUni} provided is invalid."); commonNameAscii = identifiers.First(); commonNameUni = idn.GetUnicode(commonNameAscii); } } // Determine the friendly name base (for the renewal) var friendlyNameBase = order.Renewal.FriendlyName; if (string.IsNullOrEmpty(friendlyNameBase)) { friendlyNameBase = order.Target.FriendlyName; } if (string.IsNullOrEmpty(friendlyNameBase)) { friendlyNameBase = commonNameUni; } // Determine the friendly name for this specific certificate var friendlyNameIntermediate = friendlyNameBase; if (!string.IsNullOrEmpty(order.FriendlyNamePart)) { friendlyNameIntermediate += $" [{order.FriendlyNamePart}]"; } var friendlyName = $"{friendlyNameIntermediate} @ {_inputService.FormatDate(DateTime.Now)}"; // Try using cached certificate first to avoid rate limiting during // (initial?) deployment troubleshooting. Real certificate requests // will only be done once per day maximum unless the --force parameter // is used. var cache = CachedInfo(order); if (cache != null && cache.CacheFile != null) { if (cache.CacheFile.LastWriteTime > DateTime.Now.AddDays(_settings.Cache.ReuseDays * -1)) { if (runLevel.HasFlag(RunLevel.IgnoreCache)) { _log.Warning("Cached certificate available on disk but not used due to --{switch} switch.", nameof(MainArguments.Force).ToLower()); } else { _log.Warning("Using cached certificate for {friendlyName}. To force a new request of the " + "certificate within {days} days, run with the --{switch} switch.", friendlyNameIntermediate, _settings.Cache.ReuseDays, nameof(MainArguments.Force).ToLower()); return(cache); } } } if (order.Details.Payload.Status != AcmeClient.OrderValid) { // Clear cache and write new cert ClearCache(order.Renewal, postfix: CsrPostFix); if (order.Target.CsrBytes == null) { if (csrPlugin == null) { throw new InvalidOperationException("Missing csrPlugin"); } // Backwards compatible with existing keys, which are not split per order yet. var keyFile = new FileInfo(GetPath(order.Renewal, $".keys")); if (!keyFile.Exists) { keyFile = new FileInfo(GetPath(order.Renewal, $"-{cacheKey}.keys")); } var csr = await csrPlugin.GenerateCsr(keyFile.FullName, commonNameAscii, identifiers); var keySet = await csrPlugin.GetKeys(); order.Target.CsrBytes = csr.GetDerEncoded(); order.Target.PrivateKey = keySet.Private; var csrPath = GetPath(order.Renewal, $"-{cacheKey}{CsrPostFix}"); await File.WriteAllTextAsync(csrPath, _pemService.GetPem("CERTIFICATE REQUEST", order.Target.CsrBytes)); _log.Debug("CSR stored at {path} in certificate cache folder {folder}", Path.GetFileName(csrPath), Path.GetDirectoryName(csrPath)); } _log.Verbose("Submitting CSR"); order.Details = await _client.SubmitCsr(order.Details, order.Target.CsrBytes); if (order.Details.Payload.Status != AcmeClient.OrderValid) { _log.Error("Unexpected order status {status}", order.Details.Payload.Status); throw new Exception($"Unable to complete order"); } } _log.Information("Requesting certificate {friendlyName}", friendlyNameIntermediate); var certInfo = await _client.GetCertificate(order.Details); if (certInfo == null || certInfo.Certificate == null) { throw new Exception($"Unable to get certificate"); } var alternatives = new List <X509Certificate2Collection> { ParseCertificate(certInfo.Certificate, friendlyName, order.Target.PrivateKey) }; foreach (var alt in certInfo.Links["alternate"]) { try { var altCertRaw = await _client.GetCertificate(alt); var altCert = ParseCertificate(altCertRaw, friendlyName, order.Target.PrivateKey); alternatives.Add(altCert); } catch (Exception ex) { _log.Warning("Unable to get alternate certificate: {ex}", ex.Message); } } var selected = Select(alternatives); ClearCache(order.Renewal, postfix: $"*{PfxPostFix}"); ClearCache(order.Renewal, postfix: $"*{PfxPostFixLegacy}"); await File.WriteAllBytesAsync(pfxFileInfo.FullName, selected.Export(X509ContentType.Pfx, order.Renewal.PfxPassword?.Value)); _log.Debug("Certificate written to cache file {path} in certificate cache folder {folder}. It will be " + "reused when renewing within {x} day(s) as long as the Target and Csr parameters remain the same and " + "the --force switch is not used.", pfxFileInfo.Name, pfxFileInfo.Directory.FullName, _settings.Cache.ReuseDays); if (csrPlugin != null) { try { var cert = selected. OfType <X509Certificate2>(). Where(x => x.HasPrivateKey). FirstOrDefault(); if (cert != null) { var certIndex = selected.IndexOf(cert); var newVersion = await csrPlugin.PostProcess(cert); if (newVersion != cert) { newVersion.FriendlyName = friendlyName; selected[certIndex] = newVersion; await File.WriteAllBytesAsync(pfxFileInfo.FullName, selected.Export(X509ContentType.Pfx, order.Renewal.PfxPassword?.Value)); newVersion.Dispose(); } } } catch (Exception ex) { _log.Warning("Private key conversion error: {ex}", ex.Message); } } pfxFileInfo.Refresh(); // Update LastFriendlyName so that the user sees // the most recently issued friendlyName in // the WACS GUI order.Renewal.LastFriendlyName = friendlyNameBase; // Recreate X509Certificate2 with correct flags for Store/Install return(FromCache(pfxFileInfo, order.Renewal.PfxPassword?.Value)); }
/// <summary> /// Setup a new scheduled renewal /// </summary> /// <param name="runLevel"></param> internal async Task SetupRenewal(RunLevel runLevel, Renewal?tempRenewal = null) { if (_args.Test) { runLevel |= RunLevel.Test; } if (_args.Force) { runLevel |= RunLevel.IgnoreCache; } _log.Information(LogType.All, "Running in mode: {runLevel}", runLevel); if (tempRenewal == null) { tempRenewal = Renewal.Create(_args.Id, _settings.ScheduledTask.RenewalDays, _passwordGenerator); } else { tempRenewal.InstallationPluginOptions.Clear(); tempRenewal.StorePluginOptions.Clear(); } using var configScope = _scopeBuilder.Configuration(_container, tempRenewal, runLevel); // Choose target plugin var targetPluginOptionsFactory = configScope.Resolve <ITargetPluginOptionsFactory>(); if (targetPluginOptionsFactory is INull) { _exceptionHandler.HandleException(message: $"No source plugin could be selected"); return; } var(targetPluginDisabled, targetPluginDisabledReason) = targetPluginOptionsFactory.Disabled; if (targetPluginDisabled) { _exceptionHandler.HandleException(message: $"Source plugin {targetPluginOptionsFactory.Name} is not available. {targetPluginDisabledReason}"); return; } var targetPluginOptions = runLevel.HasFlag(RunLevel.Unattended) ? await targetPluginOptionsFactory.Default() : await targetPluginOptionsFactory.Acquire(_input, runLevel); if (targetPluginOptions == null) { _exceptionHandler.HandleException(message: $"Source plugin {targetPluginOptionsFactory.Name} aborted or failed"); return; } tempRenewal.TargetPluginOptions = targetPluginOptions; // Generate Target and validation plugin choice using var targetScope = _scopeBuilder.Target(_container, tempRenewal, runLevel); var initialTarget = targetScope.Resolve <Target>(); if (initialTarget is INull) { _exceptionHandler.HandleException(message: $"Source plugin {targetPluginOptionsFactory.Name} was unable to generate a target"); return; } if (!initialTarget.IsValid(_log)) { _exceptionHandler.HandleException(message: $"Source plugin {targetPluginOptionsFactory.Name} generated an invalid target"); return; } _log.Information("Source generated using plugin {name}: {target}", targetPluginOptions.Name, initialTarget); // Choose FriendlyName if (!string.IsNullOrEmpty(_args.FriendlyName)) { tempRenewal.FriendlyName = _args.FriendlyName; } else if (runLevel.HasFlag(RunLevel.Advanced | RunLevel.Interactive)) { var alt = await _input.RequestString($"Suggested friendly name '{initialTarget.FriendlyName}', press <Enter> to accept or type an alternative"); if (!string.IsNullOrEmpty(alt)) { tempRenewal.FriendlyName = alt; } } tempRenewal.LastFriendlyName = tempRenewal.FriendlyName ?? initialTarget.FriendlyName; // Choose validation plugin var validationPluginOptionsFactory = targetScope.Resolve <IValidationPluginOptionsFactory>(); if (validationPluginOptionsFactory is INull) { _exceptionHandler.HandleException(message: $"No validation plugin could be selected"); return; } // Configure validation try { var validationOptions = runLevel.HasFlag(RunLevel.Unattended) ? await validationPluginOptionsFactory.Default(initialTarget) : await validationPluginOptionsFactory.Acquire(initialTarget, _input, runLevel); if (validationOptions == null) { _exceptionHandler.HandleException(message: $"Validation plugin {validationPluginOptionsFactory.Name} was unable to generate options"); return; } tempRenewal.ValidationPluginOptions = validationOptions; } catch (Exception ex) { _exceptionHandler.HandleException(ex, $"Validation plugin {validationPluginOptionsFactory.Name} aborted or failed"); return; } // Choose order plugin var orderPluginOptionsFactory = targetScope.Resolve <IOrderPluginOptionsFactory>(); if (orderPluginOptionsFactory is INull) { _exceptionHandler.HandleException(message: $"No order plugin could be selected"); return; } // Configure order try { var orderOptions = runLevel.HasFlag(RunLevel.Unattended) ? await orderPluginOptionsFactory.Default() : await orderPluginOptionsFactory.Acquire(_input, runLevel); if (orderOptions == null) { _exceptionHandler.HandleException(message: $"Order plugin {orderPluginOptionsFactory.Name} was unable to generate options"); return; } tempRenewal.OrderPluginOptions = orderOptions; } catch (Exception ex) { _exceptionHandler.HandleException(ex, $"Order plugin {orderPluginOptionsFactory.Name} aborted or failed"); return; } // Choose CSR plugin if (initialTarget.CsrBytes == null) { var csrPluginOptionsFactory = configScope.Resolve <ICsrPluginOptionsFactory>(); if (csrPluginOptionsFactory is INull) { _exceptionHandler.HandleException(message: $"No CSR plugin could be selected"); return; } // Configure CSR try { var csrOptions = runLevel.HasFlag(RunLevel.Unattended) ? await csrPluginOptionsFactory.Default() : await csrPluginOptionsFactory.Acquire(_input, runLevel); if (csrOptions == null) { _exceptionHandler.HandleException(message: $"CSR plugin {csrPluginOptionsFactory.Name} was unable to generate options"); return; } tempRenewal.CsrPluginOptions = csrOptions; } catch (Exception ex) { _exceptionHandler.HandleException(ex, $"CSR plugin {csrPluginOptionsFactory.Name} aborted or failed"); return; } } // Choose and configure store plugins var resolver = configScope.Resolve <IResolver>(); var storePluginOptionsFactories = new List <IStorePluginOptionsFactory>(); try { while (true) { var storePluginOptionsFactory = await resolver.GetStorePlugin(configScope, storePluginOptionsFactories); if (storePluginOptionsFactory == null) { _exceptionHandler.HandleException(message: $"Store could not be selected"); return; } StorePluginOptions?storeOptions; try { storeOptions = runLevel.HasFlag(RunLevel.Unattended) ? await storePluginOptionsFactory.Default() : await storePluginOptionsFactory.Acquire(_input, runLevel); } catch (Exception ex) { _exceptionHandler.HandleException(ex, $"Store plugin {storePluginOptionsFactory.Name} aborted or failed"); return; } if (storeOptions == null) { _exceptionHandler.HandleException(message: $"Store plugin {storePluginOptionsFactory.Name} was unable to generate options"); return; } var isNull = storePluginOptionsFactory is NullStoreOptionsFactory; if (!isNull || storePluginOptionsFactories.Count == 0) { tempRenewal.StorePluginOptions.Add(storeOptions); storePluginOptionsFactories.Add(storePluginOptionsFactory); } if (isNull) { break; } } } catch (Exception ex) { _exceptionHandler.HandleException(ex, "Invalid selection of store plugins"); return; } // Choose and configure installation plugins var installationPluginFactories = new List <IInstallationPluginOptionsFactory>(); try { while (true) { var installationPluginOptionsFactory = await resolver.GetInstallationPlugin(configScope, tempRenewal.StorePluginOptions.Select(x => x.Instance), installationPluginFactories); if (installationPluginOptionsFactory == null) { _exceptionHandler.HandleException(message: $"Installation plugin could not be selected"); return; } InstallationPluginOptions installOptions; try { installOptions = runLevel.HasFlag(RunLevel.Unattended) ? await installationPluginOptionsFactory.Default(initialTarget) : await installationPluginOptionsFactory.Acquire(initialTarget, _input, runLevel); } catch (Exception ex) { _exceptionHandler.HandleException(ex, $"Installation plugin {installationPluginOptionsFactory.Name} aborted or failed"); return; } if (installOptions == null) { _exceptionHandler.HandleException(message: $"Installation plugin {installationPluginOptionsFactory.Name} was unable to generate options"); return; } var isNull = installationPluginOptionsFactory is NullInstallationOptionsFactory; if (!isNull || installationPluginFactories.Count == 0) { tempRenewal.InstallationPluginOptions.Add(installOptions); installationPluginFactories.Add(installationPluginOptionsFactory); } if (isNull) { break; } } } catch (Exception ex) { _exceptionHandler.HandleException(ex, "Invalid selection of installation plugins"); return; } // Try to run for the first time var renewal = await CreateRenewal(tempRenewal, runLevel); retry: var result = await _renewalExecution.HandleRenewal(renewal, runLevel); if (result.Abort) { _exceptionHandler.HandleException(message: $"Create certificate cancelled"); } else if (!result.Success) { if (runLevel.HasFlag(RunLevel.Interactive) && await _input.PromptYesNo("Create certificate failed, retry?", false)) { goto retry; } if (!renewal.New && await _input.PromptYesNo("Save these new settings anyway?", false)) { _renewalStore.Save(renewal, result); } _exceptionHandler.HandleException(message: $"Create certificate failed: {string.Join("\n\t- ", result.ErrorMessages)}"); } else { try { _renewalStore.Save(renewal, result); await _notification.NotifyCreated(renewal, _log.Lines); } catch (Exception ex) { _exceptionHandler.HandleException(ex); } } }
/// <summary> /// Setup a new scheduled renewal /// </summary> /// <param name="runLevel"></param> private void CreateNewCertificate(RunLevel runLevel) { if (_args.Test) { runLevel |= RunLevel.Test; } _log.Information(true, "Running in mode: {runLevel}", runLevel); using (var configScope = _scopeBuilder.Configuration(_container, runLevel)) { // Choose target plugin var tempRenewal = Renewal.Create(_passwordGenerator); var targetPluginOptionsFactory = configScope.Resolve <ITargetPluginOptionsFactory>(); if (targetPluginOptionsFactory is INull) { HandleException(message: $"No target plugin could be selected"); return; } var targetPluginOptions = runLevel.HasFlag(RunLevel.Unattended) ? targetPluginOptionsFactory.Default(_arguments) : targetPluginOptionsFactory.Aquire(_arguments, _input, runLevel); if (targetPluginOptions == null) { HandleException(message: $"Target plugin {targetPluginOptionsFactory.Name} aborted or failed"); return; } tempRenewal.TargetPluginOptions = targetPluginOptions; // Generate Target and validation plugin choice Target initialTarget = null; IValidationPluginOptionsFactory validationPluginOptionsFactory = null; using (var targetScope = _scopeBuilder.Target(_container, tempRenewal, runLevel)) { initialTarget = targetScope.Resolve <Target>(); if (initialTarget == null) { HandleException(message: $"Target plugin {targetPluginOptionsFactory.Name} was unable to generate a target"); return; } if (!initialTarget.IsValid(_log)) { HandleException(message: $"Target plugin {targetPluginOptionsFactory.Name} generated an invalid target"); return; } _log.Information("Target generated using plugin {name}: {target}", targetPluginOptions.Name, initialTarget); // Choose FriendlyName if (runLevel.HasFlag(RunLevel.Advanced) && runLevel.HasFlag(RunLevel.Interactive) && string.IsNullOrEmpty(_args.FriendlyName)) { var alt = _input.RequestString($"Suggested FriendlyName is '{initialTarget.FriendlyName}', press enter to accept or type an alternative"); if (!string.IsNullOrEmpty(alt)) { tempRenewal.FriendlyName = alt; } } tempRenewal.LastFriendlyName = initialTarget.FriendlyName; // Choose validation plugin validationPluginOptionsFactory = targetScope.Resolve <IValidationPluginOptionsFactory>(); if (validationPluginOptionsFactory is INull) { HandleException(message: $"No validation plugin could be selected"); return; } } // Configure validation try { ValidationPluginOptions validationOptions = null; if (runLevel.HasFlag(RunLevel.Unattended)) { validationOptions = validationPluginOptionsFactory.Default(initialTarget, _arguments); } else { validationOptions = validationPluginOptionsFactory.Aquire(initialTarget, _arguments, _input, runLevel); } if (validationOptions == null) { HandleException(message: $"Validation plugin {validationPluginOptionsFactory.Name} was unable to generate options"); return; } tempRenewal.ValidationPluginOptions = validationOptions; } catch (Exception ex) { HandleException(ex, $"Validation plugin {validationPluginOptionsFactory.Name} aborted or failed"); return; } // Choose CSR plugin var csrPluginOptionsFactory = configScope.Resolve <ICsrPluginOptionsFactory>(); if (csrPluginOptionsFactory is INull) { HandleException(message: $"No CSR plugin could be selected"); return; } // Configure CSR try { CsrPluginOptions csrOptions = null; if (runLevel.HasFlag(RunLevel.Unattended)) { csrOptions = csrPluginOptionsFactory.Default(_arguments); } else { csrOptions = csrPluginOptionsFactory.Aquire(_arguments, _input, runLevel); } if (csrOptions == null) { HandleException(message: $"CSR plugin {csrPluginOptionsFactory.Name} was unable to generate options"); return; } tempRenewal.CsrPluginOptions = csrOptions; } catch (Exception ex) { HandleException(ex, $"CSR plugin {csrPluginOptionsFactory.Name} aborted or failed"); return; } // Choose storage plugin var storePluginOptionsFactory = configScope.Resolve <IStorePluginOptionsFactory>(); if (storePluginOptionsFactory is INull) { HandleException(message: $"No store plugin could be selected"); return; } // Configure storage try { StorePluginOptions storeOptions = null; if (runLevel.HasFlag(RunLevel.Unattended)) { storeOptions = storePluginOptionsFactory.Default(_arguments); } else { storeOptions = storePluginOptionsFactory.Aquire(_arguments, _input, runLevel); } if (storeOptions == null) { HandleException(message: $"Store plugin {storePluginOptionsFactory.Name} was unable to generate options"); return; } tempRenewal.StorePluginOptions = storeOptions; } catch (Exception ex) { HandleException(ex, $"Store plugin {storePluginOptionsFactory.Name} aborted or failed"); return; } // Choose and configure installation plugins try { var installationPluginOptionsFactories = configScope.Resolve <List <IInstallationPluginOptionsFactory> >(); if (installationPluginOptionsFactories.Count() == 0) { // User cancelled, otherwise we would at least have the Null-installer HandleException(message: $"User aborted"); return; } foreach (var installationPluginOptionsFactory in installationPluginOptionsFactories) { InstallationPluginOptions installOptions; try { if (runLevel.HasFlag(RunLevel.Unattended)) { installOptions = installationPluginOptionsFactory.Default(initialTarget, _arguments); } else { installOptions = installationPluginOptionsFactory.Aquire(initialTarget, _arguments, _input, runLevel); } } catch (Exception ex) { HandleException(ex, $"Install plugin {installationPluginOptionsFactory.Name} aborted or failed"); return; } if (installOptions == null) { HandleException(message: $"Installation plugin {installationPluginOptionsFactory.Name} was unable to generate options"); return; } tempRenewal.InstallationPluginOptions.Add(installOptions); } } catch (Exception ex) { HandleException(ex, "Invalid selection of installation plugins"); return; } // Try to run for the first time var renewal = CreateRenewal(tempRenewal, runLevel); var result = Renew(renewal, runLevel); if (!result.Success) { HandleException(message: $"Create certificate failed: {result.ErrorMessage}"); } else { _renewalService.Save(renewal, result); } } }
public override CsrOptions Aquire(IArgumentsService arguments, IInputService inputService, RunLevel runLevel) { var args = arguments.GetArguments <CsrArguments>(); var ret = new CsrOptions(); do { ret.CsrFile = arguments.TryGetArgument( args.CsrFile, inputService, "Enter the path to the CSR"); }while (!ret.CsrFile.ValidFile(_log)); string pkFile; do { pkFile = arguments.TryGetArgument(args.CsrFile, inputService, "Enter the path to the corresponding private key, or <ENTER> to create a certificate without one"); }while (!(string.IsNullOrWhiteSpace(pkFile) || pkFile.ValidFile(_log))); if (!string.IsNullOrWhiteSpace(pkFile)) { ret.PkFile = pkFile; } return(ret); }
public Sftp(SftpOptions options, HttpValidationParameters pars, RunLevel runLevel) : base(options, runLevel, pars) { _sshFtpClient = new SshFtpClient(_options.Credential.GetCredential(), pars.LogService); }
private static void CreateNewCertificate(RunLevel runLevel) { _log.Information(true, "Running in {runLevel} mode", runLevel); var tempRenewal = CreateRenewal(_options); using (var scope = AutofacBuilder.Renewal(_container, tempRenewal, runLevel)) { // Choose target plugin var targetPluginFactory = scope.Resolve <ITargetPluginFactory>(); if (targetPluginFactory is INull) { return; // User cancelled or unable to resolve } // Aquire target var targetPlugin = scope.Resolve <ITargetPlugin>(); var target = runLevel == RunLevel.Unattended ? targetPlugin.Default(_optionsService) : targetPlugin.Aquire(_optionsService, _input, runLevel); var originalTarget = tempRenewal.Binding; tempRenewal.Binding = target; if (target == null) { _log.Error("Plugin {name} was unable to generate a target", targetPluginFactory.Name); return; } tempRenewal.Binding.TargetPluginName = targetPluginFactory.Name; tempRenewal.Binding.SSLPort = _options.SSLPort; tempRenewal.Binding.ValidationPort = _options.ValidationPort; tempRenewal.Binding.ValidationPluginName = originalTarget.ValidationPluginName; _log.Information("Plugin {name} generated target {target}", targetPluginFactory.Name, tempRenewal.Binding); // Choose validation plugin var validationPluginFactory = scope.Resolve <IValidationPluginFactory>(); if (validationPluginFactory is INull) { return; // User cancelled } else if (!validationPluginFactory.CanValidate(target)) { // Might happen in unattended mode _log.Error("Validation plugin {name} is unable to validate target", validationPluginFactory.Name); return; } // Configure validation try { if (runLevel == RunLevel.Unattended) { validationPluginFactory.Default(target, _optionsService); } else { validationPluginFactory.Aquire(target, _optionsService, _input, runLevel); } tempRenewal.Binding.ValidationPluginName = $"{validationPluginFactory.ChallengeType}.{validationPluginFactory.Name}"; } catch (Exception ex) { _log.Error(ex, "Invalid validation input"); return; } // Choose and configure installation plugins try { var installFactories = scope.Resolve <List <IInstallationPluginFactory> >(); if (installFactories.Count == 0) { // User cancelled, otherwise we would at least have the Null-installer return; } foreach (var installFactory in installFactories) { if (runLevel == RunLevel.Unattended) { installFactory.Default(tempRenewal, _optionsService); } else { installFactory.Aquire(tempRenewal, _optionsService, _input, runLevel); } } tempRenewal.InstallationPluginNames = installFactories.Select(f => f.Name).ToList(); } catch (Exception ex) { _log.Error(ex, "Invalid installation input"); return; } var result = Renew(scope, CreateRenewal(tempRenewal)); if (!result.Success) { _log.Error("Create certificate failed"); } } }
public override Task <HostOptions> Acquire(IInputService inputService, RunLevel runLevel) => Default();
/// <summary> /// Make sure we have authorization for every host in target /// </summary> /// <param name="target"></param> /// <returns></returns> private async Task <Challenge> Authorize( ILifetimeScope execute, RunLevel runLevel, ValidationPluginOptions options, TargetPart targetPart, Authorization authorization) { var invalid = new Challenge { Status = AcmeClient.AuthorizationInvalid }; var valid = new Challenge { Status = AcmeClient.AuthorizationValid }; var client = execute.Resolve <AcmeClient>(); var identifier = authorization.Identifier.Value; IValidationPlugin?validationPlugin = null; using var validation = _scopeBuilder.Validation(execute, options, targetPart, identifier); try { if (authorization.Status == AcmeClient.AuthorizationValid) { if (!runLevel.HasFlag(RunLevel.Test) && !runLevel.HasFlag(RunLevel.IgnoreCache)) { _log.Information("Cached authorization result for {identifier}: {Status}", identifier, authorization.Status); return(valid); } if (runLevel.HasFlag(RunLevel.IgnoreCache)) { // Due to the IgnoreCache flag (--force switch) // we are going to attempt to re-authorize the // domain even though its already autorized. // On failure, we can still use the cached result. // This helps for migration scenarios. invalid = valid; } } _log.Information("Authorize identifier {identifier}", identifier); _log.Verbose("Initial authorization status: {status}", authorization.Status); _log.Verbose("Challenge types available: {challenges}", authorization.Challenges.Select(x => x.Type ?? "[Unknown]")); var challenge = authorization.Challenges.FirstOrDefault(c => string.Equals(c.Type, options.ChallengeType, StringComparison.CurrentCultureIgnoreCase)); if (challenge == null) { if (authorization.Status == AcmeClient.AuthorizationValid) { var usedType = authorization.Challenges. Where(x => x.Status == AcmeClient.AuthorizationValid). FirstOrDefault(); _log.Warning("Expected challenge type {type} not available for {identifier}, already validated using {valided}.", options.ChallengeType, authorization.Identifier.Value, usedType?.Type ?? "[unknown]"); return(valid); } else { _log.Error("Expected challenge type {type} not available for {identifier}.", options.ChallengeType, authorization.Identifier.Value); invalid.Error = "Expected challenge type not available"; return(invalid); } } else { _log.Verbose("Initial challenge status: {status}", challenge.Status); if (challenge.Status == AcmeClient.AuthorizationValid) { // We actually should not get here because if one of the // challenges is valid, the authorization itself should also // be valid. if (!runLevel.HasFlag(RunLevel.Test) && !runLevel.HasFlag(RunLevel.IgnoreCache)) { _log.Information("Cached authorization result: {Status}", authorization.Status); return(valid); } if (runLevel.HasFlag(RunLevel.IgnoreCache)) { // Due to the IgnoreCache flag (--force switch) // we are going to attempt to re-authorize the // domain even though its already autorized. // On failure, we can still use the cached result. // This helps for migration scenarios. invalid = valid; } } } // We actually have to do validation now try { validationPlugin = validation.Resolve <IValidationPlugin>(); } catch (Exception ex) { _log.Error(ex, "Error resolving validation plugin"); } if (validationPlugin == null) { _log.Error("Validation plugin not found or not created."); invalid.Error = "Validation plugin not found or not created."; return(invalid); } var(disabled, disabledReason) = validationPlugin.Disabled; if (disabled) { _log.Error($"Validation plugin is not available. {disabledReason}"); invalid.Error = "Validation plugin is not available."; return(invalid); } _log.Information("Authorizing {dnsIdentifier} using {challengeType} validation ({name})", identifier, options.ChallengeType, options.Name); try { var details = await client.DecodeChallengeValidation(authorization, challenge); await validationPlugin.PrepareChallenge(details); } catch (Exception ex) { _log.Error(ex, "Error preparing for challenge answer"); invalid.Error = "Error preparing for challenge answer"; return(invalid); } _log.Debug("Submitting challenge answer"); challenge = await client.AnswerChallenge(challenge); if (challenge.Status != AcmeClient.AuthorizationValid) { if (challenge.Error != null) { _log.Error(challenge.Error.ToString()); } _log.Error("Authorization result: {Status}", challenge.Status); invalid.Error = challenge.Error; return(invalid); } else { _log.Information("Authorization result: {Status}", challenge.Status); return(valid); } } catch (Exception ex) { _log.Error("Error authorizing {renewal}", targetPart); _exceptionHandler.HandleException(ex); invalid.Error = ex.Message; return(invalid); } finally { if (validationPlugin != null) { try { _log.Verbose("Starting post-validation cleanup"); await validationPlugin.CleanUp(); _log.Verbose("Post-validation cleanup was succesful"); } catch (Exception ex) { _log.Warning("An error occured during post-validation cleanup: {ex}", ex.Message); } } } }
Target ITargetPlugin.Aquire(IOptionsService optionsService, IInputService inputService, RunLevel runLevel) { var input = inputService.RequestString("Enter comma-separated list of host names, starting with the primary one"); var target = Create(input); if (runLevel >= RunLevel.Advanced) { target.AskForCommonNameChoice(inputService); } return(target); }
/// <summary> /// Controls what modules are running. /// </summary> /// <param name="runlevel"></param> public void InitModules(RunLevel runlevel = RunLevel.Lobby) { if (runlevel == Runlevel) return; Runlevel = runlevel; if (Runlevel == RunLevel.Lobby) { _startAt = DateTime.Now.AddSeconds(GameCountdown); } else if (Runlevel == RunLevel.Game) { IoCManager.Resolve<IMapManager>().LoadMap(_serverMapName); //IoCManager.Resolve<IAtmosManager>().InitializeGasCells(); EntityManager = new EntityManager(IoCManager.Resolve<ISS14NetServer>()); IoCManager.Resolve<IRoundManager>().CurrentGameMode.StartGame(); } }
public async System.Threading.Tasks.Task EnsureTaskScheduler(RunLevel runLevel, bool offerRecreate) { string taskName; var existingTask = ExistingTask; taskName = existingTask != null ? existingTask.Name : TaskName(_settings.Client.ClientName); using var taskService = new TaskService(); if (existingTask != null) { var healthy = IsHealthy(existingTask); var recreate = false; if (runLevel.HasFlag(RunLevel.Interactive)) { if (offerRecreate || !healthy) { recreate = await _input.PromptYesNo($"Do you want to replace the existing task?", false); } } if (!recreate) { if (!healthy) { _log.Error("Proceeding with unhealthy scheduled task, automatic renewals may not work until this is addressed"); } return; } _log.Information("Deleting existing task {taskName} from Windows Task Scheduler.", taskName); taskService.RootFolder.DeleteTask(taskName, false); } var actionString = $"--{nameof(MainArguments.Renew).ToLowerInvariant()} --{nameof(MainArguments.BaseUri).ToLowerInvariant()} \"{_settings.BaseUri}\""; _log.Information("Adding Task Scheduler entry with the following settings", taskName); _log.Information("- Name {name}", taskName); _log.Information("- Path {action}", WorkingDirectory); _log.Information("- Command {exec} {action}", ExecutingFile, actionString); _log.Information("- Start at {start}", _settings.ScheduledTask.StartBoundary); if (_settings.ScheduledTask.RandomDelay.TotalMinutes > 0) { _log.Information("- Random delay {delay}", _settings.ScheduledTask.RandomDelay); } _log.Information("- Time limit {limit}", _settings.ScheduledTask.ExecutionTimeLimit); // Create a new task definition and assign properties var task = taskService.NewTask(); task.RegistrationInfo.Description = "Check for renewal of ACME certificates."; var now = DateTime.Now; var runtime = new DateTime(now.Year, now.Month, now.Day, _settings.ScheduledTask.StartBoundary.Hours, _settings.ScheduledTask.StartBoundary.Minutes, _settings.ScheduledTask.StartBoundary.Seconds); task.Triggers.Add(new DailyTrigger { DaysInterval = 1, StartBoundary = runtime, RandomDelay = _settings.ScheduledTask.RandomDelay }); task.Settings.ExecutionTimeLimit = _settings.ScheduledTask.ExecutionTimeLimit; task.Settings.MultipleInstances = TaskInstancesPolicy.IgnoreNew; task.Settings.RunOnlyIfNetworkAvailable = true; task.Settings.DisallowStartIfOnBatteries = false; task.Settings.StopIfGoingOnBatteries = false; task.Settings.StartWhenAvailable = true; // Create an action that will launch the app with the renew parameters whenever the trigger fires task.Actions.Add(new ExecAction(_settings.ExePath, actionString, WorkingDirectory)); task.Principal.RunLevel = TaskRunLevel.Highest; while (true) { try { if (!_arguments.UseDefaultTaskUser && runLevel.HasFlag(RunLevel.Interactive | RunLevel.Advanced) && await _input.PromptYesNo($"Do you want to specify the user the task will run as?", false)) { // Ask for the login and password to allow the task to run var username = await _input.RequestString("Enter the username (Domain\\username)"); var password = await _input.ReadPassword("Enter the user's password"); _log.Debug("Creating task to run as {username}", username); try { taskService.RootFolder.RegisterTaskDefinition( taskName, task, TaskCreation.Create, username, password, TaskLogonType.Password); } catch (UnauthorizedAccessException) { _log.Error("Unable to register scheduled task, please run as administrator or equivalent"); } } else if (existingTask != null) { _log.Debug("Creating task to run with previously chosen credentials"); string?password = null; string?username = null; if (existingTask.Definition.Principal.LogonType == TaskLogonType.Password) { username = existingTask.Definition.Principal.UserId; password = await _input.ReadPassword($"Password for {username}"); } task.Principal.UserId = existingTask.Definition.Principal.UserId; task.Principal.LogonType = existingTask.Definition.Principal.LogonType; try { taskService.RootFolder.RegisterTaskDefinition( taskName, task, TaskCreation.CreateOrUpdate, username, password, existingTask.Definition.Principal.LogonType); } catch (UnauthorizedAccessException) { _log.Error("Unable to register scheduled task, please run as administrator or equivalent"); } } else { _log.Debug("Creating task to run as system user"); task.Principal.UserId = "SYSTEM"; task.Principal.LogonType = TaskLogonType.ServiceAccount; try { taskService.RootFolder.RegisterTaskDefinition( taskName, task, TaskCreation.CreateOrUpdate, null, null, TaskLogonType.ServiceAccount); } catch (UnauthorizedAccessException) { _log.Error("Unable to register scheduled task, please run as administrator or equivalent"); } } break; } catch (COMException cex) { if (cex.HResult == -2147023570) { _log.Warning("Invalid username/password, please try again"); } else { _log.Error(cex, "Failed to create task"); break; } } catch (Exception ex) { _log.Error(ex, "Failed to create task"); break; } } }