Skip to content

Commit

Permalink
Merge pull request #73 from natashell666/Issue-#72
Browse files Browse the repository at this point in the history
Kestrel Server Zitified
  • Loading branch information
dovholuknf authored Apr 23, 2024
2 parents 7071f92 + 9484565 commit ba8d4a2
Show file tree
Hide file tree
Showing 11 changed files with 412 additions and 0 deletions.
126 changes: 126 additions & 0 deletions OpenZiti.NET.Samples.Kestrel/Controllers/MetricItemsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ZitiRestServerCSharp.Models;

namespace ZitiRestServerCSharp.Controllers
{
[Route("api/MetricItemsController")]
[ApiController]
public class MetricItemsController : ControllerBase
{
private readonly MetricContext _context;

public MetricItemsController(MetricContext context)
{
_context = context;
}

// GET: api/MetricItemsController
[HttpGet]
public async Task<ActionResult<IEnumerable<MetricItem>>> GetMetricItems()
{
if (_context.MetricItems == null)
{
return NotFound();
}
return await _context.MetricItems.ToListAsync();
}

// GET: api/MetricItemsController/5
[HttpGet("{id}")]
public async Task<ActionResult<MetricItem>> GetMetricItem(long id)
{
Console.WriteLine("Starting Metrics -> Get -> id: " + id);
if (_context.MetricItems == null)
{
return NotFound();
}
var metricItem = await _context.MetricItems.FindAsync(id);

if (metricItem == null)
{
return NotFound();
}
Console.WriteLine("Ending Metrics -> Get -> id: " + id);
return metricItem;
}

// PUT: api/MetricItemsController/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPut("{id}")]
public async Task<IActionResult> PutMetricItem(long id, MetricItem metricItem)
{
if (id != metricItem.Id)
{
return BadRequest();
}

_context.Entry(metricItem).State = EntityState.Modified;

try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MetricItemExists(id))
{
return NotFound();
}
else
{
throw;
}
}

return NoContent();
}

// POST: api/MetricItemsController
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPost]
public async Task<ActionResult<MetricItem>> PostMetricItem(MetricItem metricItem)
{
Console.WriteLine("Starting Add MetricItem with values" + metricItem);
if (_context.MetricItems == null)
{
return Problem("Entity set 'MetricContext.MetricItems' is null.");
}
_context.MetricItems.Add(metricItem);
await _context.SaveChangesAsync();

Console.WriteLine("Creating values" + metricItem);
return CreatedAtAction(nameof(GetMetricItem), new { id = metricItem.Id }, metricItem);
}

// DELETE: api/MetricItemsController/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteMetricItem(long id)
{
if (_context.MetricItems == null)
{
return NotFound();
}
var metricItem = await _context.MetricItems.FindAsync(id);
if (metricItem == null)
{
return NotFound();
}

_context.MetricItems.Remove(metricItem);
await _context.SaveChangesAsync();

return NoContent();
}

private bool MetricItemExists(long id)
{
return (_context.MetricItems?.Any(e => e.Id == id)).GetValueOrDefault();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Microsoft.AspNetCore.Mvc;

namespace ZitiRestServerCSharp.Controllers;

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

private readonly ILogger<WeatherForecastController> _logger;

public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}

[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using System.Buffers;
using System.Buffers.Binary;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Net;
using System.Net.Sockets;
using System.Threading.Channels;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;
using OpenZiti;

internal class ZitiConnectionListenerFactory : IConnectionListenerFactory
{
private ILogger<ZitiConnectionListenerFactory> _logger;
private ZitiSocket _zitiSocket;

public ZitiConnectionListenerFactory(ILogger<ZitiConnectionListenerFactory> logger)
{
_logger = logger;
}

public async ValueTask<IConnectionListener> BindAsync(EndPoint endpoint, CancellationToken cancellationToken = default)
{
#region OptionZiti
API.SetLogLevel(ZitiLogLevel.INFO);
_zitiSocket = new ZitiSocket(SocketType.Stream);
ZitiContext ctx = new ZitiContext("C:\\OpenZiti\\CSharp-RestApi-Server.json");
var svcName = "CSharp-Service";
string terminator = "";

API.Bind(_zitiSocket, ctx, svcName, terminator);
Console.WriteLine("Bound to Ziti");
API.Listen(_zitiSocket, 100);
Console.WriteLine("Listening on Ziti");
#endregion

return new SocketListener(_zitiSocket, _logger);
}

class SocketListener : IConnectionListener
{
private ZitiSocket _zitiSocket;
private Socket _socket;
private readonly Channel<ConnectionContext> _channel = Channel.CreateBounded<ConnectionContext>(20);
private readonly SocketConnectionContextFactory _contextFactory;

public SocketListener(ZitiSocket zitiSocket, ILogger logger)
{
_zitiSocket = zitiSocket;
_socket = zitiSocket.ToSocket();
_contextFactory = new(new(), logger);
}

public EndPoint EndPoint => _socket.LocalEndPoint!;

public async ValueTask DisposeAsync()
{
}

public ValueTask UnbindAsync(CancellationToken cancellationToken = default)
{
return default;
}

public async ValueTask<ConnectionContext?> AcceptAsync(CancellationToken cancellationToken = default)
{
try
{
while (true)
{
ZitiSocket client = API.Accept(_zitiSocket, out var caller);
Console.WriteLine("Accepted connection from an Authorized Ziti Client");
var socket = client.ToSocket();
if (socket.RemoteEndPoint is IPEndPoint remoteEndPoint)
{
string clientIpAddress = remoteEndPoint.Address.ToString();
int remotePort = remoteEndPoint.Port;
Console.WriteLine($"Connection stablished with Authorized Ziti Client IP Address: {clientIpAddress}, Port: {remotePort}");
}
if (socket.LocalEndPoint is IPEndPoint localEndPoint)
{
string localIpAddress = localEndPoint.Address.ToString();
int localPort = localEndPoint.Port;
Console.WriteLine($"Connection stablished at IP Address: {localIpAddress}, Port: {localPort}");
}
return _contextFactory.Create(socket);
}
}
catch (ObjectDisposedException)
{
}
catch (SocketException)
{
}
return null;
}
}
}
13 changes: 13 additions & 0 deletions OpenZiti.NET.Samples.Kestrel/Models/MetricContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.EntityFrameworkCore;

namespace ZitiRestServerCSharp.Models;

public class MetricContext : DbContext
{
public MetricContext(DbContextOptions<MetricContext> options)
: base(options)
{
}

public DbSet<MetricItem> MetricItems { get; set; } = null!;
}
8 changes: 8 additions & 0 deletions OpenZiti.NET.Samples.Kestrel/Models/MetricItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace ZitiRestServerCSharp.Models;
public class MetricItem
{
public long Id { get; set; }
public string? SensorGuid { get; set; }
public string? Name { get; set; }
public int value { get; set; }
}
27 changes: 27 additions & 0 deletions OpenZiti.NET.Samples.Kestrel/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using OpenZiti;
using System.Net.Sockets;
using Microsoft.EntityFrameworkCore;
using ZitiRestServerCSharp.Models;

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.UseDelegatedTransport();

builder.Services.AddControllers();
builder.Services.AddDbContext<MetricContext>(opt =>
opt.UseInMemoryDatabase("Metrics"));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

app.UseSwagger();
app.UseSwaggerUI();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();
33 changes: 33 additions & 0 deletions OpenZiti.NET.Samples.Kestrel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
![Ziggy using the sdk-csharp](https://raw.githubusercontent.com/openziti/branding/main/images/banners/CSharp.jpg)

# Zitified Kestrel Sample

This sample demonstrates how to use the OpenZiti SDK to secure a Kestrel server.
In this case, the server is a simple REST API listening in the Ziti Overlay, that returns some metric data. This data looks like this:
```
{
"sensorguid": "abcd1234",
"name": "temp",
"value": "6"
}
```

## OpenZiti Concepts Demonstrated

This sample demonstrates some key OpenZiti concepts:
* Application-embedded zero trust server.
* Availability to natively integrate with the DotNet Core ecosystem.
* Offloading traffic from an identity.

## Running the Sample

To run the sample, you should be able to just run it directly and it will bootstrap the overlay network. The program expects to have your identity saved into the `C:\OpenZiti\CSharp-RestApi-Server.json` file. This location can be changed in the file `DelegatedZitiConnectionListenerFactory.cs`. You can run the code as:
```
dotnet run --project OpenZiti.NET.Samples.Kestrel/ZitiRestServerCSharp.csproj
```

## Code Walkthrough

There're a few key components in this sample:
* `DelegatedZitiConnectionListenerFactory.cs` - This is the main entry point of the application. It sets up the Kestrel server and the Ziti SDK using the identity provided.
* `ServiceCollectionExtension.cs` - This file contains the extension method to overide the default `IConnectionListenerFactory` with the `DelegatedZitiConnectionListenerFactory`.
17 changes: 17 additions & 0 deletions OpenZiti.NET.Samples.Kestrel/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Net;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Server.Kestrel.Core;

namespace Microsoft.AspNetCore.Hosting
{
public static class ServiceCollectionExtensions
{
public static IWebHostBuilder UseDelegatedTransport(this IWebHostBuilder hostBuilder)
{
return hostBuilder.ConfigureServices(services =>
{
services.AddSingleton<IConnectionListenerFactory, ZitiConnectionListenerFactory>();
});
}
}
}
12 changes: 12 additions & 0 deletions OpenZiti.NET.Samples.Kestrel/WeatherForecast.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace ZitiRestServerCSharp;

public class WeatherForecast
{
public DateOnly Date { get; set; }

public int TemperatureC { get; set; }

public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);

public string? Summary { get; set; }
}
Loading

0 comments on commit ba8d4a2

Please sign in to comment.