Yarp.ReverseProxy

toreaux

Alessio Meroni

Posted on October 11, 2024

Yarp.ReverseProxy

Hi,

I anticipate the network structure. There is a virtual network, where there is a cloud server on which an application with YARP Reverse Proxy is installed. This is in VPN with x gateway on which a local application will run, not on the network. These local applications can be reached by external clients following access to the application on the cloud server.

I have to modify the application on the cloud server so that the user, specifying IP and Port, can connect to the final gateway. The user must be able to navigate the final application. In the reverse proxy you need to manage this dynamic, that is, that I don't have to specify and modify all the various options.

Currently, by entering IP and Port, the reverse proxy shows me the address with part of the path. For example, if I go to contuoso.com/Home/Proxy following a previous form, I am redirected to http://{Ip}:{Port}/Home/Proxy.

It must be ensured that by entering the final address the user can then navigate independently. For example, open the login on the application locally: http://10.10.0.2/Users/Login.

Below I leave the current code that should be changed:

HomeController.cs

    [HttpGet]
    public IActionResult Status()
    {
        return View();
    }

    [HttpPost]
    public IActionResult SetProxy(string ip, string port)
    {
        if (string.IsNullOrEmpty(ip) || string.IsNullOrEmpty(port))
        {
            ViewBag.Error = "IP e Porta sono obbligatori.";
            return View();
        }

        string targetUrl = $"http://{ip}:{port}";
        HttpContext.Session.SetString("TargetUrl", targetUrl);

        // Log the target URL
        _logger.LogInformation($"Target URL set to: {targetUrl}");

        // Aggiorna la configurazione del proxy
        var routes = new List<RouteConfig>
        {
            new RouteConfig
            {
                RouteId = "proxyRoute",
                ClusterId = "backendCluster",
                Match = new RouteMatch
                {
                    // Inoltra tutte le richieste senza rimuovere o modificare il percorso
                    Path = "/{**catch-all}"
                }
            }
        };

        var clusters = new List<ClusterConfig>
        {
            new ClusterConfig
            {
                ClusterId = "backendCluster",
                Destinations = new Dictionary<string, DestinationConfig>
                {
                    { "destination1", new DestinationConfig { Address = targetUrl } }
                }
            }
        };

        _customProxyConfigProvider.UpdateConfig(routes, clusters);

        return RedirectToAction("Proxy");
    }

    public async Task Proxy()
    {
        var targetUrl = HttpContext.Session.GetString("TargetUrl");
        if (string.IsNullOrEmpty(targetUrl))
        {
            Response.StatusCode = 400;
            await Response.WriteAsync("Target URL non configurato.");
            return;
        }

        // Log the target URL before forwarding
        _logger.LogInformation($"Forwarding request to: {targetUrl}");

        var httpContext = HttpContext;
        var forwarderRequestConfig = new ForwarderRequestConfig();

        var error = await _httpForwarder.SendAsync(httpContext, targetUrl, _httpClient, forwarderRequestConfig);

        if (error != ForwarderError.None)
        {
            var errorFeature = httpContext.GetForwarderErrorFeature();
            var exception = errorFeature?.Exception;
            // Gestisci l'errore di inoltro
            _logger.LogError($"Errore di inoltro: {error}, eccezione: {exception?.Message}");
            Response.StatusCode = 500;
            await Response.WriteAsync($"Errore di inoltro: {error}, eccezione: {exception?.Message}");
        }
    }
Enter fullscreen mode Exit fullscreen mode

Program.cs

builder.Services.AddSingleton<CustomProxyConfigProvider>();
builder.Services.AddSingleton<IProxyConfigProvider>(sp => sp.GetRequiredService<CustomProxyConfigProvider>());

builder.Services.AddReverseProxy();
builder.Services.AddSession();
builder.Services.AddHttpForwarder();

builder.Services.AddSingleton(new HttpMessageInvoker(new SocketsHttpHandler()));

...

app.MapControllers();
app.MapReverseProxy();`
Enter fullscreen mode Exit fullscreen mode

CustomProxyConfigProvider

public class CustomProxyConfigProvider : IProxyConfigProvider
{
    private volatile CustomProxyConfig _config;
    private readonly CancellationTokenSource _cts = new CancellationTokenSource();

    public CustomProxyConfigProvider()
    {
        _config = new CustomProxyConfig(new List<RouteConfig>(), new List<ClusterConfig>());
    }

    public IProxyConfig GetConfig() => _config;

    public void UpdateConfig(List<RouteConfig> routes, List<ClusterConfig> clusters)
    {
        _config = new CustomProxyConfig(routes, clusters);
        _cts.Cancel();
    }

    private class CustomProxyConfig : IProxyConfig
    {
        public CustomProxyConfig(IReadOnlyList<RouteConfig> routes, IReadOnlyList<ClusterConfig> clusters)
        {
            Routes = routes;
            Clusters = clusters;
            ChangeToken = new CancellationChangeToken(new CancellationTokenSource().Token);
        }

        public IReadOnlyList<RouteConfig> Routes { get; }
        public IReadOnlyList<ClusterConfig> Clusters { get; }
        public IChangeToken ChangeToken { get; }
    }
}
Enter fullscreen mode Exit fullscreen mode

Any suggestions?

💖 💪 🙅 🚩
toreaux
Alessio Meroni

Posted on October 11, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Yarp.ReverseProxy
dotnetcore Yarp.ReverseProxy

October 11, 2024