Skip to content

Commit

Permalink
Add OpenTelemetry endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
LucHeart committed Nov 19, 2024
1 parent 7f8dfe2 commit d75f533
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 64 deletions.
2 changes: 1 addition & 1 deletion API/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
{
serverOptions.ListenAnyIP(80);
#if DEBUG
serverOptions.ListenAnyIP(443, options => { options.UseHttps("devcert.pfx"); });
serverOptions.ListenAnyIP(443, options => { options.UseHttps(); });
#endif
serverOptions.Limits.RequestHeadersTimeout = TimeSpan.FromMilliseconds(3000);
});
Expand Down
5 changes: 2 additions & 3 deletions API/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF
{
ApplicationLogging.LoggerFactory = loggerFactory;

app.UseCommonOpenShockServices();
app.UseCommonOpenShockMiddleware();

if (!_apiConfig.Db.SkipMigration)
{
Expand Down Expand Up @@ -199,15 +199,14 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerF
description.GroupName.ToUpperInvariant());
});


app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<UserHub>("/1/hubs/user",
options => { options.Transports = HttpTransportType.WebSockets; });
endpoints.MapHub<ShareLinkHub>("/1/hubs/share/link/{id}",
options => { options.Transports = HttpTransportType.WebSockets; });
endpoints.MapScalarApiReference(options =>
{
options.OpenApiRoutePattern = "/swagger/{documentName}/swagger.json";
Expand Down
5 changes: 5 additions & 0 deletions Common/Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10"/>
<PackageReference Include="NRedisStack" Version="0.13.0" />
<PackageReference Include="OpenShock.Serialization.Flatbuffers" Version="2.0.0-preview.5" />
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.10.0-beta.1" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.10.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.9.0" />
<PackageReference Include="Redis.OM" Version="0.7.6" />
<PackageReference Include="Semver" Version="3.0.0" />
<PackageReference Include="Serilog.Sinks.Grafana.Loki" Version="8.3.0"/>
Expand Down
2 changes: 2 additions & 0 deletions Common/Config/BaseConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ public class BaseConfig
{
[Required] public required DbConfig Db { get; init; }
[Required] public required RedisConfig Redis { get; init; }

public MetricsConfig Metrics { get; init; } = new();
}
8 changes: 8 additions & 0 deletions Common/Config/MetricsConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using OpenShock.Common.Utils;

namespace OpenShock.Common.Config;

public sealed class MetricsConfig
{
public IReadOnlyCollection<string> AllowedNetworks { get; init; } = TrustedProxiesFetcher.PrivateNetworks;
}
18 changes: 14 additions & 4 deletions Common/OpenShockMiddlewareHelper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Net;
using Microsoft.AspNetCore.HttpOverrides;
using OpenShock.Common.Config;
using OpenShock.Common.Redis;
using OpenShock.Common.Utils;
using Redis.OM;
Expand All @@ -19,12 +20,13 @@ public static class OpenShockMiddlewareHelper
ForwardedForHeaderName = "CF-Connecting-IP"
};

public static IApplicationBuilder UseCommonOpenShockServices(this IApplicationBuilder app)
public static IApplicationBuilder UseCommonOpenShockMiddleware(this IApplicationBuilder app)
{
foreach (var proxy in TrustedProxiesFetcher.GetTrustedProxies())
var config = app.ApplicationServices.GetRequiredService<BaseConfig>();

foreach (var proxy in TrustedProxiesFetcher.GetTrustedNetworks())
{
var split = proxy.Split('/');
ForwardedSettings.KnownNetworks.Add(new IPNetwork(IPAddress.Parse(split[0]), int.Parse(split[1])));
ForwardedSettings.KnownNetworks.Add(proxy);
}

app.UseForwardedHeaders(ForwardedSettings);
Expand Down Expand Up @@ -59,6 +61,14 @@ public static IApplicationBuilder UseCommonOpenShockServices(this IApplicationBu
redisConnection.CreateIndex(typeof(DeviceOnline));
redisConnection.CreateIndex(typeof(DevicePair));
redisConnection.CreateIndex(typeof(LcgNode));

var metricsAllowedIpNetworks = config.Metrics.AllowedNetworks.Select(x => IPNetwork.Parse(x));

app.UseOpenTelemetryPrometheusScrapingEndpoint(context =>
{
var remoteIp = context.Connection.RemoteIpAddress;
return remoteIp != null && metricsAllowedIpNetworks.Any(x => x.Contains(remoteIp));
});

return app;
}
Expand Down
17 changes: 15 additions & 2 deletions Common/OpenShockServiceHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
using OpenShock.Common.Services.BatchUpdate;
using OpenShock.Common.Services.RedisPubSub;
using OpenShock.Common.Services.Session;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
using Redis.OM;
using Redis.OM.Contracts;
using StackExchange.Redis;
Expand All @@ -34,6 +36,8 @@ public static class OpenShockServiceHelper
/// <returns></returns>
public static ServicesResult AddOpenShockServices(this IServiceCollection services, BaseConfig config)
{
services.AddSingleton<BaseConfig>();

// <---- ASP.NET ---->
services.AddExceptionHandler<OpenShockExceptionHandler>();

Expand Down Expand Up @@ -85,6 +89,8 @@ public static ServicesResult AddOpenShockServices(this IServiceCollection servic
{
setup.GroupNameFormat = "VVV";
setup.SubstituteApiVersionInUrl = true;
setup.DefaultApiVersion = new ApiVersion(1, 0);
setup.AssumeDefaultVersionWhenUnspecified = true;
});

// generic ASP.NET stuff
Expand All @@ -106,6 +112,14 @@ public static ServicesResult AddOpenShockServices(this IServiceCollection servic
});
});

// OpenTelemetry

services.AddOpenTelemetry()
.WithMetrics(metrics => metrics
.AddRuntimeInstrumentation()
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddPrometheusExporter());

// <---- Redis ---->

Expand Down Expand Up @@ -177,8 +191,7 @@ public static ServicesResult AddOpenShockServices(this IServiceCollection servic
services.AddSingleton<IBatchUpdateService, BatchUpdateService>();
services.AddHostedService<BatchUpdateService>(provider =>
(BatchUpdateService)provider.GetRequiredService<IBatchUpdateService>());



return new ServicesResult
{
RedisConfig = configurationOptions
Expand Down
129 changes: 77 additions & 52 deletions Common/Utils/TrustedProxiesFetcher.cs
Original file line number Diff line number Diff line change
@@ -1,66 +1,93 @@
namespace OpenShock.Common.Utils;
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;

namespace OpenShock.Common.Utils;

public static class TrustedProxiesFetcher
{
private static readonly string[] DefaultProxies = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"];
private static readonly HttpClient Client = new();

public static readonly string[] PrivateNetworks =
[
// Loopback
"127.0.0.0/8",
"::1/128",
"::ffff:127.0.0.0/8",

// Private IPv4
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",

// Private IPv6
"fc00::/7",
"fe80::/10",
];

private static readonly IPNetwork[] PrivateNetworksParsed =
PrivateNetworks.Select(x => IPNetwork.Parse(x)).ToArray();

// Fetched 00:00:00 30/09/2024 UTC
private static readonly string[] PrefetchedCloudflareProxies = [
private static readonly IPNetwork[] PrefetchedCloudflareProxies =
{
// IPv4
"173.245.48.0/20",
"103.21.244.0/22",
"103.22.200.0/22",
"103.31.4.0/22",
"141.101.64.0/18",
"108.162.192.0/18",
"190.93.240.0/20",
"188.114.96.0/20",
"197.234.240.0/22",
"198.41.128.0/17",
"162.158.0.0/15",
"104.16.0.0/13",
"104.24.0.0/14",
"172.64.0.0/13",
"131.0.72.0/22",
IPNetwork.Parse("173.245.48.0/20"),
IPNetwork.Parse("103.21.244.0/22"),
IPNetwork.Parse("103.22.200.0/22"),
IPNetwork.Parse("103.31.4.0/22"),
IPNetwork.Parse("141.101.64.0/18"),
IPNetwork.Parse("108.162.192.0/18"),
IPNetwork.Parse("190.93.240.0/20"),
IPNetwork.Parse("188.114.96.0/20"),
IPNetwork.Parse("197.234.240.0/22"),
IPNetwork.Parse("198.41.128.0/17"),
IPNetwork.Parse("162.158.0.0/15"),
IPNetwork.Parse("104.16.0.0/13"),
IPNetwork.Parse("104.24.0.0/14"),
IPNetwork.Parse("172.64.0.0/13"),
IPNetwork.Parse("131.0.72.0/22"),

// IPv6
"2400:cb00::/32",
"2606:4700::/32",
"2803:f800::/32",
"2405:b500::/32",
"2405:8100::/32",
"2a06:98c0::/29",
"2c0f:f248::/32",
];
IPNetwork.Parse("2400:cb00::/32"),
IPNetwork.Parse("2606:4700::/32"),
IPNetwork.Parse("2803:f800::/32"),
IPNetwork.Parse("2405:b500::/32"),
IPNetwork.Parse("2405:8100::/32"),
IPNetwork.Parse("2a06:98c0::/29"),
IPNetwork.Parse("2c0f:f248::/32"),
};

private static string[] SplitNewLine(string content)
private static readonly char[] NewLineSeperators = [' ', '\r', '\n', '\t'];

private static async Task<IEnumerable<IPNetwork>> FetchCloudflareIPs(Uri uri, CancellationToken ct)
{
return content.Split((char[])[' ', '\r', '\n', '\t'], StringSplitOptions.RemoveEmptyEntries);
using var response = await Client.GetAsync(uri, ct);
var stringResponse = await response.Content.ReadAsStringAsync(ct);

return ParseNetworks(stringResponse.AsSpan());
}

private static async Task<string[]> FetchCloudflareIPsV4(HttpClient client, CancellationToken ct)
private static IEnumerable<IPNetwork> ParseNetworks(ReadOnlySpan<char> response)
{
using var response = await client.GetAsync("https://www.cloudflare.com/ips-v4", ct);
return SplitNewLine(await response.Content.ReadAsStringAsync(ct));
}
var ranges = response.Split(NewLineSeperators);

private static async Task<string[]> FetchCloudflareIPsV6(HttpClient client, CancellationToken ct)
{
using var response = await client.GetAsync("https://www.cloudflare.com/ips-v6", ct);
return SplitNewLine(await response.Content.ReadAsStringAsync(ct));
}
var networks = new List<IPNetwork>();
foreach (var range in ranges)
{
networks.Add(IPNetwork.Parse(response[range]));
}

private static async Task<string[]> FetchCloudflareIPs()
return networks;
}

private static async Task<IEnumerable<IPNetwork>> FetchCloudflareIPs()
{
try
{
using CancellationTokenSource cts = new(TimeSpan.FromSeconds(2)); // Don't want to make application startup slow
CancellationToken ct = cts.Token;

using var client = new HttpClient();

var v4Task = FetchCloudflareIPsV4(client, ct);
var v6Task = FetchCloudflareIPsV6(client, ct);
using CancellationTokenSource cts = new(TimeSpan.FromSeconds(5)); // Don't want to make application startup slow
var ct = cts.Token;

var v4Task = FetchCloudflareIPs(new Uri("https://www.cloudflare.com/ips-v4"), ct);
var v6Task = FetchCloudflareIPs(new Uri("https://www.cloudflare.com/ips-v6"), ct);

await Task.WhenAll(v4Task, v6Task);

Expand All @@ -72,17 +99,15 @@ private static async Task<string[]> FetchCloudflareIPs()
}
}

private static readonly Task<string[]> CloudflareProxiesTask = FetchCloudflareIPs();

public static async Task<string[]> GetTrustedProxiesAsync(bool fetch = true)
public static async Task<IPNetwork[]> GetTrustedNetworksAsync(bool fetch = true)
{
string[] cfProxies = fetch ? await CloudflareProxiesTask : PrefetchedCloudflareProxies;
var cfProxies = fetch ? await FetchCloudflareIPs() : PrefetchedCloudflareProxies;

return [.. DefaultProxies, .. cfProxies];
return [.. PrivateNetworksParsed, .. cfProxies];
}

public static string[] GetTrustedProxies(bool fetch = true)
public static IPNetwork[] GetTrustedNetworks(bool fetch = true)
{
return GetTrustedProxiesAsync(fetch).Result;
return GetTrustedNetworksAsync(fetch).Result;
}
}
4 changes: 3 additions & 1 deletion Cron/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Hangfire;
using Hangfire.PostgreSql;
using OpenShock.Common;
using OpenTelemetry.Trace;

namespace OpenShock.Cron;

Expand Down Expand Up @@ -28,13 +29,14 @@ public void ConfigureServices(IServiceCollection services)
c.UseNpgsqlConnection(_config.Db.Conn)));
services.AddHangfireServer();


services.AddOpenShockServices(_config);
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory,
ILogger<Startup> logger)
{
app.UseCommonOpenShockServices();
app.UseCommonOpenShockMiddleware();

app.UseHangfireDashboard(options: new DashboardOptions
{
Expand Down
2 changes: 1 addition & 1 deletion LiveControlGateway/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public void ConfigureServices(IServiceCollection services)
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
ApplicationLogging.LoggerFactory = loggerFactory;
app.UseCommonOpenShockServices();
app.UseCommonOpenShockMiddleware();

app.UseSwagger();
var provider = app.ApplicationServices.GetRequiredService<IApiVersionDescriptionProvider>();
Expand Down

0 comments on commit d75f533

Please sign in to comment.