Hostování ASP.NET Core na Linuxu

26.10.2020
Článek popisuje jak zprovoznit ASP.NET Core na Linuxu. Cílem je z Linuxu vytvořit webový server, na kterém může běžet webová stránka napsaná v jazyce C#. Jde o proof of concept, kdy mě zajímalo, jak moc je složité nastavit Linux + Nginx jako alternativu k IIS. Nastavení se dá použít pro vývojové nebo testovací prostředí, případně jako podklad pro produkci, pokud bychom chtěli hostovat stránky na VPS nebo on-premise serveru.

Slovníček

ASP.NET Core Open-source webový framework od Microsoftu (první release 2016).
Kestrel Odlehčený, cross-platform webový server od Microsoftu, který je součástí ASP.NET Core projektu.
Nginx Původem ruský webový server, na kterém běží ~třetina webových stránek na internetu.
IIS Webový server od Microsoftu s podporou .NET
VMPlayer VMware Workstation Player je zdarma dostupná aplikace pro správu a spuštění virutálního operačního systému.
Reverzní proxy Proxy na straně serveru. Proxy požadavky přeposílá na další server(y). Může také sloužit pro nastavení load balancingu, kešování, zabezpečení, přidání šifrování apod.
Bridged network Propojí virtuální počítač se sítí pomocí hostitelského počítače.

Obsah

Příprava prostředí

Idea je znázorněná obrázkem a jedná se o velmi jednoduchý scénář, který vyžaduje pouze jeden počítač (s procesorem, který podporuje virtualizaci). Na počítači se nainstaluje nějaký "virtualizér", v mém případě VMPlayer, ale je možné použít i VirtualBox nebo Hyper-V. Virtuální počítač připojíme do lokální sítě (bridge network) a nainstalujeme Linux (zvolil jsem linuxovou distribuci Ubuntu). V Linuxu pak nainstalujeme .NET Core runtime a také webový server Nginx, který nastavíme tak, aby veškeré HTTP/HTTPS požadavky přesměrovával na Kestrel, jehož odpověď nám pak Nginx přepošle zpátky (reverse proxy).

Instalace Linuxu

Stáhneme si instalační .ISO pro Ubuntu (v mém případě verze Desktop 20.04.1 LTS) Nastavíme VMPlayer do bridge módu a nainstalujeme Linux standardním způsobem.

Instalace .NET Core

Nyní nainstalujeme .NET Core runtime, verze musí být kompatibilní s verzí, ve které pak poběží web, který budeme hostovat. Jelikož budu na serveru web přímo i vytvářet, tak musím dodatečně nainstalovat i SDK (Software Development Kit).
sudo apt update

wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb

sudo dpkg -i packages-microsoft-prod.deb

sudo apt-get update; \
  sudo apt-get install -y apt-transport-https && \
  sudo apt-get update && \
  sudo apt-get install -y aspnetcore-runtime-3.1

sudo apt-get update; \
  sudo apt-get install -y apt-transport-https && \
  sudo apt-get update && \
  sudo apt-get install -y dotnet-sdk-3.1
a provedeme kontrolu, že se nám runtime a SDK nainstaloval v pořádku
dotnet --list-runtimes
dotnet --list-sdks

Instalace Nginx

Nginx nainstalujeme jednoduše následujícím příkazem :-)
sudo apt install nginx

Konfigurace Nginx serveru

Nginx může hostovat více stránek, každá stránka má pak vlastní konfigurační soubor (tzv. block, což je obdoba virutal hostu v Apachi). V adresáři /etc/nginx/sites-available/ vytvoříme tedy konfigurační soubor pro náš web, který jsem pojmenoval pracovně CoreSampleWeb.cz.
/etc/nginx/sites-available/coresampleweb.cz
server {
  listen     	*:80;
  server_name	CoreSampleWeb.cz
  return     	301 https://$host$request_uri;
}

server {
  listen                    *:443 ssl;
  server_name               CoreSampleWeb.cz;
  ssl_certificate           /etc/ssl/certs/nginx-selfsigned.crt;
  ssl_certificate_key       /etc/ssl/private/nginx-selfsigned.key;
  ssl_protocols             TLSv1.1 TLSv1.2;
  ssl_prefer_server_ciphers on;
  ssl_ciphers               "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
  ssl_ecdh_curve            secp384r1;
  ssl_session_cache         shared:SSL:10m;
  ssl_session_tickets       off;
  # ssl_stapling             on; #ensure your cert is capable
  # ssl_stapling_verify      on; #ensure your cert is capable

  # Enabling HSTS, (max-age in seconds fo 2 years)
  # The first time your site is accessed using HTTPS and it returns the Strict-Transport-Security header, 
  # the browser records this information, so that future attempts to load the site using HTTP will automatically use HTTPS instead.
  # It helps to prevent MITM attack.
  # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
  add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";

  # X-Frame-Options allows content publishers to prevent their own content from being used in an invisible frame by attackers. 
  # The DENY option is the most secure, preventing any use of the current page in a frame. More commonly, SAMEORIGIN is used, 
  # as it does enable the use of frames, but limits them to the current domain.
  # This option is related to frame and iframe (inline frame) html tag.
  # The method exploiting this vulnerability is called clickjacking.
  add_header X-Frame-Options DENY;

  # This header prevents most browsers from MIME-sniffing a response away from the declared content type, as the header instructs 
  # the browser not to override the response content type. With the nosniff option, if the server says the content is "text/html", 
  # the browser renders it as "text/html".
  # https://www.keycdn.com/support/what-is-mime-sniffing
  add_header X-Content-Type-Options nosniff;

  # Add client IP to the header address chain
  # https://en.wikipedia.org/wiki/X-Forwarded-For
  proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;

  # Add information about client protocol to the header
  # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto
  proxy_set_header   X-Forwarded-Proto $scheme;

  # Other settings, related to optimalization and web socket (SignalR)
  # proxy_http_version 1.1;
  # proxy_set_header   Upgrade $http_upgrade;
  # proxy_set_header   Connection keep-alive;
  # proxy_set_header   Host $host;
  # proxy_cache_bypass $http_upgrade;

  # Redirects all traffic
  location / {
    proxy_pass http://localhost:5000;
    # limit_req  zone=one burst=10 nodelay;
  }
}
Spustíme kontrolu, jestli Nginx rozumí tomu, co jsme mu napsali :-) a zaktivujeme konfiguraci
sudo nginx -t
sudo nginx -s reload
V konfiguraci je nastaveno několik důležitých věcí
  • Všechny HTTP požadavky s doménovým jménem coresampleweb.cz přeposíláme na HTTPS
  • Všechny HTTPS požadavky s doménovým jménem coresampleweb.cz přeposíláme na http://localhost:5000
  • Veřejný SSL klíč je uložený v souboru /etc/ssl/certs/nginx-selfsigned.crt
  • Privátní SSL klíč je uložený v souboru /etc/ssl/private/nginx-selfsigned.key
  • Nastavili jsme HSTS pomocí hlavičky Strict-Transport-Security
  • Zablokovali jsme clickjacking pomocí hlavičky X-Frame-Options
  • Zablokovali jsme MIME-sniffing pomocí hlavičky X-Content-Type-Options
  • Nastavili jsme hlavičky X-Forwarded-For a X-Forwarded-Proto, které může web aplikace využít

Vygenerování SSL certifikátu

Do úložiště klíčů si vygenerujeme privátní a veřejný klíč. Takový klíč se bude zobrazovat v prohlížeči uživatele jako nedůvěryhodný, protože nebyl podepsán certifikační autoritou, ale to nám pro testovací účely nevadí.
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt

Správa služby

Pro ovládání Nginx služby můžeme použít příkaz systemctl.
# Enable or disable Nginx to start automatically when the server boots
sudo systemctl enable nginx
sudo systemctl disable nginx

# Start, stop, restart or reload service
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
sudo systemctl reload nginx

# Check status of the service
sudo systemctl status nginx

Vytvoření a publikování ASP.NET Core projektu

Z důvodu jednoduchosti aplikaci vytvořím přímo na linuxovém serveru, jinak je samozřejmě možné aplikaci přesunout přes sdílený disk, usb, ftps apod. Vytvořím si tedy v domovském adresáři složku workspace.
mkdir -p ~/workspace/CoreSampleWeb
cd ~/workspace/CoreSampleWeb
přes dotnet příkaz vygenerujeme výchozí MVC aplikaci
dotnet new mvc
Upravíme Startup.cs
  • Přidáme podporu pro ForwardedHeaders
  • Zakomentujeme app.UseHttpsRedirection(), protože HTTPS již řešíme na úrovni Nginxu
public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.Configure<ForwardedHeadersOptions>(options =>
        {
            options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
        });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseForwardedHeaders();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseForwardedHeaders();
            app.UseHsts();
        }

        //app.UseHttpsRedirection();
        app.UseStaticFiles();
aplikaci buildneme
dotnet publish --configuration release
aplikaci přesuneme do produkčního adresáře, který jsme nastavili v Nginxu.
sudo rm -rf /var/www/CoreSampleWeb/*
sudo cp -r ~/workspace/CoreSampleWeb/bin/Release/netcoreapp3.1/publish/* /var/www/CoreSampleWeb/
spustíme Kestrel
dotnet CoreSampleWeb.dll
nyní nám web již běží, v případě potřeby můžeme DNS název přidat i do souboru /etc/hosts.
127.0.0.1  CoreSampleWeb.cz

Vyzkoušení funkčnosti

Nyní máme již téměř vše nastaveno, pro HTTP/S službu vytvoříme na firewallu pravidlo a zkusíme se připojit z nějakého počítače v lokální síti.

Nastavení firewallu

Zkontrolujeme a nastavíme firewall, abychom se mohli na stránku připojit i mimo server. K tomu použijeme příkaz ufw, který na pozadí nastavuje iptables
sudo ufw enable
sudo ufw allow 80
sudo ufw allow 443
sudo ufw status
pro správu adresace můžeme použít ifconfig
sudo apt install net-tools
ifconfig
pokud bychom potřebovali zrestartovat IP adresaci, například z důvodu změny na DHCP serveru, vmware nastavení, apod., tak můžeme použít network-managera
sudo service network-manager restart

Připojení se z klienta

Před tím, než zadáme adresu našeho webu do prohlížeče, musíme nastavit DNS záznam. Otevřeme notepad jako administrátor a upravíme soubor C:\Windows\System32\drivers\etc\hosts, kde 192.168.0.178 je adresa Linux serveru.
192.168.0.178 CoreSampleWeb.cz
Potom se již můžeme na web připojit.

Odkazy