mirror of
https://github.com/immense/Remotely.git
synced 2025-10-26 11:27:15 +00:00
Add UseHttpLogging option in appsettings. (#562)
* Add UseHttpLogging option. * Default to false for UseHttpLogging. * Fix log downloads.
This commit is contained in:
parent
56ee561ca2
commit
6ddc20b322
@ -196,7 +196,19 @@ namespace Remotely.Agent.Services
|
||||
{
|
||||
_logger.LogInformation("Reconnected to server.");
|
||||
var device = await _deviceInfoService.CreateDevice(_connectionInfo.DeviceID, _connectionInfo.OrganizationID);
|
||||
_ = await _hubConnection.InvokeAsync<bool>("DeviceCameOnline", device);
|
||||
|
||||
if (!await _hubConnection.InvokeAsync<bool>("DeviceCameOnline", device))
|
||||
{
|
||||
await Connect();
|
||||
return;
|
||||
}
|
||||
|
||||
if (await CheckForServerMigration())
|
||||
{
|
||||
await Connect();
|
||||
return;
|
||||
}
|
||||
|
||||
await _updater.CheckForUpdates();
|
||||
}
|
||||
private void RegisterMessageHandlers()
|
||||
|
||||
@ -142,6 +142,7 @@ For more information on configuring ASP.NET Core, see https://docs.microsoft.com
|
||||
* Theme: The color theme to use for the site. Values are "Light" or "Dark". This can also be configured per-user in Account - Options.
|
||||
* TrustedCorsOrigins: For cross-origin API requests via JavaScript. The websites listed in this array with be allowed to make requests to the API. This does not grant authentication, which is still required on most endpoints.
|
||||
* UseHsts: Whether ASP.NET Core will use HTTP Strict Transport Security.
|
||||
* UseHttpLogging: Enables logging for all HTTP requests. Also enables additional log entries in `ClientDownloadsController` regarding the effective scheme, host, and remote IP address as a result of processing forwarded headers.
|
||||
|
||||
|
||||
## Changing the Database
|
||||
|
||||
@ -25,16 +25,21 @@ namespace Remotely.Server.API
|
||||
[ApiController]
|
||||
public class ClientDownloadsController : ControllerBase
|
||||
{
|
||||
private readonly IApplicationConfig _appConfig;
|
||||
private readonly IEmbeddedServerDataSearcher _embeddedDataSearcher;
|
||||
private readonly SemaphoreSlim _fileLock = new(1,1);
|
||||
private readonly SemaphoreSlim _fileLock = new(1, 1);
|
||||
private readonly IWebHostEnvironment _hostEnv;
|
||||
|
||||
private readonly ILogger<ClientDownloadsController> _logger;
|
||||
public ClientDownloadsController(
|
||||
IWebHostEnvironment hostEnv,
|
||||
IEmbeddedServerDataSearcher embeddedDataSearcher)
|
||||
IEmbeddedServerDataSearcher embeddedDataSearcher,
|
||||
IApplicationConfig appConfig,
|
||||
ILogger<ClientDownloadsController> logger)
|
||||
{
|
||||
_hostEnv = hostEnv;
|
||||
_embeddedDataSearcher = embeddedDataSearcher;
|
||||
_appConfig = appConfig;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet("desktop/{platformID}")]
|
||||
@ -138,6 +143,8 @@ namespace Remotely.Server.API
|
||||
|
||||
private async Task<IActionResult> GetDesktopFile(string filePath, string organizationId = null)
|
||||
{
|
||||
LogRequest(nameof(GetDesktopFile));
|
||||
|
||||
var serverUrl = $"{Request.Scheme}://{Request.Host}";
|
||||
var embeddedData = new EmbeddedServerData(new Uri(serverUrl), organizationId);
|
||||
var result = await _embeddedDataSearcher.GetRewrittenStream(filePath, embeddedData);
|
||||
@ -152,65 +159,81 @@ namespace Remotely.Server.API
|
||||
|
||||
private async Task<IActionResult> GetInstallFile(string organizationId, string platformID)
|
||||
{
|
||||
LogRequest(nameof(GetInstallFile));
|
||||
|
||||
if (!await _fileLock.WaitAsync(TimeSpan.FromSeconds(15)))
|
||||
{
|
||||
return StatusCode(StatusCodes.Status408RequestTimeout);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (await _fileLock.WaitAsync(TimeSpan.FromSeconds(15)))
|
||||
switch (platformID)
|
||||
{
|
||||
switch (platformID)
|
||||
{
|
||||
case "WindowsInstaller":
|
||||
case "WindowsInstaller":
|
||||
{
|
||||
var filePath = Path.Combine(_hostEnv.WebRootPath, "Content", "Remotely_Installer.exe");
|
||||
var serverUrl = $"{Request.Scheme}://{Request.Host}";
|
||||
var embeddedData = new EmbeddedServerData(new Uri(serverUrl), organizationId);
|
||||
var result = await _embeddedDataSearcher.GetRewrittenStream(filePath, embeddedData);
|
||||
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
var filePath = Path.Combine(_hostEnv.WebRootPath, "Content", "Remotely_Installer.exe");
|
||||
var serverUrl = $"{Request.Scheme}://{Request.Host}";
|
||||
var embeddedData = new EmbeddedServerData(new Uri(serverUrl), organizationId);
|
||||
var result = await _embeddedDataSearcher.GetRewrittenStream(filePath, embeddedData);
|
||||
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
throw result.Exception;
|
||||
}
|
||||
|
||||
return File(result.Value, "application/octet-stream", "Remotely_Installer.exe");
|
||||
throw result.Exception;
|
||||
}
|
||||
case "ManjaroInstaller-x64":
|
||||
{
|
||||
var fileName = "Install-Manjaro-x64.sh";
|
||||
|
||||
return await GetBashInstaller(fileName, organizationId);
|
||||
}
|
||||
case "UbuntuInstaller-x64":
|
||||
{
|
||||
var fileName = "Install-Ubuntu-x64.sh";
|
||||
return File(result.Value, "application/octet-stream", "Remotely_Installer.exe");
|
||||
}
|
||||
case "ManjaroInstaller-x64":
|
||||
{
|
||||
var fileName = "Install-Manjaro-x64.sh";
|
||||
|
||||
return await GetBashInstaller(fileName, organizationId);
|
||||
}
|
||||
case "MacOSInstaller-x64":
|
||||
{
|
||||
var fileName = "Install-MacOS-x64.sh";
|
||||
return await GetBashInstaller(fileName, organizationId);
|
||||
}
|
||||
case "UbuntuInstaller-x64":
|
||||
{
|
||||
var fileName = "Install-Ubuntu-x64.sh";
|
||||
|
||||
return await GetBashInstaller(fileName, organizationId);
|
||||
}
|
||||
case "MacOSInstaller-arm64":
|
||||
{
|
||||
var fileName = "Install-MacOS-arm64.sh";
|
||||
return await GetBashInstaller(fileName, organizationId);
|
||||
}
|
||||
case "MacOSInstaller-x64":
|
||||
{
|
||||
var fileName = "Install-MacOS-x64.sh";
|
||||
|
||||
return await GetBashInstaller(fileName, organizationId);
|
||||
}
|
||||
default:
|
||||
return BadRequest();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return StatusCode(StatusCodes.Status408RequestTimeout);
|
||||
return await GetBashInstaller(fileName, organizationId);
|
||||
}
|
||||
case "MacOSInstaller-arm64":
|
||||
{
|
||||
var fileName = "Install-MacOS-arm64.sh";
|
||||
|
||||
return await GetBashInstaller(fileName, organizationId);
|
||||
}
|
||||
default:
|
||||
return BadRequest();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_fileLock.CurrentCount == 0)
|
||||
_fileLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private void LogRequest(string methodName)
|
||||
{
|
||||
if (_appConfig.UseHttpLogging)
|
||||
{
|
||||
var ip = Request.HttpContext.Connection.RemoteIpAddress;
|
||||
if (ip?.IsIPv4MappedToIPv6 == true)
|
||||
{
|
||||
_fileLock.Release();
|
||||
ip = ip.MapToIPv4();
|
||||
}
|
||||
|
||||
_logger.LogInformation(
|
||||
"Started client download via {methodName}. Effective Scheme: {scheme}. Effective Host: {host}. Remote IP: {ip}.",
|
||||
methodName,
|
||||
Request.Scheme,
|
||||
Request.Host,
|
||||
$"{ip}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ using Remotely.Server.Auth;
|
||||
using Remotely.Server.Services;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System;
|
||||
|
||||
namespace Remotely.Server.API
|
||||
{
|
||||
@ -10,20 +11,22 @@ namespace Remotely.Server.API
|
||||
[ApiController]
|
||||
public class ServerLogsController : ControllerBase
|
||||
{
|
||||
private readonly IDataService _dataService;
|
||||
private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions() { WriteIndented = true };
|
||||
|
||||
public ServerLogsController(IDataService dataService)
|
||||
{
|
||||
DataService = dataService;
|
||||
_dataService = dataService;
|
||||
}
|
||||
public IDataService DataService { get; set; }
|
||||
|
||||
[ServiceFilter(typeof(ApiAuthorizationFilter))]
|
||||
[HttpGet("Download")]
|
||||
public ActionResult Download()
|
||||
{
|
||||
Request.Headers.TryGetValue("OrganizationID", out var orgID);
|
||||
var logs = DataService.GetAllEventLogs(orgID);
|
||||
var fileBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(logs));
|
||||
Request.Headers.TryGetValue("OrganizationID", out var orgId);
|
||||
|
||||
var logs = _dataService.GetAllEventLogs(User.Identity?.Name, orgId);
|
||||
var fileBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(logs, _jsonOptions));
|
||||
return File(fileBytes, "application/octet-stream", "ServerLogs.json");
|
||||
}
|
||||
}
|
||||
|
||||
@ -338,10 +338,12 @@
|
||||
<ValidationMessage For="() => Input.UseHsts" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>ICE Servers</label>
|
||||
<div class="form-group">
|
||||
<label>Use HTTP Logging</label>
|
||||
<br />
|
||||
<span class="text-muted">Must be edited in appsettings.json.</span>
|
||||
<InputCheckbox @bind-Value="Input.UseHttpLogging" autocomplete="off" />
|
||||
<br />
|
||||
<ValidationMessage For="() => Input.UseHttpLogging" />
|
||||
</div>
|
||||
|
||||
<h4>Connection Strings</h4>
|
||||
|
||||
@ -103,6 +103,9 @@ namespace Remotely.Server.Pages
|
||||
|
||||
[Display(Name = "Use HSTS")]
|
||||
public bool UseHsts { get; set; }
|
||||
|
||||
[Display(Name = "Use HTTP Logging")]
|
||||
public bool UseHttpLogging { get; set; }
|
||||
}
|
||||
|
||||
public class ConnectionStringsModel
|
||||
|
||||
@ -19,13 +19,13 @@
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-primary"
|
||||
onclick="location.assign('/API/ServerLogs/Download')">
|
||||
onclick="window.open('/API/ServerLogs/Download', '_blank')">
|
||||
Download Logs
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-primary"
|
||||
onclick="location.assign('/API/ScriptResults')">
|
||||
onclick="window.open('/API/ScriptResults', '_blank')">
|
||||
Download Script History
|
||||
</button>
|
||||
</div>
|
||||
@ -75,7 +75,15 @@
|
||||
<tr @key="eventLog">
|
||||
<td>@eventLog.EventType</td>
|
||||
<td>@eventLog.TimeStamp</td>
|
||||
<td>@eventLog.Message</td>
|
||||
<td>
|
||||
@if (!string.IsNullOrWhiteSpace(eventLog.Message))
|
||||
{
|
||||
foreach (var line in eventLog.Message.Split("\n", StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
<div>@line</div>
|
||||
}
|
||||
}
|
||||
</td>
|
||||
<td>@eventLog.Source</td>
|
||||
<td>@eventLog.StackTrace</td>
|
||||
</tr>
|
||||
|
||||
@ -33,6 +33,7 @@ using Remotely.Server.Services.RcImplementations;
|
||||
using Immense.RemoteControl.Server.Abstractions;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Remotely.Shared.Services;
|
||||
using System;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
var configuration = builder.Configuration;
|
||||
@ -43,7 +44,7 @@ builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging"));
|
||||
builder.Logging.AddConsole();
|
||||
builder.Logging.AddDebug();
|
||||
|
||||
if (EnvironmentHelper.IsWindows &&
|
||||
if (OperatingSystem.IsWindows() &&
|
||||
bool.TryParse(builder.Configuration["ApplicationOptions:EnableWindowsEventLog"], out var enableEventLog) &&
|
||||
enableEventLog)
|
||||
{
|
||||
@ -109,6 +110,16 @@ services.AddRazorPages();
|
||||
services.AddServerSideBlazor();
|
||||
services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<RemotelyUser>>();
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
services.AddHttpLogging(options =>
|
||||
{
|
||||
options.RequestHeaders.Add("X-Forwarded-For");
|
||||
options.RequestHeaders.Add("X-Forwarded-Proto");
|
||||
options.RequestHeaders.Add("X-Forwarded-Host");
|
||||
options.RequestHeaders.Add("X-Original-For");
|
||||
options.RequestHeaders.Add("X-Original-Proto");
|
||||
options.RequestHeaders.Add("X-Original-Host");
|
||||
options.RequestHeaders.Add("Host");
|
||||
});
|
||||
|
||||
var trustedOrigins = configuration.GetSection("ApplicationOptions:TrustedCorsOrigins").Get<string[]>();
|
||||
|
||||
@ -166,7 +177,7 @@ services.AddScoped<IEmailSenderEx, EmailSenderEx>();
|
||||
services.AddScoped<IEmailSender, EmailSender>();
|
||||
services.AddScoped<IAppDbFactory, AppDbFactory>();
|
||||
services.AddTransient<IDataService, DataService>();
|
||||
services.AddScoped<IApplicationConfig, ApplicationConfig>();
|
||||
services.AddSingleton<IApplicationConfig, ApplicationConfig>();
|
||||
services.AddScoped<ApiAuthorizationFilter>();
|
||||
services.AddScoped<ExpiringTokenFilter>();
|
||||
services.AddHostedService<DbCleanupService>();
|
||||
@ -200,9 +211,15 @@ services.AddScoped<IHubEventHandler>(s => s.GetRequiredService<IHubEventHandlerE
|
||||
services.AddSingleton<IServiceHubSessionCache, ServiceHubSessionCache>();
|
||||
|
||||
var app = builder.Build();
|
||||
var appConfig = app.Services.GetRequiredService<IApplicationConfig>();
|
||||
|
||||
app.UseForwardedHeaders();
|
||||
|
||||
if (appConfig.UseHttpLogging)
|
||||
{
|
||||
app.UseHttpLogging();
|
||||
}
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
|
||||
@ -34,6 +34,7 @@ namespace Remotely.Server.Services
|
||||
Theme Theme { get; }
|
||||
string[] TrustedCorsOrigins { get; }
|
||||
bool UseHsts { get; }
|
||||
bool UseHttpLogging { get; }
|
||||
}
|
||||
|
||||
public class ApplicationConfig : IApplicationConfig
|
||||
@ -70,6 +71,7 @@ namespace Remotely.Server.Services
|
||||
public Theme Theme => Enum.Parse<Theme>(Config["ApplicationOptions:Theme"] ?? "Dark", true);
|
||||
public string[] TrustedCorsOrigins => Config.GetSection("ApplicationOptions:TrustedCorsOrigins").Get<string[]>() ?? System.Array.Empty<string>();
|
||||
public bool UseHsts => bool.Parse(Config["ApplicationOptions:UseHsts"] ?? "false");
|
||||
public bool UseHttpLogging => bool.Parse(Config["ApplicationOptions:UseHttpLogging"] ?? "false");
|
||||
private IConfiguration Config { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ namespace Remotely.Server.Services
|
||||
|
||||
Device[] GetAllDevices(string orgID);
|
||||
|
||||
EventLog[] GetAllEventLogs(string orgID);
|
||||
EventLog[] GetAllEventLogs(string username, string orgId);
|
||||
|
||||
InviteLink[] GetAllInviteLinks(string organizationId);
|
||||
|
||||
@ -1069,12 +1069,27 @@ namespace Remotely.Server.Services
|
||||
return dbContext.Devices.Where(x => x.OrganizationID == orgID).ToArray();
|
||||
}
|
||||
|
||||
public EventLog[] GetAllEventLogs(string orgID)
|
||||
public EventLog[] GetAllEventLogs(string username, string orgId)
|
||||
{
|
||||
using var dbContext = _appDbFactory.GetContext();
|
||||
|
||||
return dbContext.EventLogs
|
||||
.Where(x => x.OrganizationID == orgID)
|
||||
var query = dbContext.EventLogs
|
||||
.AsNoTracking()
|
||||
.AsQueryable();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
var user = dbContext.Users.FirstOrDefault(x => x.UserName == username);
|
||||
if (user?.IsAdministrator == true)
|
||||
{
|
||||
return query
|
||||
.OrderByDescending(x => x.TimeStamp)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
return query
|
||||
.Where(x => x.OrganizationID == orgId)
|
||||
.OrderByDescending(x => x.TimeStamp)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
@ -6,7 +6,8 @@
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning"
|
||||
"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information",
|
||||
"Default": "Information"
|
||||
}
|
||||
},
|
||||
"ApplicationOptions": {
|
||||
@ -35,6 +36,7 @@
|
||||
"SmtpUserName": "",
|
||||
"Theme": "Dark",
|
||||
"TrustedCorsOrigins": [],
|
||||
"UseHsts": false
|
||||
"UseHsts": false,
|
||||
"UseHttpLogging": false
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user