Build & Deploy ASP.NET MVC Core web server to a Raspberry Pi

Tags: raspberry-pilinux

Listen for external connections

Want want to listen on port 80 for HTTP and port 443 for HTTPS, but only root can listen on these ports, so we’re going to listen on 8800 and 44433 and redirect traffic to 80 and 443 to these ports so we don’t have to run our app as root.

Enable Kestrel

In your CreateHostBuilder method:

webBuilder.UseKestrel(options =>
{
    options.Listen(IPAddress.Any, 8800);
    options.Listen(IPAddress.Any, 44433);
});

Forward ports

sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8800
sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-ports 44433

Side note - list which ports you’ve forwarded

sudo iptables -t nat -S | grep REDIRECT

Override forwarding to 44433

If you configure .NET to use port 44433 for HTTPS then it will try to redirect HTTP traffic to this port. But you want it to go to 443 still. In your ConfigureServices method:

services.AddHttpsRedirection(options =>
{
    options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect;
    options.HttpsPort = 443;
});

Build & copy to Raspberry Pi

dotnet publish [project name] --configuration Release --runtime linux-arm
cd [project name]\bin\Release\[dotnet core version]\linux-arm\publish
scp -r * pi@[pi ip address]:/var/www/[project name]
cd ..\..\..\..\..\..

Run in Raspberry Pi

cd /var/www/[project name]
dotnet [project name].dll

Create certificates

Install certbot & generate certificates (source)

Note this will listen on port 80 as a verification step so won’t work if the server is currently running and/or the port forwarding is enabled. A reboot will fix both.

sudo apt-get install certbot
sudo certbot certonly --standalone

Combine certificate+key into pfx

sudo openssl pkcs12 -export -out certificate.pfx -inkey /etc/letsencrypt/live/[your domain]/privkey.pem -in /etc/letsencrypt/live/[your domain]/fullchain.pem
sudo chmod 755 certificate.pfx

Renew certificate

sudo certbot renew

Then repeat step above.

Troubleshooting

Delete port forwarding

sudo iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8800
sudo iptables -t nat -D PREROUTING -p tcp --dport 443 -j REDIRECT --to-ports 44433

Check for processes running on port 80

sudo netstat -lnp | grep :80

Update Kestrel config to use certificate

webBuilder.UseKestrel(options =>
{
    options.Listen(IPAddress.Any, 8800);
    options.Listen(IPAddress.Any, 44433, listenOptions =>
    {
        listenOptions.UseHttps("certificate.pfx", "password chosen in previous step");
    });
});

Use systemd to automatically start up on reboot

Create service file

sudo vim /etc/systemd/system/mysite.service

Enter contents:

Description=My Website
After=network.target

[Service]
ExecStartPre=/sbin/iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8800 ; /sbin/iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-ports 44433
ExecStart=/usr/bin/sudo -u pi dotnet <website>.dll &
ExecStopPost=/sbin/iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8800 ; /sbin/iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-ports 44433
WorkingDirectory=/var/www/<website>/
StandardOutput=inherit
StandardError=inherit
Restart=always
User=root

[Install]
WantedBy=multi-user.target

Enable start on boot:

sudo systemctl enable mysite.service

Start now:

sudo systemctl start mysite.service

Script to take website down, renew certificate, bring back up

#!/bin/bash

systemctl stop mysite.service
certbot renew
openssl pkcs12 -export -out /var/www/<website>/certificate.pfx -inkey /etc/letsencrypt/live/[your domain]/privkey.pem -in /etc/letsencrypt/live/[your domain]/fullchain.pem
chown pi /var/www/<website>/certificate.pfx
chmod 600 /var/www/<website>/certificate.pfx
systemctl start mysite.service