Reverse Proxy Manager on Raspberry Pi

I was looking for some time to easily manage the binding of multiple web application hosted in my docker host machine. I have found a good candidate in Nginx Proxy Manager from Jamie Curnow https://jc21.com

To best fit the installation, I rearranged the default setup for a fastest deploy in my environment based on PhotonOS and Portainer for Raspberry PI.

Base configuration

Open an ssh console and create a new folder where you store a configuration file. Note that “npm” means “Nginx Proxy Manager”.

mkdir /var/npm
nano /var/npm/config.json

Than paste the configuration parameters as follow.

{
  "database": {
    "engine": "mysql",
    "host": "db",
    "name": "npm",
    "user": "npm",
    "password": "npm",
    "port": 3306
  }
}

Enter into the Portainer UI, go to the Stacks menu and add a new stack.

Service stack creation

Choose a name for example “npm”, paste on the Web editor the script and deploy the stack.

version: "2"
services:
  app:
    image: jc21/nginx-proxy-manager:latest
    restart: always
    ports:
      # Public HTTP Port:
      - "80:80"
      # Public HTTPS Port:
      - "443:443"
      # Admin Web Port:
      - "81:81"
    volumes:
      # Make sure this config.json file exists as per instructions above:
      - /var/npm/config.json:/app/config/production.json
      - npm-data:/data
      - npm-letsencrypt:/etc/letsencrypt
    depends_on:
      - db
  db:
    image: mariadb:latest
    restart: always    
    environment:
      MYSQL_ROOT_PASSWORD: "npm"
      MYSQL_DATABASE: "npm"
      MYSQL_USER: "npm"
      MYSQL_PASSWORD: "npm"
    volumes:
      - npm-mysql:/var/lib/mysql

If all goes to the right way you should see your service up and healthy

Now you could proceed with the proxy configuration at the link http://<host-address>:81

Default credential

Email:    admin@example.com
Password: changeme

Complete your customization and good work!

Dotnet core 3.0 NativeLibrary

Since the first dotnet core cross platform release, it was a dream that became reality for me to merge the good of multiple worlds.

Many times I need to host a little service to do simple data transfer, and what I prefer is to put it in a small linux dockerized server.

Recently one of my old project SnwConnector emerged from the dust to my attention.

SnwConnector

SnwConnector was a dotnet wrapper of SAP NetWeaver RFC SDK build on top of the old dotnet framework and the Windows x64 SAP native library.

I created the project long time ago, the project was open source and hosted on CodePlex!!!

What I want to do now is to port my library to dotnet core, but keep the ability to be cross-platform, so I could build and test the project on my Windows machine, but keep the opportunity to deploy on a Linux or OSX machine.

NativeLibrary

So it was a good news for me the introduction of NativeLibrary class with dotnet core 3.0.

First I created a Native folder that contains the subfolders with the native library for each platform that I could get from the SAP support center.

After that I add the UnsafeNativeMethods.cs class so by RuntimeInformation you get the type of OSPlatfotm and ProcessArchitecture due the fact that you can force 32bit execution on 64bit architecture.

By these information I can load the right Native library thanks the NativeLibrary.Load(dllPath)

internal static class UnsafeNativeMethods
{
    const string SapNwRfc = "sapnwrfc";

    internal class PlatformDlls
    {
        public string Path { get; set; }
        public string[] DllNames { get; set; }
    }

    static UnsafeNativeMethods()
    {
        PlatformDlls libraryInfo = GetNativeLibraryInfo();
        var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        foreach (var dll in libraryInfo.DllNames)
        {
            var dllPath = Path.Combine(path, "Native", libraryInfo.Path, dll);
            NativeLibrary.Load(dllPath);
        }
    }

    private static PlatformDlls GetNativeLibraryInfo()
    {
        PlatformDlls platformDlls = new PlatformDlls();
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
            && (RuntimeInformation.ProcessArchitecture == Architecture.X64))
        {
            platformDlls.Path = "Win64";
            platformDlls.DllNames = new[] {
                "icudt50.dll",
                "icuin50.dll",
                "icuuc50.dll",
                "libicudecnumber.dll",
                "libsapucum.dll",
                "sapnwrfc.dll"
            };
        }
        else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
            && (RuntimeInformation.ProcessArchitecture == Architecture.X86))
        {
            platformDlls.Path = "Win32";
            platformDlls.DllNames = new[] {
                "icudt50.dll",
                "icuin50.dll",
                "icuuc50.dll",
                "libicudecnumber.dll",
                "libsapucum.dll",
                "sapnwrfc.dll"
            };
        }
        else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
            && (RuntimeInformation.ProcessArchitecture == Architecture.X64))
        {
            platformDlls.Path = "Linux64";
            platformDlls.DllNames = new[]
            {
                "libicudata.so.50",
                "libicudecnumber.so",
                "libicui18n.so.50",
                "libicuuc.so.50",
                "libsapnwrfc.so",
                "libsapucum.so"
            };
        }
        else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
        {
            platformDlls.Path = "OSX";
            platformDlls.DllNames = new[] {
                "libicudata.50.dylib",
                "libicudecnumber.dylib",
                "libicui18n.50.dylib",
                "libicuuc.50.dylib",
                "libsapnwrfc.dylib",
                "libsapucum.dylib"
            };
        }
        else
        {
            throw new Exception("Unsupported OSPlatform, can't locate sapnwrfc library.");
        }

        return platformDlls;
    }

    //
    // Info API
    //
    [DllImport(SapNwRfc, EntryPoint = "RfcInit")]
    public static extern RfcRc RfcInit();

    [DllImport(SapNwRfc, EntryPoint = "RfcGetVersion")]
    public static extern IntPtr RfcGetVersion(out int majorVersion, out int minorVersion, out int patchLevel);

    //
    // Connection API 
    //
    [DllImport(SapNwRfc, EntryPoint = "RfcOpenConnection")]
    public static extern IntPtr RfcOpenConnection(RfcConnectionParameter[] connectionParams, int paramCount, out RfcErrorInfo errorInfo);

    [DllImport(SapNwRfc, EntryPoint = "RfcCloseConnection")]
    public static extern RfcRc RfcCloseConnection(IntPtr rfcHandle, out RfcErrorInfo errorInfo);

    [DllImport(SapNwRfc, EntryPoint = "RfcPing")]
    public static extern RfcRc RfcPing(IntPtr rfcHandle, out RfcErrorInfo errorInfo);

    [DllImport(SapNwRfc, EntryPoint = "RfcGetConnectionAttributes")]
    public static extern RfcRc RfcGetConnectionAttributes(IntPtr rfcHandle, out RfcAttributes attributes, out RfcErrorInfo errorInfo);

    [DllImport(SapNwRfc, EntryPoint = "RfcSetIniPath", CharSet = CharSet.Unicode)]
    public static extern RfcRc RfcSetIniPath(string pathName, out RfcErrorInfo errorInfo);
}

If you noticed I defined a const SapNwRfc to refer the SAP NetWeaver library used by the attribute DllImport.

You should know that DllImport will search automatically by adding suffixes “.dll” or “.so” or “.dylib” and prefix “lib”.

Tested with the same compiled project by Windows and Linux and it runs!

How to setup an asp.net core 3.0 library project

As every software programmer I want to reuse my code in many projects.
This time a faced the problem to create a API controller on an external library and reference it on my asp.net core project.

There is not a particular template so I used the normal classlib by typing

dotnet new classlib

After that explore your csproj file and it should look like

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
</Project>

At this point change the target framework with netcoreapp3.0 and add a new MyApiController.cs class like the following

using Microsoft.AspNetCore.Mvc;

namespace MyAspnetCoreLibrary
{
    [Route("api/[Controller]")]
    [ApiController]
    public class MyApiController : ControllerBase
    {

    }
}

Obviously a lot of asp.net core library are missing if you try to compile, so go back to your csproj and add a FrameworkReference tag

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" Version="3.0.0" />
  </ItemGroup>

</Project>

Now you should compile without errors

That’s all!