Skip to content

Commit

Permalink
Merge pull request #145 from Beyley/https
Browse files Browse the repository at this point in the history
Add rudimentary HTTPS support
  • Loading branch information
jvyden authored Jul 11, 2024
2 parents 127c34e + fd389c6 commit fa6b594
Show file tree
Hide file tree
Showing 13 changed files with 212 additions and 31 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/nuget.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ jobs:
run: dotnet nuget push Bunkum.Protocols.Http/bin/Release/Bunkum.Protocols.Http.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN}
env:
NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}
- name: Upload Bunkum.Protocols.Https
run: dotnet nuget push Bunkum.Protocols.Https/bin/Release/Bunkum.Protocols.Https.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN}
env:
NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}
- name: Upload Bunkum.Protocols.TlsSupport
run: dotnet nuget push Bunkum.Protocols.TlsSupport/bin/Release/Bunkum.Protocols.TlsSupport.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN}
env:
NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}
- name: Upload Bunkum.Protocols.Gopher
run: dotnet nuget push Bunkum.Protocols.Gopher/bin/Release/Bunkum.Protocols.Gopher.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN}
env:
Expand Down
1 change: 1 addition & 0 deletions Bunkum.Protocols.Gemini/Bunkum.Protocols.Gemini.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
<ItemGroup>
<ProjectReference Include="..\Bunkum.Core\Bunkum.Core.csproj" />
<ProjectReference Include="..\Bunkum.Listener\Bunkum.Listener.csproj" />
<ProjectReference Include="..\Bunkum.Protocols.TlsSupport\Bunkum.Protocols.TlsSupport.csproj" />
</ItemGroup>

</Project>
8 changes: 5 additions & 3 deletions Bunkum.Protocols.Gemini/BunkumGeminiServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Bunkum.Core.Configuration;
using Bunkum.Listener;
using Bunkum.Protocols.Gemini.Socket;
using Bunkum.Protocols.TlsSupport;
using NotEnoughLogs;
using NotEnoughLogs.Sinks;

Expand All @@ -11,6 +12,7 @@ namespace Bunkum.Protocols.Gemini;
public class BunkumGeminiServer : BunkumServer
{
private readonly X509Certificate2 _cert;
private readonly SslConfiguration _sslConfiguration;

/// <summary>
/// Create a new BunkumGeminiServer
Expand All @@ -21,14 +23,14 @@ public class BunkumGeminiServer : BunkumServer
public BunkumGeminiServer(SslConfiguration? sslConfiguration = null, LoggerConfiguration? configuration = null, List<ILoggerSink>? sinks = null) : base(configuration, sinks)
{
//If the SSL configuration is not specified, load the config from JSON
sslConfiguration ??= Config.LoadFromJsonFile<SslConfiguration>("geminissl.json", this.Logger);
this._sslConfiguration = sslConfiguration ?? Config.LoadFromJsonFile<SslConfiguration>("geminissl.json", this.Logger);

this._cert = new X509Certificate2(File.ReadAllBytes(sslConfiguration.SslCertificate), sslConfiguration.CertificatePassword);
this._cert = new X509Certificate2(File.ReadAllBytes(sslConfiguration.SslCertificate), this._sslConfiguration.CertificatePassword);
}

protected override BunkumListener CreateDefaultListener(Uri listenEndpoint, bool useForwardedIp, Logger logger)
{
return new SocketGeminiListener(this._cert, listenEndpoint, logger);
return new SocketGeminiListener(this._cert, this._sslConfiguration, listenEndpoint, logger);
}
protected override string ProtocolUriName => "gemini";
}
21 changes: 15 additions & 6 deletions Bunkum.Protocols.Gemini/Socket/SocketGeminiListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
Expand All @@ -11,6 +11,7 @@
using Bunkum.Listener.Extensions;
using Bunkum.Listener.Protocol;
using Bunkum.Listener.Request;
using Bunkum.Protocols.TlsSupport;
using NotEnoughLogs;

namespace Bunkum.Protocols.Gemini.Socket;
Expand All @@ -20,14 +21,16 @@ public partial class SocketGeminiListener : BunkumGeminiListener
private System.Net.Sockets.Socket? _socket;
private readonly Uri _listenEndpoint;
private readonly X509Certificate2 _cert;
private readonly SslConfiguration _sslConfiguration;

[GeneratedRegex("^[a-zA-Z]+$")]
private static partial Regex LettersRegex();

public SocketGeminiListener(X509Certificate2 cert, Uri listenEndpoint, Logger logger) : base(logger)
public SocketGeminiListener(X509Certificate2 cert, SslConfiguration sslConfiguration, Uri listenEndpoint, Logger logger) : base(logger)
{
this._listenEndpoint = listenEndpoint;
this._cert = cert;
this._sslConfiguration = sslConfiguration;

this.Logger.LogInfo(ListenerCategory.Startup, "Internal Gemini server is listening at URL {0}", listenEndpoint);
}
Expand Down Expand Up @@ -88,13 +91,19 @@ private async Task<ListenerContext> ReadRequestIntoContext(System.Net.Sockets.So
{
SslStream stream = new(rawStream);

await stream.AuthenticateAsServerAsync(new SslServerAuthenticationOptions
SslServerAuthenticationOptions authOptions = new()
{
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13,
EnabledSslProtocols = this._sslConfiguration.EnabledSslProtocols,
ServerCertificate = this._cert,
ClientCertificateRequired = true,
RemoteCertificateValidationCallback = (sender, certificate, chain, errors) => true,
});
RemoteCertificateValidationCallback = (_, _, _, _) => true,
};

// If the cipher suite set is enabled, and we are not on windows, enable the selected cipher suites
if (this._sslConfiguration.EnabledCipherSuites != null && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
authOptions.CipherSuitesPolicy = new CipherSuitesPolicy(this._sslConfiguration.EnabledCipherSuites);

await stream.AuthenticateAsServerAsync(authOptions);

Uri uri = new(GetPath(stream));

Expand Down
15 changes: 0 additions & 15 deletions Bunkum.Protocols.Gemini/SslConfiguration.cs

This file was deleted.

2 changes: 2 additions & 0 deletions Bunkum.Protocols.Http/BunkumHttpServer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Security.Cryptography.X509Certificates;
using Bunkum.Core;
using Bunkum.Core.Configuration;
using Bunkum.Listener;
using NotEnoughLogs;
using NotEnoughLogs.Sinks;
Expand Down
50 changes: 47 additions & 3 deletions Bunkum.Protocols.Http/Socket/SocketHttpListener.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using System.Diagnostics;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;
using System.Web;
using Bunkum.Listener;
Expand All @@ -16,14 +20,27 @@ public partial class SocketHttpListener : BunkumHttpListener
private System.Net.Sockets.Socket? _socket;
private readonly Uri _listenEndpoint;
private readonly bool _useForwardedIp;

private readonly X509Certificate2? _cert;
private readonly SslProtocols _enabledSslProtocols;
private readonly TlsCipherSuite[]? _enabledCipherSuites;

[GeneratedRegex("^[a-zA-Z]+$")]
private static partial Regex LettersRegex();

public SocketHttpListener(Uri listenEndpoint, bool useForwardedIp, Logger logger) : base(logger)
public SocketHttpListener(
Uri listenEndpoint,
bool useForwardedIp,
Logger logger,
X509Certificate2? certificate = null,
SslProtocols enabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13,
TlsCipherSuite[]? enabledCipherSuites = null)
: base(logger)
{
this._listenEndpoint = listenEndpoint;
this._useForwardedIp = useForwardedIp;
this._cert = certificate;
this._enabledCipherSuites = enabledCipherSuites;
this._enabledSslProtocols = enabledSslProtocols;

this.Logger.LogInfo(ListenerCategory.Startup, "Internal HTTP server is listening at URL " + listenEndpoint);
}
Expand Down Expand Up @@ -75,8 +92,31 @@ public override void StartListening()
}
}

private ListenerContext ReadRequestIntoContext(System.Net.Sockets.Socket client, Stream stream)
private ListenerContext ReadRequestIntoContext(System.Net.Sockets.Socket client, Stream rawStream)
{
Stream stream = rawStream;
SslStream? sslStream = null;
if (this._cert != null)
{
sslStream = new SslStream(rawStream);

SslServerAuthenticationOptions authOptions = new()
{
EnabledSslProtocols = this._enabledSslProtocols,
ServerCertificate = this._cert,
ClientCertificateRequired = false,
RemoteCertificateValidationCallback = (_, _, _, _) => true,
};

// If the cipher suite set is enabled, and we are not on windows, enable the selected cipher suites
if (this._enabledCipherSuites != null && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
authOptions.CipherSuitesPolicy = new CipherSuitesPolicy(this._enabledCipherSuites);

sslStream.AuthenticateAsServer(authOptions);

stream = sslStream;
}

Span<char> method = stackalloc char[RequestLineMethodLimit];
Span<char> path = stackalloc char[RequestLinePathLimit];
Span<char> version = stackalloc char[RequestLineVersionLimit];
Expand Down Expand Up @@ -199,6 +239,10 @@ private ListenerContext ReadRequestIntoContext(System.Net.Sockets.Socket client,
}
context.InputStream = inputStream;

// If this an SSL connection, set the remote certificate
if (sslStream != null)
context.RemoteCertificate = sslStream.RemoteCertificate;

return context;
}

Expand Down
11 changes: 7 additions & 4 deletions Bunkum.Protocols.Http/Socket/SocketHttpListenerContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ public SocketHttpListenerContext(System.Net.Sockets.Socket socket, Stream stream

protected override async Task SendResponseInternal(HttpStatusCode code, ArraySegment<byte>? data = null)
{
// this is dumb and stupid
this.ResponseHeaders.Add("Server", "Bunkum");
this.ResponseHeaders.Add("Connection", "close");
this.ResponseHeaders.Add("Date", DateTime.UtcNow.ToString("ddd, dd MMM yyyy HH:mm:ss 'GMT'"));
// These are "TryAdd" and not "Add" since if you are proxying requests,
// both of these should be set to the headers sent by the proxied server, and not be generated by Bunkum directly
this.ResponseHeaders.TryAdd("Server", "Bunkum");
this.ResponseHeaders.TryAdd("Date", DateTime.UtcNow.ToString("ddd, dd MMM yyyy HH:mm:ss 'GMT'"));
// This is an unconditional set because Bunkum does not support keep-alive connections,
// theres currently no reason for anything other than Bunkum to set this value
this.ResponseHeaders["Connection"] = "close";

List<string> response = new() { $"HTTP/1.1 {code.GetHashCode()} {code.ToString()}" }; // TODO: spaced code names ("Not Found" instead of "NotFound")
foreach ((string? key, string? value) in this.ResponseHeaders)
Expand Down
14 changes: 14 additions & 0 deletions Bunkum.Protocols.Https/Bunkum.Protocols.Https.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Bunkum.Protocols.Http\Bunkum.Protocols.Http.csproj" />
<ProjectReference Include="..\Bunkum.Protocols.TlsSupport\Bunkum.Protocols.TlsSupport.csproj" />
</ItemGroup>

</Project>
33 changes: 33 additions & 0 deletions Bunkum.Protocols.Https/BunkumHttpsServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Security.Cryptography.X509Certificates;
using Bunkum.Core;
using Bunkum.Core.Configuration;
using Bunkum.Listener;
using Bunkum.Protocols.TlsSupport;
using NotEnoughLogs;
using NotEnoughLogs.Sinks;

namespace Bunkum.Protocols.Https;

public class BunkumHttpsServer : BunkumServer
{
private readonly X509Certificate2? _cert;
private readonly SslConfiguration _sslConfiguration;

public BunkumHttpsServer(LoggerConfiguration? configuration = null, List<ILoggerSink>? sinks = null,
SslConfiguration? sslConfiguration = null) : base(configuration, sinks)
{
//If the SSL configuration is not specified, load the config from JSON
this._sslConfiguration = sslConfiguration ?? Config.LoadFromJsonFile<SslConfiguration>("ssl.json", this.Logger);

this._cert = new X509Certificate2(File.ReadAllBytes(this._sslConfiguration.SslCertificate), this._sslConfiguration.CertificatePassword);
}

/// <inherit-doc/>
protected override BunkumListener CreateDefaultListener(Uri listenEndpoint, bool useForwardedIp, Logger logger)
{
return new Http.Socket.SocketHttpListener(listenEndpoint, useForwardedIp, logger, this._cert, this._sslConfiguration.EnabledSslProtocols, this._sslConfiguration.EnabledCipherSuites);
}

/// <inherit-doc/>
protected override string ProtocolUriName => "https";
}
13 changes: 13 additions & 0 deletions Bunkum.Protocols.TlsSupport/Bunkum.Protocols.TlsSupport.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Bunkum.Core\Bunkum.Core.csproj" />
</ItemGroup>

</Project>
53 changes: 53 additions & 0 deletions Bunkum.Protocols.TlsSupport/SslConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Net.Security;
using System.Security.Authentication;
using Bunkum.Core.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace Bunkum.Protocols.TlsSupport;

public class SslConfiguration : Config
{
public override int CurrentConfigVersion => 1;
public override int Version { get; set; }

protected override void Migrate(int oldVer, dynamic oldConfig)
{}

/// <summary>
/// The path to the certificate
/// </summary>
public string SslCertificate { get; set; } = "cert.pfx";
/// <summary>
/// The password for the certificate, null if none
/// </summary>
public string? CertificatePassword { get; set; }

/// <summary>
/// The SSL protocols which are enabled. If null, enables TLS1.3 and TLS1.2
/// </summary>
[JsonProperty("EnabledSslProtocols", ItemConverterType = typeof(StringEnumConverter))]
private SslProtocols[]? _EnabledSslProtocols { get; set; }
/// <summary>
/// The cipher suites which are enabled. If null, lets the system decide
/// </summary>
[JsonProperty(ItemConverterType = typeof(StringEnumConverter))]
public TlsCipherSuite[]? EnabledCipherSuites { get; set; }

[JsonIgnore]
public SslProtocols EnabledSslProtocols
{
get
{
SslProtocols protocols = SslProtocols.None;

if (this._EnabledSslProtocols == null)
protocols = SslProtocols.Tls12 | SslProtocols.Tls13;
else
protocols = this._EnabledSslProtocols
.Aggregate(protocols, (current, protocol) => current | protocol);

return protocols;
}
}
}
14 changes: 14 additions & 0 deletions Bunkum.sln
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Database", "Database", "{ED
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataStore", "DataStore", "{D09D7F3C-B198-40D8-B7E0-CC2CFFC75CDD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bunkum.Protocols.Https", "Bunkum.Protocols.Https\Bunkum.Protocols.Https.csproj", "{FEDB1C3F-5549-4B44-94CE-54FFE590A35A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bunkum.Protocols.TlsSupport", "Bunkum.Protocols.TlsSupport\Bunkum.Protocols.TlsSupport.csproj", "{B7B1AAD8-2E00-439E-A587-7434E51C1E9E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -174,6 +178,14 @@ Global
{23BC7994-0E93-4FB3-8D31-16EC3BE1198B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23BC7994-0E93-4FB3-8D31-16EC3BE1198B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23BC7994-0E93-4FB3-8D31-16EC3BE1198B}.Release|Any CPU.Build.0 = Release|Any CPU
{FEDB1C3F-5549-4B44-94CE-54FFE590A35A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FEDB1C3F-5549-4B44-94CE-54FFE590A35A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FEDB1C3F-5549-4B44-94CE-54FFE590A35A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FEDB1C3F-5549-4B44-94CE-54FFE590A35A}.Release|Any CPU.Build.0 = Release|Any CPU
{B7B1AAD8-2E00-439E-A587-7434E51C1E9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7B1AAD8-2E00-439E-A587-7434E51C1E9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7B1AAD8-2E00-439E-A587-7434E51C1E9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7B1AAD8-2E00-439E-A587-7434E51C1E9E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{B6B9F1DD-7E85-402E-88A7-EF6B88E6CE9A} = {77922B8B-F6FE-47D6-8742-6BA528BC8D7C}
Expand All @@ -200,5 +212,7 @@ Global
{DEE9CFC0-9529-451F-9633-AF5B49498AFB} = {EDBF1652-710C-4CF9-8890-AF3B46FDA8E1}
{D09D7F3C-B198-40D8-B7E0-CC2CFFC75CDD} = {77922B8B-F6FE-47D6-8742-6BA528BC8D7C}
{993BD323-2E2C-4E79-9252-F92B8CEB834D} = {D09D7F3C-B198-40D8-B7E0-CC2CFFC75CDD}
{FEDB1C3F-5549-4B44-94CE-54FFE590A35A} = {BBAA9024-6FEC-4C68-9F96-83D2055EC6D2}
{B7B1AAD8-2E00-439E-A587-7434E51C1E9E} = {BBAA9024-6FEC-4C68-9F96-83D2055EC6D2}
EndGlobalSection
EndGlobal

0 comments on commit fa6b594

Please sign in to comment.