From 8fb5cc0818e6c17d9d63c314bdee7229023dae15 Mon Sep 17 00:00:00 2001 From: Joseph <40335314+JRedOW@users.noreply.github.com> Date: Tue, 15 Oct 2024 18:22:04 +0000 Subject: [PATCH 1/4] Updated Docker Compat. --- Dockerfile | 27 +++++++++++++++++++++++++++ src/Promul.Server~/Dockerfile | 20 -------------------- 2 files changed, 27 insertions(+), 20 deletions(-) create mode 100644 Dockerfile delete mode 100644 src/Promul.Server~/Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8510758 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +FROM mcr.microsoft.com/dotnet/sdk:7.0@sha256:d32bd65cf5843f413e81f5d917057c82da99737cb1637e905a1a4bc2e7ec6c8d AS build-env +WORKDIR /App + +# Copy everything +COPY . ./ +# Restore +RUN dotnet restore src/Promul.Server~/Promul.Relay.Server.csproj +# Build and publish a release +RUN dotnet publish src/Promul.Server~/Promul.Relay.Server.csproj -c Release -o out /p:UseAppHost=false + + +# Build runtime image +FROM mcr.microsoft.com/dotnet/aspnet:7.0@sha256:c7d9ee6cd01afe9aa80642e577c7cec9f5d87f88e5d70bd36fd61072079bc55b +WORKDIR /App + +# Exposed Ports +EXPOSE 80 +EXPOSE 4098 + +# Enviroment Variables +ENV JOIN_CODE_LENGTH=6 +ENV RELAY_PORT=4098 +ENV RELAY_ADDRESS=us628.relays.net.fireworkeyes.com +ENV ENABLE_DESTROY_API=false + +COPY --from=build-env /App/out . +ENTRYPOINT ["dotnet", "Promul.Relay.Server.dll"] \ No newline at end of file diff --git a/src/Promul.Server~/Dockerfile b/src/Promul.Server~/Dockerfile deleted file mode 100644 index 993cecd..0000000 --- a/src/Promul.Server~/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base -WORKDIR /app -EXPOSE 80 -EXPOSE 443 - -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build -WORKDIR /src -COPY ["Promul.Api/Promul.Api.csproj", "Promul.Api/"] -RUN dotnet restore "Promul.Api/Promul.Api.csproj" -COPY . . -WORKDIR "/src/Promul.Api" -RUN dotnet build "Promul.Api.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "Promul.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "Promul.Api.dll"] From 11d2a57c0845ccace41462ce74387bf83dd3e27d Mon Sep 17 00:00:00 2001 From: Joseph <40335314+JRedOW@users.noreply.github.com> Date: Tue, 15 Oct 2024 19:28:35 +0000 Subject: [PATCH 2/4] API Session Destruction and Enviromental Variables --- .../Sessions/Destroy Session.bru | 15 ++++ .../Controllers/SessionController.cs | 86 +++++++++++++++++-- test~/Promul.Server.Tests/IntegrationTests.cs | 25 ++++++ 3 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 src/Promul.Server~/Bruno API Collection/Sessions/Destroy Session.bru diff --git a/src/Promul.Server~/Bruno API Collection/Sessions/Destroy Session.bru b/src/Promul.Server~/Bruno API Collection/Sessions/Destroy Session.bru new file mode 100644 index 0000000..6a6fdb8 --- /dev/null +++ b/src/Promul.Server~/Bruno API Collection/Sessions/Destroy Session.bru @@ -0,0 +1,15 @@ +meta { + name: Destroy Session + type: http + seq: 2 +} + +delete { + url: {{baseUrl}}/session/destroy + body: json + auth: none +} + +body:json { + {"joinCode": "TEST"} +} diff --git a/src/Promul.Server~/Controllers/SessionController.cs b/src/Promul.Server~/Controllers/SessionController.cs index 11567b2..0105c18 100644 --- a/src/Promul.Server~/Controllers/SessionController.cs +++ b/src/Promul.Server~/Controllers/SessionController.cs @@ -2,6 +2,8 @@ using Promul.Relay.Server.Models.Sessions; using Promul.Relay.Server.Relay; +using System.Security.Cryptography; + namespace Promul.Relay.Server.Controllers; [ApiController] @@ -21,15 +23,52 @@ public SessionController(ILogger logger, RelayServer server) public SessionInfo CreateSession() { const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + int joinCodeLength = 6; + + if (System.Environment.GetEnvironmentVariable("JOIN_CODE_LENGTH") != null) + { + if (Int32.TryParse(System.Environment.GetEnvironmentVariable("JOIN_CODE_LENGTH"), out int length)) + { + if (length <= 0) { + _logger.LogInformation("Not using JOIN_CODE_LENGTH enviroment variable value: `{}`, below zero", + length); + } + + joinCodeLength = length; + } + else + { + _logger.LogInformation("Not using JOIN_CODE_LENGTH enviroment variable: `{}`, not an integer", + System.Environment.GetEnvironmentVariable("JOIN_CODE_LENGTH")); + } + } + + var joinCode = new string(Enumerable.Repeat(chars, joinCodeLength).Select(s => s[RandomNumberGenerator.GetInt32(s.Length)]).ToArray()); + //var joinCode = "TEST"; + + // Verify Join Code is unique + // Not the best implementation, but would only cause real theoretical problems with around 26^6/6 (51 million) lobbies + // If that's somehow an issue, just expand join code length using env var JOIN_CODE_LENGTH + while (_relay.GetSession(joinCode) != null) + { + joinCode = new string(Enumerable.Repeat(chars, joinCodeLength).Select(s => s[RandomNumberGenerator.GetInt32(s.Length)]).ToArray()); + } - //var joinCode = new string(Enumerable.Repeat(chars, 6).Select(s => s[RandomNumberGenerator.GetInt32(s.Length)]).ToArray()); - var joinCode = "TEST"; _relay.CreateSession(joinCode); + + int relayPort = 15593; + if ( + System.Environment.GetEnvironmentVariable("RELAY_PORT") != null && + Int32.TryParse(System.Environment.GetEnvironmentVariable("RELAY_PORT"), out int port) + ) + { + relayPort = port; + } var sci = new SessionInfo { JoinCode = joinCode, - RelayAddress = "aus628.relays.net.fireworkeyes.com", - RelayPort = 15593 + RelayAddress = System.Environment.GetEnvironmentVariable("RELAY_ADDRESS") ?? "aus628.relays.net.fireworkeyes.com", + RelayPort = relayPort }; _logger.LogInformation("User {}:{} created session with join code {}", @@ -45,14 +84,49 @@ public ActionResult JoinSession([FromBody] SessionRequestJoinInfo j { var session = _relay.GetSession(joinCode.JoinCode); if (session == null) return NotFound(); + + int relayPort = 15593; + if ( + System.Environment.GetEnvironmentVariable("RELAY_PORT") != null && + Int32.TryParse(System.Environment.GetEnvironmentVariable("RELAY_PORT"), out int port) + ) + { + relayPort = port; + } return new SessionInfo { JoinCode = session.JoinCode, - RelayAddress = "aus628.relays.net.fireworkeyes.com", - RelayPort = 15593 + RelayAddress = System.Environment.GetEnvironmentVariable("RELAY_ADDRESS") ?? "aus628.relays.net.fireworkeyes.com", + RelayPort = relayPort }; } + [HttpDelete("Destroy")] + public ActionResult DestroySession([FromBody] SessionRequestJoinInfo joinCode) + { + // For backwards compatability and safety if API public + if (System.Environment.GetEnvironmentVariable("ENABLE_DESTROY_API") != null && !( + System.Environment.GetEnvironmentVariable("ENABLE_DESTROY_API") == "true" || + System.Environment.GetEnvironmentVariable("ENABLE_DESTROY_API") == "t" || + System.Environment.GetEnvironmentVariable("ENABLE_DESTROY_API") == "1" ) + ) + { + return NotFound(); + } + + var session = _relay.GetSession(joinCode.JoinCode); + if (session == null) return NotFound(); + + _logger.LogInformation("User {}:{} destroyed session with join code {}", + HttpContext.Connection.RemoteIpAddress, + HttpContext.Connection.RemotePort, + joinCode.JoinCode); + + _relay.DestroySession(session); + + return Ok(); + } + public struct SessionRequestJoinInfo { public string JoinCode { get; set; } diff --git a/test~/Promul.Server.Tests/IntegrationTests.cs b/test~/Promul.Server.Tests/IntegrationTests.cs index d962cd1..f050082 100644 --- a/test~/Promul.Server.Tests/IntegrationTests.cs +++ b/test~/Promul.Server.Tests/IntegrationTests.cs @@ -69,4 +69,29 @@ public async Task Test_That_Client_Cannot_Join_Nonexistent_Session() var invalidJoin = await client.PutAsync("/session/join", JsonContent.Create(new { joinCode = "invalid" })); Assert.That(invalidJoin.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); } + + [Test] + public async Task Test_Destroy_Created_Session() + { + var client = _factory.CreateClient(); + var rs = _factory.Services.GetRequiredService(); + Assert.That(rs.GetAllSessions(), Is.Empty); + var response = await client.PutAsync("/session/create", null); + response.EnsureSuccessStatusCode(); + var r = await response.Content.ReadFromJsonAsync(); + Assert.That(r.JoinCode, Is.Not.Empty); + Assert.That(rs.GetAllSessions(), Has.Count.EqualTo(1)); + + // Override Destroy Enviroment Variable + System.Environment.SetEnvironmentVariable("ENABLE_DESTROY_API", "true"); + + var request = new HttpRequestMessage { + Method = HttpMethod.Delete, + RequestUri = new Uri("/session/destroy"), + Content = JsonContent.Create(new { joinCode = r.JoinCode }) + }; + var removeResponse = await client.SendAsync(request); + removeResponse.EnsureSuccessStatusCode(); + Assert.That(rs.GetAllSessions(), Has.Count.EqualTo(0)); + } } \ No newline at end of file From 51c1e42dd392228da729ad68042e6993d1d7ea5d Mon Sep 17 00:00:00 2001 From: Joseph <40335314+JRedOW@users.noreply.github.com> Date: Wed, 16 Oct 2024 13:31:52 +0000 Subject: [PATCH 3/4] Update Readme Guide --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 47b5f6a..f0ecc7d 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Promul is intended to be a free and open-source alternative to [Unity Relay](htt ## Setup #### Relay server and API -Clone this repository and build and run the program under [/Server](/Server). The relay server will bind on UDP port 4098 while the API server will bind on TCP port 3000. +Clone this repository and build and run the program under [/src/Promul.Server~](/src/Promul.Server~) or build and run the provided docker image. The relay server will bind on UDP port 4098 while the API server will bind on TCP port 3000. To create a new session, call `PUT /session/create`. The API will respond with the join code. From 766431ba921688814a73e53835bd892a1faee06b Mon Sep 17 00:00:00 2001 From: Joseph <40335314+JRedOW@users.noreply.github.com> Date: Wed, 16 Oct 2024 13:37:39 +0000 Subject: [PATCH 4/4] Docker Publish Action --- .github/workflows/publish-package.yml | 54 +++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 .github/workflows/publish-package.yml diff --git a/.github/workflows/publish-package.yml b/.github/workflows/publish-package.yml new file mode 100644 index 0000000..cac9c57 --- /dev/null +++ b/.github/workflows/publish-package.yml @@ -0,0 +1,54 @@ +name: Publish the Docker image + +on: + push: + tags: + - 'v*' + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + attestations: write + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image + id: push + uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + - name: Generate artifact attestation + uses: actions/attest-build-provenance@v1 + with: + subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}} + subject-digest: ${{ steps.push.outputs.digest }} + push-to-registry: true +