Web Server Certificate From a File

If one desires to run HTTPs server from C#, they might get the following warning:

Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found or is out of date. To generate a developer certificate run 'dotnet dev-certs https'. To trust the certificate (Windows and macOS only) run 'dotnet dev-certs https --trust'. For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054.

And yes, once could follow instructions and have everything running. But where’s the fun in that?

Alternative approach would be to load certificate from the file and .NET makes that really easy.

private static X509Certificate2? GetCertificate() {
  var certFilename = Path.Combine(AppContext.BaseDirectory, "my.pfx");
  if (File.Exists(certFilename)) {
    try {
      return new X509Certificate2(certFilename);
    } catch (CryptographicException ex) {
      // log error or whatever
    }
  }
  return null;
}

So, when bringing server up we can just call it using something like this:

var cert = GetCertificate();
options.Listen(IPAddress.Any, 443, listenOptions => {
  listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
  if (cert != null) {
    listenOptions.UseHttps(cert);
  } else {
    listenOptions.UseHttps();
  }
});

Zip in Git Bash

While creating build system that works across the platforms, one can find issues in the most basic things. And that’s even when shell is the same. For example, while Bash on Linux and Windows works out the same, a lot of supporting tools differ - a lot. And there’s no better example than creating a zip archive.

Under Linux you can count on zip command being available. Even if one doesn’t have it, it’s easy to install without messing with their desktop. On Windows story gets more complicated. Git Bash for example doesn’t have it even compiled and there’s no really good way to add it. Yes, you can use any application but different one is installed on every system. To create more “fun”, supporting multiple applications also means dealing with their command-line arguments. And yes, 7-Zip has completely different syntax as compared to WinRAR.

However, when it comes to making zip archive, there’s actually a solution that works for both Windows (via Git Bash) and Linux. Surprisingly, the answer is perl.

If one is careful to use Perl’s older IO::Compress::Zip library, creating an archive becomes a simple task:

perl -e '
  use strict;
  use warnings;
  use autodie;
  use IO::Compress::Zip qw(:all);
  zip [
    "src/mimetype",
    <"src/META-INF/*.*">,
    <"src/OEBPS/*.*">,
    <"src/OEBPS/chapters/*.*">
  ] => "bin/book.epub",
       FilterName => sub { s[^src/][] },
       Zip64 => 0,
  or die "Zip failed: $ZipError\n";
'

Yeah, might not be ideal when it comes to beauty but it definitely works across platforms.

Add Application to Auto-Start from PowerShell

Since PowerShell comes installed with every Windows, I find it ideal tool for initial system setup when nothing else is installed. And one of the tasks I need it for is setting up auto-start.

My standard approach for setting application auto-start used to be to just use regedit.exe and load prepared registry file. But, as I was rewriting my setup scripts, I figured it’s time to update this too.

And it’s easy enough:

New-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" `
    -Name "^^Application^^" `
    -Value "^^C:\Path\To\MyApplication.exe^^"

Network Share Login via C#

Accessing remote share programmatically from Windows is easy. Just use \\machine\share notation and you’re golden. Except if you need to access it with a specific user. While PrincipalContext can often help, for samba shares hosted by Linux, that doesn’t necessarily work.

bool Login(string path, string user, string password) {
  var nr = new NativeMethods.NETRESOURCE {
    dwType = NativeMethods.RESOURCETYPE_DISK,
    lpRemoteName = path
  };
  var result = NativeMethods.WNetUseConnection(IntPtr.Zero, nr, password, name, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
  return (result == NativeMethods.NO_ERROR);
}

And, of course, we also need our definitions:

private static class NativeMethods {
    internal const Int32 NO_ERROR = 0;
    internal const Int32 RESOURCETYPE_DISK = 0x00000001;

    [StructLayout(LayoutKind.Sequential)]
    internal class NETRESOURCE {
        public Int32 dwScope = 0;
        public Int32 dwType = 0;
        public Int32 dwDisplayType = 0;
        public Int32 dwUsage = 0;
        public IntPtr lpLocalName;
        public String lpRemoteName;
        public IntPtr lpComment;
        public IntPtr lpProvider;
    }

    [DllImport("mpr.dll", CharSet = CharSet.Ansi)]
    internal static extern Int32 WNetUseConnection(
        IntPtr hwndOwner,
        NETRESOURCE lpNetResource,
        String lpPassword,
        String lpUserId,
        Int32 dwFlags,
        IntPtr lpAccessName,
        IntPtr lpBufferSize,
        IntPtr lpResult
    );

}

And that should be enough for access.

DNS over HTTPS for Mikrotik

Illustration

With everything moving to HTTPS, there’s still one component that gets overlooked - DNS. Most of time lookups are still done via essentially plain-text protocol. And it’s not for the lack of encrypted alternatives as there are at least three different ways of doing it: DNS over HTTPS (DoH), DNS over TLS (DoT), and DNSCrypt. When it comes to Mikrotik, choice narrows a bit and only DNS over HTTPS is supported.

Realistically, DoH is enough. DoT might be a bit more elegant implementation at a lower OSI layer but both clients and public servers seem to prefer DoH due to it’s heavy reliance on HTTPS layer ubiquitous to pretty much any application these days. DNSCrypt never got off the ground and we pretty much can ignore it.

So, today’s task is to set Mikrotik router to use DNS over HTTPS lookups.

To start it all off, we first need to download CA certificate store. And yes, we could skip this step and simply not check certificates but I feel that leaves too much space for man-in-the-middle attach. Downloading one file every few years will not kill anybody.

/tool fetch url=https://curl.se/ca/cacert.pem
      status: finished
       total: 199KiB
    duration: 1s

/certificate import file-name=cacert.pem passphrase=""
     certificates-imported: 128``
     private-keys-imported: 0
            files-imported: 1
       decryption-failures: 0
  keys-with-no-certificate: 0

With certificates in place, we get to setup DNS over HTTPs.

/ip dns
set use-doh-server=^^https://cloudflare-dns.com/dns-query^^ verify-doh-cert=yes

And that’s it. To verify it working, you can visit https://1.1.1.1/help and look for “Using DNS over HTTPS (DoH)” line.


That said, one could notice one glaring issue - we still have our original DNS servers listed. And I personally do this intentionally as a fallback method. However, that does leak a bit of information and, if someone blocks our DNS resolver, can force plain-text legacy lookups.

To harden system a bit, first we need to add static entries for DNS IPs (you can find them out with nslookup cloudflare-dns.com).

/ip dns static
add name=cloudflare-dns.com address=^^2606:4700::6810:f8f9^^
add name=cloudflare-dns.com address=^^2606:4700::6810:f9f9^^
add name=cloudflare-dns.com address=^^104.16.248.249^^
add name=cloudflare-dns.com address=^^104.16.249.249^^

With static entries in place, we can clear the legacy DNS servers.

/ip dns
set servers=""

And, if we’re using DHCP, we need to disregard DNS IPs provided by upstream too.

/ip dhcp-client
set 0 use-peer-dns=no

/ipv6 dhcp-client
set 0 use-peer-dns=no

And now all DNS lookups done by router are done using DoH and nothing else.