2020-06-28 21:44:35 -05:00
|
|
|
using System;
|
2021-05-19 04:39:27 +02:00
|
|
|
using System.Globalization;
|
2017-09-25 17:18:13 +09:00
|
|
|
using System.IO;
|
2021-04-20 07:06:32 +02:00
|
|
|
using System.Linq;
|
2018-02-13 03:27:36 +09:00
|
|
|
using System.Net;
|
2020-06-28 17:55:27 +09:00
|
|
|
using BTCPayServer.Configuration;
|
2021-12-31 16:59:02 +09:00
|
|
|
using BTCPayServer.Controllers.GreenField;
|
2020-06-28 17:55:27 +09:00
|
|
|
using BTCPayServer.Data;
|
2021-04-20 07:06:32 +02:00
|
|
|
using BTCPayServer.Fido2;
|
2020-06-28 17:55:27 +09:00
|
|
|
using BTCPayServer.Filters;
|
|
|
|
using BTCPayServer.Logging;
|
2019-02-22 11:37:45 +01:00
|
|
|
using BTCPayServer.PaymentRequest;
|
2020-10-21 14:02:20 +02:00
|
|
|
using BTCPayServer.Plugins;
|
2020-06-28 17:55:27 +09:00
|
|
|
using BTCPayServer.Security;
|
2019-02-19 13:18:30 +09:00
|
|
|
using BTCPayServer.Services.Apps;
|
2019-04-22 09:41:20 +02:00
|
|
|
using BTCPayServer.Storage;
|
2021-12-31 16:59:02 +09:00
|
|
|
using Fido2NetLib;
|
2021-03-23 17:42:49 +09:00
|
|
|
using Microsoft.AspNetCore.Authentication;
|
2021-03-30 11:41:44 +09:00
|
|
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
2020-06-28 17:55:27 +09:00
|
|
|
using Microsoft.AspNetCore.Builder;
|
|
|
|
using Microsoft.AspNetCore.DataProtection;
|
|
|
|
using Microsoft.AspNetCore.Hosting;
|
2021-04-20 07:06:32 +02:00
|
|
|
using Microsoft.AspNetCore.Http;
|
2020-06-28 17:55:27 +09:00
|
|
|
using Microsoft.AspNetCore.HttpOverrides;
|
|
|
|
using Microsoft.AspNetCore.Identity;
|
2021-12-31 16:59:02 +09:00
|
|
|
using Microsoft.AspNetCore.Mvc;
|
2020-06-28 17:55:27 +09:00
|
|
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
|
|
|
using Microsoft.Extensions.Configuration;
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
|
|
using Microsoft.Extensions.Hosting;
|
|
|
|
using Microsoft.Extensions.Logging;
|
2021-01-06 15:51:13 +01:00
|
|
|
using Microsoft.Extensions.Options;
|
2020-06-28 17:55:27 +09:00
|
|
|
using Microsoft.Net.Http.Headers;
|
2017-09-13 15:47:34 +09:00
|
|
|
|
|
|
|
namespace BTCPayServer.Hosting
|
|
|
|
{
|
2017-10-27 17:53:04 +09:00
|
|
|
public class Startup
|
|
|
|
{
|
2019-10-03 17:06:49 +09:00
|
|
|
public Startup(IConfiguration conf, IWebHostEnvironment env, ILoggerFactory loggerFactory)
|
2017-10-27 17:53:04 +09:00
|
|
|
{
|
|
|
|
Configuration = conf;
|
|
|
|
_Env = env;
|
2018-11-01 12:52:32 +09:00
|
|
|
LoggerFactory = loggerFactory;
|
2021-11-22 17:16:08 +09:00
|
|
|
Logs = new Logs();
|
|
|
|
Logs.Configure(loggerFactory);
|
2017-10-27 17:53:04 +09:00
|
|
|
}
|
2020-06-28 22:07:48 -05:00
|
|
|
|
|
|
|
readonly IWebHostEnvironment _Env;
|
2017-10-27 17:53:04 +09:00
|
|
|
public IConfiguration Configuration
|
|
|
|
{
|
|
|
|
get; set;
|
|
|
|
}
|
2018-11-01 12:52:32 +09:00
|
|
|
public ILoggerFactory LoggerFactory { get; }
|
2021-11-22 17:16:08 +09:00
|
|
|
public Logs Logs { get; }
|
2017-09-13 15:47:34 +09:00
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
public void ConfigureServices(IServiceCollection services)
|
|
|
|
{
|
|
|
|
services.AddMemoryCache();
|
2020-01-26 15:02:40 +09:00
|
|
|
services.AddDataProtection()
|
|
|
|
.SetApplicationName("BTCPay Server")
|
2021-01-06 15:51:13 +01:00
|
|
|
.PersistKeysToFileSystem(new DirectoryInfo(new DataDirectories().Configure(Configuration).DataDir));
|
2017-10-27 17:53:04 +09:00
|
|
|
services.AddIdentity<ApplicationUser, IdentityRole>()
|
|
|
|
.AddEntityFrameworkStores<ApplicationDbContext>()
|
2019-10-08 15:21:30 +09:00
|
|
|
.AddDefaultTokenProviders();
|
2021-03-23 17:42:49 +09:00
|
|
|
services.Configure<AuthenticationOptions>(opts =>
|
|
|
|
{
|
|
|
|
opts.DefaultAuthenticateScheme = null;
|
|
|
|
opts.DefaultChallengeScheme = null;
|
|
|
|
opts.DefaultForbidScheme = null;
|
|
|
|
opts.DefaultScheme = IdentityConstants.ApplicationScheme;
|
|
|
|
opts.DefaultSignInScheme = null;
|
|
|
|
opts.DefaultSignOutScheme = null;
|
|
|
|
});
|
2021-03-30 11:41:44 +09:00
|
|
|
services.PostConfigure<CookieAuthenticationOptions>(IdentityConstants.ApplicationScheme, opt =>
|
|
|
|
{
|
|
|
|
opt.LoginPath = "/login";
|
2022-01-07 19:46:38 +09:00
|
|
|
opt.AccessDeniedPath = "/Error/Denied";
|
2022-01-07 21:47:19 +09:00
|
|
|
opt.LogoutPath = "/logout";
|
2021-03-30 11:41:44 +09:00
|
|
|
});
|
|
|
|
|
2021-03-23 17:42:49 +09:00
|
|
|
services.Configure<SecurityStampValidatorOptions>(opts =>
|
|
|
|
{
|
|
|
|
opts.ValidationInterval = TimeSpan.FromMinutes(5.0);
|
|
|
|
});
|
2019-10-08 15:21:30 +09:00
|
|
|
|
2021-11-22 17:16:08 +09:00
|
|
|
services.AddBTCPayServer(Configuration, Logs);
|
2019-04-22 09:41:20 +02:00
|
|
|
services.AddProviderStorage();
|
2019-04-13 14:00:48 +02:00
|
|
|
services.AddSession();
|
2019-05-24 06:17:02 +00:00
|
|
|
services.AddSignalR();
|
2021-04-20 07:06:32 +02:00
|
|
|
services.AddFido2(options =>
|
|
|
|
{
|
|
|
|
options.ServerName = "BTCPay Server";
|
|
|
|
})
|
|
|
|
.AddCachedMetadataService(config =>
|
|
|
|
{
|
|
|
|
//They'll be used in a "first match wins" way in the order registered
|
|
|
|
config.AddStaticMetadataRepository();
|
|
|
|
});
|
2021-12-31 16:59:02 +09:00
|
|
|
var descriptor = services.Single(descriptor => descriptor.ServiceType == typeof(Fido2Configuration));
|
2021-04-20 07:06:32 +02:00
|
|
|
services.Remove(descriptor);
|
|
|
|
services.AddScoped(provider =>
|
|
|
|
{
|
|
|
|
var httpContext = provider.GetService<IHttpContextAccessor>();
|
|
|
|
return new Fido2Configuration()
|
|
|
|
{
|
|
|
|
ServerName = "BTCPay Server",
|
|
|
|
Origin = $"{httpContext.HttpContext.Request.Scheme}://{httpContext.HttpContext.Request.Host}",
|
|
|
|
ServerDomain = httpContext.HttpContext.Request.Host.Host
|
|
|
|
};
|
|
|
|
});
|
|
|
|
services.AddScoped<Fido2Service>();
|
2021-12-24 09:27:00 +01:00
|
|
|
services.AddSingleton<UserLoginCodeService>();
|
2021-12-31 16:59:02 +09:00
|
|
|
|
|
|
|
var mvcBuilder = services.AddMvc(o =>
|
|
|
|
{
|
|
|
|
o.Filters.Add(new XFrameOptionsAttribute("DENY"));
|
|
|
|
o.Filters.Add(new XContentTypeOptionsAttribute("nosniff"));
|
|
|
|
o.Filters.Add(new XXSSProtectionAttribute());
|
|
|
|
o.Filters.Add(new ReferrerPolicyAttribute("same-origin"));
|
|
|
|
o.ModelBinderProviders.Insert(0, new ModelBinders.DefaultModelBinderProvider());
|
|
|
|
if (!Configuration.GetOrDefault<bool>("nocsp", false))
|
|
|
|
o.Filters.Add(new ContentSecurityPolicyAttribute(CSPTemplate.AntiXSS));
|
|
|
|
o.Filters.Add(new JsonHttpExceptionFilter());
|
2022-01-10 22:10:04 +09:00
|
|
|
o.Filters.Add(new JsonObjectExceptionFilter());
|
2021-12-31 16:59:02 +09:00
|
|
|
})
|
2020-03-13 11:47:22 +01:00
|
|
|
.ConfigureApiBehaviorOptions(options =>
|
|
|
|
{
|
|
|
|
options.InvalidModelStateResponseFactory = context =>
|
|
|
|
{
|
2021-04-26 12:37:56 +09:00
|
|
|
return new UnprocessableEntityObjectResult(context.ModelState.ToGreenfieldValidationError());
|
2020-03-13 11:47:22 +01:00
|
|
|
};
|
|
|
|
})
|
2020-08-12 16:02:13 +09:00
|
|
|
.AddRazorOptions(o =>
|
|
|
|
{
|
|
|
|
// /Components/{View Component Name}/{View Name}.cshtml
|
|
|
|
o.ViewLocationFormats.Add("/{0}.cshtml");
|
|
|
|
o.PageViewLocationFormats.Add("/{0}.cshtml");
|
|
|
|
})
|
2019-10-06 15:54:19 +09:00
|
|
|
.AddNewtonsoftJson()
|
2020-01-10 17:47:54 +09:00
|
|
|
.AddRazorRuntimeCompilation()
|
2020-10-21 14:02:20 +02:00
|
|
|
.AddPlugins(services, Configuration, LoggerFactory)
|
2019-10-06 15:54:19 +09:00
|
|
|
.AddControllersAsServices();
|
2022-01-08 14:15:26 +09:00
|
|
|
ValidateControllerNameTransformer.Register(services);
|
2020-10-15 14:28:09 +02:00
|
|
|
|
2018-07-12 17:38:21 +09:00
|
|
|
services.TryAddScoped<ContentSecurityPolicies>();
|
2018-01-04 22:56:49 +09:00
|
|
|
services.Configure<IdentityOptions>(options =>
|
|
|
|
{
|
|
|
|
options.Password.RequireDigit = false;
|
2019-01-08 18:32:07 -07:00
|
|
|
options.Password.RequiredLength = 6;
|
2018-01-04 22:56:49 +09:00
|
|
|
options.Password.RequireLowercase = false;
|
|
|
|
options.Password.RequireNonAlphanumeric = false;
|
|
|
|
options.Password.RequireUppercase = false;
|
2018-09-12 13:36:44 +02:00
|
|
|
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
|
|
|
|
options.Lockout.MaxFailedAccessAttempts = 5;
|
|
|
|
options.Lockout.AllowedForNewUsers = true;
|
2019-10-08 15:21:30 +09:00
|
|
|
options.Password.RequireUppercase = false;
|
2018-01-04 22:56:49 +09:00
|
|
|
});
|
2018-11-01 04:07:28 +01:00
|
|
|
// If the HTTPS certificate path is not set this logic will NOT be used and the default Kestrel binding logic will be.
|
|
|
|
string httpsCertificateFilePath = Configuration.GetOrDefault<string>("HttpsCertificateFilePath", null);
|
2018-11-01 12:52:32 +09:00
|
|
|
bool useDefaultCertificate = Configuration.GetOrDefault<bool>("HttpsUseDefaultCertificate", false);
|
|
|
|
bool hasCertPath = !String.IsNullOrEmpty(httpsCertificateFilePath);
|
2019-07-12 12:23:13 +09:00
|
|
|
services.Configure<KestrelServerOptions>(kestrel =>
|
|
|
|
{
|
|
|
|
kestrel.Limits.MaxRequestLineSize = 8_192 * 10 * 5; // Around 500K, transactions passed in URI should not be bigger than this
|
|
|
|
});
|
2018-11-01 12:52:32 +09:00
|
|
|
if (hasCertPath || useDefaultCertificate)
|
2018-11-01 04:07:28 +01:00
|
|
|
{
|
|
|
|
var bindAddress = Configuration.GetOrDefault<IPAddress>("bind", IPAddress.Any);
|
|
|
|
int bindPort = Configuration.GetOrDefault<int>("port", 443);
|
|
|
|
|
|
|
|
services.Configure<KestrelServerOptions>(kestrel =>
|
|
|
|
{
|
2018-11-01 12:52:32 +09:00
|
|
|
if (hasCertPath && !File.Exists(httpsCertificateFilePath))
|
2018-11-01 04:07:28 +01:00
|
|
|
{
|
|
|
|
// Note that by design this is a fatal error condition that will cause the process to exit.
|
|
|
|
throw new ConfigException($"The https certificate file could not be found at {httpsCertificateFilePath}.");
|
|
|
|
}
|
2019-10-08 15:21:30 +09:00
|
|
|
if (hasCertPath && useDefaultCertificate)
|
2018-11-01 12:52:32 +09:00
|
|
|
{
|
|
|
|
throw new ConfigException($"Conflicting settings: if HttpsUseDefaultCertificate is true, HttpsCertificateFilePath should not be used");
|
|
|
|
}
|
2018-11-01 04:07:28 +01:00
|
|
|
|
|
|
|
kestrel.Listen(bindAddress, bindPort, l =>
|
|
|
|
{
|
2018-11-01 12:52:32 +09:00
|
|
|
if (hasCertPath)
|
|
|
|
{
|
|
|
|
Logs.Configuration.LogInformation($"Using HTTPS with the certificate located in {httpsCertificateFilePath}.");
|
|
|
|
l.UseHttps(httpsCertificateFilePath, Configuration.GetOrDefault<string>("HttpsCertificateFilePassword", null));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Logs.Configuration.LogInformation($"Using HTTPS with the default certificate");
|
|
|
|
l.UseHttps();
|
|
|
|
}
|
2018-11-01 04:07:28 +01:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2017-10-27 17:53:04 +09:00
|
|
|
}
|
|
|
|
public void Configure(
|
|
|
|
IApplicationBuilder app,
|
2019-10-03 17:06:49 +09:00
|
|
|
IWebHostEnvironment env,
|
2017-10-27 17:53:04 +09:00
|
|
|
IServiceProvider prov,
|
2018-04-05 15:50:23 +09:00
|
|
|
BTCPayServerOptions options,
|
2021-01-06 15:51:13 +01:00
|
|
|
IOptions<DataDirectories> dataDirectories,
|
2017-10-27 17:53:04 +09:00
|
|
|
ILoggerFactory loggerFactory)
|
2018-04-05 15:50:23 +09:00
|
|
|
{
|
2021-11-22 17:16:08 +09:00
|
|
|
Logs.Configure(loggerFactory);
|
2018-04-05 15:50:23 +09:00
|
|
|
Logs.Configuration.LogInformation($"Root Path: {options.RootPath}");
|
|
|
|
if (options.RootPath.Equals("/", StringComparison.OrdinalIgnoreCase))
|
|
|
|
{
|
2021-11-22 17:16:08 +09:00
|
|
|
ConfigureCore(app, env, prov, dataDirectories);
|
2018-04-05 15:50:23 +09:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
app.Map(options.RootPath, appChild =>
|
|
|
|
{
|
2021-11-22 17:16:08 +09:00
|
|
|
ConfigureCore(appChild, env, prov, dataDirectories);
|
2018-04-05 15:50:23 +09:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2021-11-22 17:16:08 +09:00
|
|
|
private void ConfigureCore(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider prov, IOptions<DataDirectories> dataDirectories)
|
2017-10-27 17:53:04 +09:00
|
|
|
{
|
|
|
|
if (env.IsDevelopment())
|
|
|
|
{
|
|
|
|
app.UseDeveloperExceptionPage();
|
|
|
|
}
|
2019-10-09 22:26:54 +09:00
|
|
|
app.UseHeadersOverride();
|
2019-03-04 22:34:14 +09:00
|
|
|
var forwardingOptions = new ForwardedHeadersOptions()
|
2019-03-04 20:48:19 +09:00
|
|
|
{
|
2019-03-04 22:34:14 +09:00
|
|
|
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
|
|
|
|
};
|
|
|
|
forwardingOptions.KnownNetworks.Clear();
|
|
|
|
forwardingOptions.KnownProxies.Clear();
|
|
|
|
forwardingOptions.ForwardedHeaders = ForwardedHeaders.All;
|
|
|
|
app.UseForwardedHeaders(forwardingOptions);
|
2020-02-01 01:40:50 -06:00
|
|
|
|
|
|
|
app.UseStatusCodePagesWithReExecute("/Error/Handle", "?statusCode={0}");
|
|
|
|
|
2019-10-06 15:47:46 +09:00
|
|
|
app.UsePayServer();
|
|
|
|
app.UseRouting();
|
2018-08-06 12:04:36 +09:00
|
|
|
app.UseCors();
|
2019-10-06 15:47:46 +09:00
|
|
|
|
2020-04-18 17:56:05 +02:00
|
|
|
app.UseStaticFiles(new StaticFileOptions
|
|
|
|
{
|
|
|
|
OnPrepareResponse = ctx =>
|
|
|
|
{
|
|
|
|
// Cache static assets for one year, set asp-append-version="true" on references to update on change.
|
|
|
|
// https://andrewlock.net/adding-cache-control-headers-to-static-files-in-asp-net-core/
|
|
|
|
const int durationInSeconds = 60 * 60 * 24 * 365;
|
|
|
|
ctx.Context.Response.Headers[HeaderNames.CacheControl] = "public,max-age=" + durationInSeconds;
|
|
|
|
}
|
|
|
|
});
|
2020-06-28 17:55:27 +09:00
|
|
|
|
2020-12-27 22:06:00 +09:00
|
|
|
app.UseProviderStorage(dataDirectories);
|
2017-10-27 17:53:04 +09:00
|
|
|
app.UseAuthentication();
|
2019-10-06 01:21:00 +09:00
|
|
|
app.UseAuthorization();
|
2019-04-13 14:00:48 +02:00
|
|
|
app.UseSession();
|
2020-01-12 13:30:54 +09:00
|
|
|
|
2017-12-17 19:58:55 +09:00
|
|
|
app.UseWebSockets();
|
2020-01-12 13:30:54 +09:00
|
|
|
|
2021-03-19 20:54:30 +09:00
|
|
|
app.UseCookiePolicy(new CookiePolicyOptions()
|
|
|
|
{
|
|
|
|
HttpOnly = Microsoft.AspNetCore.CookiePolicy.HttpOnlyPolicy.Always,
|
|
|
|
Secure = Microsoft.AspNetCore.Http.CookieSecurePolicy.SameAsRequest
|
|
|
|
});
|
2019-10-03 18:46:09 +09:00
|
|
|
app.UseEndpoints(endpoints =>
|
|
|
|
{
|
|
|
|
AppHub.Register(endpoints);
|
|
|
|
PaymentRequestHub.Register(endpoints);
|
2021-11-24 10:52:47 +01:00
|
|
|
endpoints.MapRazorPages();
|
2019-10-06 01:21:00 +09:00
|
|
|
endpoints.MapControllers();
|
2022-01-08 14:15:26 +09:00
|
|
|
endpoints.MapControllerRoute("default", "{controller:validate=Home}/{action=Index}/{id?}");
|
2019-10-03 18:46:09 +09:00
|
|
|
});
|
2021-11-24 10:52:47 +01:00
|
|
|
app.UsePlugins();
|
2017-10-27 17:53:04 +09:00
|
|
|
}
|
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
}
|