One place for hosting & domains

      Production

      How To Harden the Security of Your Production Django Project


      The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Developing a Django application can be a quick and clean experience, because its approach is flexible and scalable. Django also offers a variety of security-oriented settings that can help you seamlessly prepare your project for production. However, when it comes to production deployment, there are several ways to further secure your project. Restructuring your project by breaking up your settings will allow you to easily set up different configurations based on the environment. Leveraging dotenv for hiding environment variables or confidential settings will ensure you don’t release any details about your project that may compromise it.

      While implementing these different strategies and features might seem time-consuming at first, developing a practical workflow will allow you to deploy releases of your project without compromising on security, or your productivity.

      In this tutorial, you will leverage a security-oriented workflow for your Django development by implementing and configuring environment-based settings, dotenv, and Django’s built-in security settings. These features all complement each other and will result in a version of your Django project that is ready for different approaches you may take to deployment.

      Prerequisites

      Before you begin this guide you’ll need the following:

      Note: If you’re using an existing Django project, you may have different requirements. This tutorial suggests a particular project structure, however, you can also use each of the sections of this tutorial individually as needed.

      Step 1 — Restructuring Django’s Settings

      In this first step, you’ll start by rearranging your settings.py file into environment-specific configurations. This is a good practice when you need to move a project between different environments, for example, development and production. This arrangement will mean less reconfiguration for different environments; instead, you’ll use an environment variable to switch between configurations, which we’ll discuss later in the tutorial.

      Create a new directory called settings in your project’s sub-directory:

      • mkdir testsite/testsite/settings

      (As per the prerequisites we’re using testsite, but you can substitute your project’s name in here.)

      This directory will replace your current settings.py configuration file; all of your environment-based settings will be in separate files contained in this folder.

      In your new settings folder, create three Python files:

      • cd testsite/testsite/settings
      • touch base.py development.py production.py

      The development.py file will contain settings you’ll normally use during development. And production.py will contain settings for use on a production server. You should keep these separate because the production configuration will use settings that will not work in a development environment; for example, forcing the use of HTTPS, adding headers, and using a production database.

      The base.py settings file will contain settings that development.py and production.py will inherit from. This is to reduce redundancy and to help keep your code cleaner. These Python files will be replacing settings.py, so you’ll now remove settings.py to avoid confusing Django.

      While still in your settings directory, rename settings.py to base.py with the following command:

      • mv ../settings.py base.py

      You’ve just completed the outline of your new environment-based settings directory. Your project won’t understand your new configuration yet, so next, you’ll fix this.

      Step 2 — Using python-dotenv

      Currently Django will not recognize your new settings directory or its internal files. So, before you continue working with your environment-based settings, you need to make Django work with python-dotenv. This is a dependency that loads environment variables from a .env file. This means that Django will look inside a .env file in your project’s root directory to determine which settings configuration it will use.

      Go to your project’s root directory:

      Install python-dotenv:

      • pip install python-dotenv

      Now you need to configure Django to use dotenv. You’ll edit two files to do this: manage.py, for development, and wsgi.py, for production.

      Let’s start by editing manage.py:

      Add the following code:

      testsite/manage.py

      import os
      import sys
      import dotenv
      
      def main():
          os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testsite.settings.development')
      
          if os.getenv('DJANGO_SETTINGS_MODULE'):
          os.environ['DJANGO_SETTINGS_MODULE'] = os.getenv('DJANGO_SETTINGS_MODULE')
      
          try:
              from django.core.management import execute_from_command_line
          except ImportError as exc:
              raise ImportError(
                  "Couldn't import Django. Are you sure it's installed and "
                  "available on your PYTHONPATH environment variable? Did you "
                  "forget to activate a virtual environment?"
              ) from exc
          execute_from_command_line(sys.argv)
      
      
      if __name__ == '__main__':
          main()
      
      dotenv.load_dotenv(
          os.path.join(os.path.dirname(__file__), '.env')
      )
      

      Save and close manage.py and then open wsgi.py for editing:

      Add the following highlighted lines:

      testsite/testsite/wsgi.py

      
      import os
      import dotenv
      
      from django.core.wsgi import get_wsgi_application
      
      dotenv.load_dotenv(
          os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
      )
      
      os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testsite.settings.development')
      
      if os.getenv('DJANGO_SETTINGS_MODULE'):
       os.environ['DJANGO_SETTINGS_MODULE'] = os.getenv('DJANGO_SETTINGS_MODULE')
      
      application = get_wsgi_application()
      

      The code you’ve added to both of these files does two things. First, whenever Django runs—manage.py for running development, wsgi.py for production—you’re telling it to look for your .env file. If the file exists, you instruct Django to use the settings file that .env recommends, otherwise you use the development configuration by default.

      Save and close the file.

      Finally, let’s create a .env in your project’s root directory:

      Now add in the following line to set the environment to development:

      testsite/.env

      DJANGO_SETTINGS_MODULE="testsite.settings.development"
      

      Note: Add .env to your .gitignore file so it is never included in your commits; you’ll use this file to contain data such as passwords and API keys that you do not want visible publicly. Every environment your project is running on will have its own .env with settings for that specific environment.

      It is recommended to create a .env.example to include in your project so you can easily create a new .env wherever you need one.

      So, by default Django will use testsite.settings.development, but if you change DJANGO_SETTINGS_MODULE to testsite.settings.production for example, it will start using your production configuration. Next, you’ll populate your development.py and production.py settings configurations.

      Step 3 — Creating Development and Production Settings

      Next you’ll open your base.py and add the configuration you want to modify for each environment in the separate development.py and production.py files. The production.py will need to use your production database credentials, so ensure you have those available.

      Note: It is up to you to determine which settings you need to configure, based on environment. This tutorial will only cover a common example for production and development settings (that is, security settings and separate database configurations).

      In this tutorial we’re using the Django project from the prerequisite tutorial as the example project. We’ll move settings from from base.py to development.py. Begin by opening development.py:

      • nano testsite/settings/development.py

      First, you will import from base.py—this file inherits settings from base.py. Then you’ll transfer the settings you want to modify for the development environment:

      testsite/testsite/settings/development.py

      from .base import *
      
      DEBUG = True
      
      DATABASES = {
          'default': {
              'ENGINE': 'django.db.backends.sqlite3',
              'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
          }
      }
      

      In this case, the settings specific to development are: DEBUG, you need this True in development, but not in production; and DATABASES, a local database instead of a production database. You’re using an SQLite database here for development.

      Note: For security purposes, Django’s DEBUG output will never display any settings that may contain the strings: API, KEY, PASS, SECRET, SIGNATURE, or TOKEN.

      This is to ensure secrets will not be revealed, if you accidentally deploy a project to production with DEBUG still enabled.

      With that being said, never deploy a project publicly with DEBUG enabled. It will only ever put the security of your project at risk.

      Next, let’s add to production.py:

      • nano testsite/settings/production.py

      Production will be similar to development.py, but with a different database configuration and DEBUG set to False:

      testsite/testsite/settings/production.py

      from .base import *
      
      DEBUG = False
      
      ALLOWED_HOSTS = []
      
      DATABASES = {
          'default': {
              'ENGINE': os.environ.get('SQL_ENGINE', 'django.db.backends.sqlite3'),
              'NAME': os.environ.get('SQL_DATABASE', os.path.join(BASE_DIR, 'db.sqlite3')),
              'USER': os.environ.get('SQL_USER', 'user'),
              'PASSWORD': os.environ.get('SQL_PASSWORD', 'password'),
              'HOST': os.environ.get('SQL_HOST', 'localhost'),
              'PORT': os.environ.get('SQL_PORT', ''),
          }
      }
      

      For the example database configuration given, you can use dotenv to configure each of the given credentials, with defaults included. Assuming you’ve already set up a database for the production version of your project, please use your configuration instead of the example provided.

      You have now configured your project to use different settings based on DJANGO_SETTINGS_MODULE in .env. Given the example settings you’ve used, when you set your project to use production settings, DEBUG becomes False, ALLOWED_HOSTS is defined, and you start using a different database that you’ve (already) configured on your server.

      Step 4 — Working with Django’s Security Settings

      Django includes security settings ready for you to add to your project. In this step, you’ll add security settings to your project that are considered essential for any production project. These settings are intended for use when your project is available to the public. It’s not recommended to use any of these settings in your development environment; hence, in this step you’re limiting these settings to the production.py configuration.

      For the most part these settings are going to enforce the use of HTTPS for various web features, such as session cookies, CSRF cookies, upgrading HTTP to HTTPS, and so on. Therefore, if you haven’t already set up your server with a domain pointing to it, hold off on this section for now. If you need to set up your server ready for deployment, check out the Conclusion for suggested articles on this.

      First open production.py:

      In your file, add the highlighted settings that work for your project, according to the explanations following the code:

      testsite/testsite/settings/production.py

      from .base import *
      
      DEBUG = False
      
      ALLOWED_HOSTS = ['your_domain', 'www.your_domain']
      
      DATABASES = {
          'default': {
              'ENGINE': os.environ.get('SQL_ENGINE', 'django.db.backends.sqlite3'),
              'NAME': os.environ.get('SQL_DATABASE', os.path.join(BASE_DIR, 'db.sqlite3')),
              'USER': os.environ.get('SQL_USER', 'user'),
              'PASSWORD': os.environ.get('SQL_PASSWORD', 'password'),
              'HOST': os.environ.get('SQL_HOST', 'localhost'),
              'PORT': os.environ.get('SQL_PORT', ''),
          }
      }
      
      SECURE_SSL_REDIRECT = True
      
      SESSION_COOKIE_SECURE = True
      
      CSRF_COOKIE_SECURE = True
      
      SECURE_BROWSER_XSS_FILTER = True
      
      • SECURE_SSL_REDIRECT redirects all HTTP requests to HTTPS (unless exempt). This means your project will always try to use an encrypted connection. You will need to have SSL configured on your server for this to work. Note that if you have Nginx or Apache configured to do this already, this setting will be redundant.
      • SESSION_COOKIE_SECURE tells the browser that cookies can only be handled over HTTPS. This means cookies your project produces for activities, such as logins, will only work over an encrypted connection.
      • CSRF_COOKIE_SECURE is the same as SESSION_COOKIE_SECURE but applies to your CSRF token. CSRF tokens protect against Cross-Site Request Forgery. Django CSRF protection does this by ensuring any forms submitted (for logins, signups, and so on) to your project were created by your project and not a third party.
      • SECURE_BROWSER_XSS_FILTER sets the X-XSS-Protection: 1; mode=block header on all responses that do not already have it. This ensures third parties cannot inject scripts into your project. For example, if a user stores a script in your database using a public field, when that script is retrieved and displayed to other users it will not run.

      If you would like to read more about the different security settings available within Django, check out their documentation.

      Warning: Django’s documentation states you shouldn’t rely completely on SECURE_BROWSER_XSS_FILTER. Never forget to validate and sanitize input.

      Additional Settings

      The following settings are for supporting HTTP Strict Transport Security (HSTS)—this means that your entire site must use SSL at all times.

      • SECURE_HSTS_SECONDS is the amount of time in seconds HSTS is set for. If you set this for an hour (in seconds), every time you visit a web page on your website, it tells your browser that for the next hour HTTPS is the only way you can visit the site. If during that hour you visit an insecure part of your website, the browser will show an error and the insecure page will be inaccessible.
      • SECURE_HSTS_PRELOAD only works if SECURE_HSTS_SECONDS is set. This header instructs the browser to preload your site. This means that your website will be added to a hardcoded list, which is implemented in popular browsers, like Firefox and Chrome. This requires that your website is always encrypted. It is important to be careful with this header. If at anytime you decide to not use encryption for your project, it can take weeks to be manually removed from the HSTS Preload list.
      • SECURE_HSTS_INCLUDE_SUBDOMAINS applies the HSTS header to all subdomains. Enabling this header, means that both your_domain and unsecure.your_domain will require encryption even if unsecure.your_domain is not related to this Django project.

      Warning: Incorrectly configuring these additional settings can break your site for a significant amount of time.

      Please read the Django documentation on HSTS before implementing these settings.

      It is necessary to consider how these settings will work with your own Django project; overall the settings discussed here are a good foundation for most Django projects. Next you’ll review some further usage of dotENV.

      Step 5 — Using python-dotenv for Secrets

      The final part of this tutorial will help you leverage python-dotenv. This will allow you to hide certain information such as your project’s SECRET_KEY or the admin’s login URL. This is a great idea if you intend to publish your code on platforms like GitHub or GitLab since these secrets won’t be published. Instead, whenever you initially set up your project on a local environment or a server, you can create a new .env file and define those secret variables.

      You must hide your SECRET_KEY so you’ll start working on that in this section.

      Open your .env file:

      And add the following line:

      testsite/.env

      DJANGO_SETTINGS_MODULE="django_hardening.settings.development"
      SECRET_KEY="your_secret_key"
      

      And in your base.py:

      • nano testsite/settings/base.py

      Let’s update the SECRET_KEY variable like so:

      testsite/testsite/settings/base.py

      . . .
      SECRET_KEY = os.getenv('SECRET_KEY')
      . . .
      

      Your project will now use the SECRET_KEY located in .env.

      Lastly, you’ll hide your admin URL by adding a long string of random characters to it. This will ensure bots can’t brute force the login fields and strangers can’t try guessing the login.

      Open .env again:

      And add a SECRET_ADMIN_URL variable:

      testsite/.env

      DJANGO_SETTINGS_MODULE="django_hardening.settings.development"
      SECRET_KEY="your_secret_key"
      SECRET_ADMIN_URL="very_secret_url"
      

      Now let’s tell Django to hide your admin URL with SECRET_ADMIN_URL:

      Note: Don’t forget to replace your_secret_key and very_secret_url with your own secret strings. Also don’t forget to replace very_secret_url with your own secret URL.

      If you want to use random strings for these variables, Python provides a fantastic secrets.py library for generating such strings. The examples they give are great ways to create small Python programs for generating secure random strings.

      Edit the admin URL like so:

      testsite/testsite/urls.py

      import os
      from django.contrib import admin
      from django.urls import path
      
      urlpatterns = [
          path(os.getenv('SECRET_ADMIN_URL') + '/admin/', admin.site.urls),
      ]
      

      You can now find the admin login page at very_secret_url/admin/ instead of just /admin/.

      Conclusion

      In this tutorial you have configured your current Django project for easy use with different environments. Your project now leverages python-dotenv for handling secrets and settings. And your production settings now have Django’s built-in security features enabled.

      If you’ve enabled all the recommended security components and re-implemented settings as directed, your project has these key features:

      • SSL/HTTPS for all communications (for example, subdomains, cookies, CSRF).
      • XSS (cross-site scripting) attacks prevention.
      • CSRF (cross-site request forgery) attacks prevention.
      • Concealed project secret key.
      • Concealed admin login URL preventing brute-force attacks.
      • Separate settings for development and production.

      If you are interested in learning more about Django, check out our tutorial series on Django development.

      Also, if you haven’t already put your project into production, here is a tutorial on How To Set Up Django with Postgres, Nginx, and Gunicorn on Ubuntu 20.04. You can also check out our Django topic page for further tutorials.

      And, of course, please read over Django’s settings documentation, for further information.



      Source link

      Comment mettre en place une application Node.js pour la production sur Ubuntu 20.04


      Introduction

      Node.js est un environnement d’exécution JavaScript open-source pour la création d’applications côté serveur et de réseau. La plate-forme fonctionne sous Linux, macOS, FreeBSD et Windows. Bien que vous puissiez exécuter les applications Node.js en ligne de commande, ce tutoriel se concentre sur leur exécution en tant que service. Cela signifie qu’ils redémarreront au redémarrage ou en cas d’échec et qu’ils peuvent être utilisés en toute sécurité dans un environnement de production.

      Dans ce tutoriel, vous allez mettre en place un environnement Node.js prêt pour la production sur un seul serveur Ubuntu 20.04. Ce serveur exécutera une application Node.js gérée par PM2, et fournira aux utilisateurs un accès sécurisé à l’application via un proxy inverse Nginx. Le serveur Nginx offrira le HTTPS en utilisant un certificat gratuit fourni par Let’s Encrypt. 

      Conditions préalables

      Ce guide suppose que vous disposez des éléments suivants :

      Lorsque vous aurez rempli les conditions préalables, vous disposerez d’un serveur desservant la page par défaut de votre domaine à l’adresse https://example.com/. 

      Étape 1 — Installation de Node.js

      Commençons par installer la dernière version LTS de Node.js, en utilisant les archives des packages NodeSource.

      Tout d’abord, installez le NodeSource PPA afin d’avoir accès à son contenu. Assurez-vous que vous êtes dans votre répertoire d’origine et utilisez curl pour récupérer le script d’installation de la version LTS la plus récente de Node.js dans ses archives.

      • cd ~
      • curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh

      Vous pouvez inspecter le contenu de ce script avec nano ou votre éditeur de texte préféré :

      Lorsque vous avez fini d’inspecter le script, lancez le sous sudo: 

      • sudo bash nodesource_setup.sh

      Le PPA sera ajouté à votre configuration et le cache local de votre package sera automatiquement mis à jour. Après avoir exécuté le script d’installation à partir de Nodesource, vous pouvez installer le paquet Node.js :

      Pour vérifier quelle version de Node.js vous avez installée après ces premières étapes, tapez :

      Output

      v14.4.0

      Note : Lors de l’installation à partir du PPA NodeSource, l’exécutable Node.js est appelé nodejs, plutôt que node.

      Le paquet nodejs contient le binaire nodejs ainsi que npm, un gestionnaire de packages pour les modules Node, de sorte que vous n’avez pas besoin d’installer npm séparément.

      npm utilise un fichier de configuration dans votre répertoire de base pour suivre les mises à jour. Il sera créé la première fois que vous utiliserez npm. Exécutez cette commande pour vérifier que npm est installé et pour créer le fichier de configuration :

      Output

      6.14.5

      Pour que certains packages npm fonctionnent (par exemple, ceux qui nécessitent la compilation du code source), vous devrez installer le package build-essential :

      • sudo apt install build-essential

      Vous disposez maintenant des outils nécessaires pour travailler avec les packages npm qui nécessitent de compiler du code source.

      Une fois le runtime Node.js installé, passons à l’écriture d’une application Node.js.

      Étape 2 — Création d’une application Node.js

      Écrivons une application Hello World qui renvoie « Hello World » à toute demande HTTP. Cet exemple de demande vous aidera à mettre en place Node.js. Vous pouvez le remplacer par votre propre application : assurez-vous simplement de modifier votre application pour écouter sur les adresses IP et les ports appropriés.

      Tout d’abord, créons un exemple de demande appelée hello.js: 

      Ajoutez le code suivant dans le fichier :

      ~/hello.js

      const http = require('http');
      
      const hostname="localhost";
      const port = 3000;
      
      const server = http.createServer((req, res) => {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'text/plain');
        res.end('Hello World!n');
      });
      
      server.listen(port, hostname, () => {
        console.log(`Server running at http://${hostname}:${port}/`);
      });
      

      Enregistrez le fichier et quittez l’éditeur.

      Cette application Node.js écoute sur l’adresse (localhost) et le port (3000) spécifiés, et renvoie « Hello World ! » avec un code de réussite de 200 HTTP.  Comme nous sommes à l’écoute sur localhost, les clients distants ne pourront pas se connecter à notre application.

      Pour tester votre demande, tapez :

      Vous recevrez le résultat suivant :

      Output

      Server running at http://localhost:3000/

      Note : L’exécution d’une application Node.js de cette manière bloquera les commandes supplémentaires jusqu’à ce que l’application soit tuée en appuyant sur CTRL+C.

      Pour tester l’application, ouvrez une autre session de terminal sur votre serveur, et connectez-vous à localhost avec curl :

      • curl http://localhost:3000

      Si vous obtenez le résultat suivant, l’application fonctionne correctement et écoute à la bonne adresse et sur le bon port :

      Output

      Hello World!

      Si vous n’obtenez pas le résultat attendu, assurez-vous que votre application Node.js est en cours d’exécution et configurée pour écouter sur l’adresse et le port appropriés.

      Une fois que vous êtes sûr que cela fonctionne, arrêtez l’application (si ce n’est pas déjà fait) en appuyant sur CTRL+C.

      Étape 3 — Installer PM2

      Ensuite, nous allons installer PM2, un gestionnaire de processus pour les applications Node.js. PM2 permet de démoniser les applications afin qu’elles s’exécutent en arrière-plan comme un service.

      Utilisez npm pour installer la dernière version de PM2 sur votre serveur :

      • sudo npm install pm2@latest -g

      L’option -g indique à npm d’installer le module dans le monde entier, afin qu’il soit disponible dans tout le système.

      Commençons par utiliser la commande pm2 start​​​1​​​ pour exécuter votre application, hello.js, en arrière-plan :

      Cela ajoute également votre demande à la liste de processus de PM2, qui est produite chaque fois que vous lancez une demande :

      Output

      ... [PM2] Spawning PM2 daemon with pm2_home=/home/sammy/.pm2 [PM2] PM2 Successfully daemonized [PM2] Starting /home/sammy/hello.js in fork_mode (1 instance) [PM2] Done. ┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐ │ id │ name │ mode │ ↺ │ status │ cpu │ memory │ ├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤ │ 0 │ hello │ fork │ 0 │ online │ 0% │ 25.2mb │ └────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘

      Comme indiqué ci-dessus, PM2 attribue automatiquement un nom d'application (basé sur le nom de fichier, sans l’extension .js) et un identifiant PM2. PM2 conserve également d’autres informations, telles que le PID du processus, son état actuel et l’utilisation de la mémoire.

      Les applications qui tournent sous PM2 seront redémarrées automatiquement si l’application crashe ou est arrêtée, mais nous pouvons prendre une mesure supplémentaire pour que l’application soit lancée au démarrage du système en utilisant la sous-commande startup.  Cette sous-commande génère et configure un script de démarrage pour lancer PM2 et ses processus gérés aux démarrages du serveur :

      La dernière ligne de la sortie résultante comprendra une commande à exécuter avec les privilèges de superuser afin de configurer PM2 pour qu’il démarre au démarrage :

      Output

      [PM2] Init System found: systemd sammy [PM2] To setup the Startup Script, copy/paste the following command: sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

      Exécutez la commande à partir de la sortie, avec votre nom d’utilisateur à la place de sammy : 

      • sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

      Comme étape supplémentaire, nous pouvons sauvegarder la liste des processus PM2 et les environnements correspondants :

      Vous avez maintenant créé une unité systemd qui exécute pm2 pour votre utilisateur au démarrage. Cette instance pm2, à son tour, lance hello.js.

      Démarrer le service avec systemctl: 

      • sudo systemctl start pm2-sammy

      Si, à ce stade, un message d’erreur s’affiche, vous devrez peut-être redémarrer, ce que vous pouvez faire avec sudo reboot. 

      Vérifiez l’état de l’unité systemd :

      • systemctl status pm2-sammy

      Pour un aperçu détaillé de systemd, veuillez consulter Les Fondamentaux de Systemd : Travailler avec les services, les unités et le journal. 

      En plus de celles que nous avons couvertes, PM2 fournit de nombreuses sous-commandes qui vous permettent de gérer ou de rechercher des informations sur vos demandes.

      Arrêtez une application avec cette commande (spécifiez le nom ou l’identifiant de l’application PM2) :

      Redémarrer une application :

      • pm2 restart app_name_or_id

      Liste des applications actuellement gérées par PM2 :

      Obtenez des informations sur une application spécifique en utilisant son Nom d'application: 

      Le moniteur du processus PM2 peut être remonté avec la sous-commande monit.  Il affiche l’état de l’application, l’unité centrale et l’utilisation de la mémoire :

      Notez que l’exécution de pm2 sans aucun argument affichera également une page d’aide avec des exemples d’utilisation.

      Maintenant que votre application Node.js est exécutée et gérée par PM2, mettons en place le proxy inverse.

      Étape 4 — Configurer Nginx en tant que serveur proxy inverse

      Votre application fonctionne et écoute sur localhost, mais vous devez mettre en place un moyen pour que vos utilisateurs y accèdent. Pour cela, nous allons mettre en place le serveur web Nginx comme proxy inverse.

      Dans le tutoriel inclus dans les conditions préalables, vous configurez votre Nginx dans le fichier /etc/nginx/sites available/example.com. Ouvrez ce fichier pour le modifier :

      • sudo nano /etc/nginx/sites-available/example.com

      Dans le bloc de serveurs, vous devez avoir un location/bloc existant. Remplacez le contenu de ce bloc par la configuration suivante. Si votre application est configurée pour écouter sur un port différent, mettez à jour la partie en surbrillance avec le numéro de port correct :

      /etc/nginx/sites-available/example.com

      server {
      ...
          location / {
              proxy_pass http://localhost:3000;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
          }
      ...
      }
      

      Cela permet de configurer le serveur pour qu’il réponde aux demandes à son root. En supposant que notre serveur soit disponible à l’adresse example.com, l’accès à https://example.com/ via un navigateur web enverrait la demande à hello.js, en écoute sur le port 3000 de localhost.

      Vous pouvez ajouter des blocs d’location supplémentaires au même bloc de serveur pour permettre l’accès à d’autres applications sur le même serveur. Par exemple, si vous exécutez également une autre application Node.js sur le port 3001, vous pourriez ajouter ce bloc de localisation pour y accéder via https://example.com/app2 :

      /etc/nginx/sites-available/example.com — Optional

      server {
      ...
          location /app2 {
              proxy_pass http://localhost:3001;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
          }
      ...
      }
      

      Une fois que vous avez fini d’ajouter les blocs de localisation pour vos applications, enregistrez le fichier et quittez votre éditeur.

      Assurez-vous que vous n’avez pas introduit d’erreurs de syntaxe en tapant :

      Redémarrez Nginx :

      • sudo systemctl restart nginx

      En supposant que votre application Node.js fonctionne, et que votre application et les configurations Nginx sont correctes, vous devriez maintenant pouvoir accéder à votre application via le proxy inverse Nginx. Essayez-le en accédant à l’URL de votre serveur (son adresse IP publique ou son nom de domaine).

      Conclusion

      Félicitations. Vous avez maintenant votre application Node.js fonctionnant derrière un proxy inverse Nginx sur un serveur Ubuntu 20.04. Cette configuration de proxy inverse est suffisamment souple pour permettre à vos utilisateurs d’accéder à d’autres applications ou à du contenu web statique que vous souhaitez partager.



      Source link

      How To Set Up a Node.js Application for Production on Ubuntu 20.04


      Not using Ubuntu 20.04?


      Choose a different version or distribution.

      Introduction

      Node.js is an open-source JavaScript runtime environment for building server-side and networking applications. The platform runs on Linux, macOS, FreeBSD, and Windows. Though you can run Node.js applications at the command line, this tutorial will focus on running them as a service. This means that they will restart on reboot or failure and are safe for use in a production environment.

      In this tutorial, you will set up a production-ready Node.js environment on a single Ubuntu 20.04 server. This server will run a Node.js application managed by PM2, and provide users with secure access to the application through an Nginx reverse proxy. The Nginx server will offer HTTPS using a free certificate provided by Let’s Encrypt.

      Prerequisites

      This guide assumes that you have the following:

      When you’ve completed the prerequisites, you will have a server serving your domain’s default placeholder page at https://example.com/.

      Step 1 — Installing Node.js

      Let’s begin by installing the latest LTS release of Node.js, using the NodeSource package archives.

      First, install the NodeSource PPA in order to get access to its contents. Make sure you’re in your home directory, and use curl to retrieve the installation script for the most recent LTS version of Node.js from its archives.

      • cd ~
      • curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh

      You can inspect the contents of this script with nano or your preferred text editor:

      When you’re done inspecting the script, run it under sudo:

      • sudo bash nodesource_setup.sh

      The PPA will be added to your configuration and your local package cache will be updated automatically. After running the setup script from Nodesource, you can install the Node.js package:

      To check which version of Node.js you have installed after these initial steps, type:

      Output

      v14.4.0

      Note: When installing from the NodeSource PPA, the Node.js executable is called nodejs, rather than node.

      The nodejs package contains the nodejs binary as well as npm, a package manager for Node modules, so you don’t need to install npm separately.

      npm uses a configuration file in your home directory to keep track of updates. It will be created the first time you run npm. Execute this command to verify that npm is installed and to create the configuration file:

      Output

      6.14.5

      In order for some npm packages to work (those that require compiling code from source, for example), you will need to install the build-essential package:

      • sudo apt install build-essential

      You now have the necessary tools to work with npm packages that require compiling code from source.

      With the Node.js runtime installed, let’s move on to writing a Node.js application.

      Step 2 — Creating a Node.js Application

      Let’s write a Hello World application that returns “Hello World” to any HTTP requests. This sample application will help you get Node.js set up. You can replace it with your own application — just make sure that you modify your application to listen on the appropriate IP addresses and ports.

      First, let’s create a sample application called hello.js:

      Insert the following code into the file:

      ~/hello.js

      const http = require('http');
      
      const hostname="localhost";
      const port = 3000;
      
      const server = http.createServer((req, res) => {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'text/plain');
        res.end('Hello World!n');
      });
      
      server.listen(port, hostname, () => {
        console.log(`Server running at http://${hostname}:${port}/`);
      });
      

      Save the file and exit the editor.

      This Node.js application listens on the specified address (localhost) and port (3000), and returns “Hello World!” with a 200 HTTP success code. Since we’re listening on localhost, remote clients won’t be able to connect to our application.

      To test your application, type:

      You will receive the following output:

      Output

      Server running at http://localhost:3000/

      Note: Running a Node.js application in this manner will block additional commands until the application is killed by pressing CTRL+C.

      To test the application, open another terminal session on your server, and connect to localhost with curl:

      • curl http://localhost:3000

      If you get the following output, the application is working properly and listening on the correct address and port:

      Output

      Hello World!

      If you do not get the expected output, make sure that your Node.js application is running and configured to listen on the proper address and port.

      Once you’re sure it’s working, kill the application (if you haven’t already) by pressing CTRL+C.

      Step 3 — Installing PM2

      Next let’s install PM2, a process manager for Node.js applications. PM2 makes it possible to daemonize applications so that they will run in the background as a service.

      Use npm to install the latest version of PM2 on your server:

      • sudo npm install pm2@latest -g

      The -g option tells npm to install the module globally, so that it’s available system-wide.

      Let’s first use the pm2 start command to run your application, hello.js, in the background:

      This also adds your application to PM2’s process list, which is outputted every time you start an application:

      Output

      ... [PM2] Spawning PM2 daemon with pm2_home=/home/sammy/.pm2 [PM2] PM2 Successfully daemonized [PM2] Starting /home/sammy/hello.js in fork_mode (1 instance) [PM2] Done. ┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐ │ id │ name │ mode │ ↺ │ status │ cpu │ memory │ ├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤ │ 0 │ hello │ fork │ 0 │ online │ 0% │ 25.2mb │ └────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘

      As indicated above, PM2 automatically assigns an App name (based on the filename, without the .js extension) and a PM2 id. PM2 also maintains other information, such as the PID of the process, its current status, and memory usage.

      Applications that are running under PM2 will be restarted automatically if the application crashes or is killed, but we can take an additional step to get the application to launch on system startup using the startup subcommand. This subcommand generates and configures a startup script to launch PM2 and its managed processes on server boots:

      The last line of the resulting output will include a command to run with superuser privileges in order to set PM2 to start on boot:

      Output

      [PM2] Init System found: systemd sammy [PM2] To setup the Startup Script, copy/paste the following command: sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

      Run the command from the output, with your username in place of sammy:

      • sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

      As an additional step, we can save the PM2 process list and corresponding environments:

      You have now created a systemd unit that runs pm2 for your user on boot. This pm2 instance, in turn, runs hello.js.

      Start the service with systemctl:

      • sudo systemctl start pm2-sammy

      If at this point you encounter an error, you may need to reboot, which you can achieve with sudo reboot.

      Check the status of the systemd unit:

      • systemctl status pm2-sammy

      For a detailed overview of systemd, please review Systemd Essentials: Working with Services, Units, and the Journal.

      In addition to those we have covered, PM2 provides many subcommands that allow you to manage or look up information about your applications.

      Stop an application with this command (specify the PM2 App name or id):

      Restart an application:

      • pm2 restart app_name_or_id

      List the applications currently managed by PM2:

      Get information about a specific application using its App name:

      The PM2 process monitor can be pulled up with the monit subcommand. This displays the application status, CPU, and memory usage:

      Note that running pm2 without any arguments will also display a help page with example usage.

      Now that your Node.js application is running and managed by PM2, let’s set up the reverse proxy.

      Step 4 — Setting Up Nginx as a Reverse Proxy Server

      Your application is running and listening on localhost, but you need to set up a way for your users to access it. We will set up the Nginx web server as a reverse proxy for this purpose.

      In the prerequisite tutorial, you set up your Nginx configuration in the /etc/nginx/sites-available/example.com file. Open this file for editing:

      • sudo nano /etc/nginx/sites-available/example.com

      Within the server block, you should have an existing location / block. Replace the contents of that block with the following configuration. If your application is set to listen on a different port, update the highlighted portion to the correct port number:

      /etc/nginx/sites-available/example.com

      server {
      ...
          location / {
              proxy_pass http://localhost:3000;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
          }
      ...
      }
      

      This configures the server to respond to requests at its root. Assuming our server is available at example.com, accessing https://example.com/ via a web browser would send the request to hello.js, listening on port 3000 at localhost.

      You can add additional location blocks to the same server block to provide access to other applications on the same server. For example, if you were also running another Node.js application on port 3001, you could add this location block to allow access to it via https://example.com/app2:

      /etc/nginx/sites-available/example.com — Optional

      server {
      ...
          location /app2 {
              proxy_pass http://localhost:3001;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
          }
      ...
      }
      

      Once you are done adding the location blocks for your applications, save the file and exit your editor.

      Make sure you didn’t introduce any syntax errors by typing:

      Restart Nginx:

      • sudo systemctl restart nginx

      Assuming that your Node.js application is running, and your application and Nginx configurations are correct, you should now be able to access your application via the Nginx reverse proxy. Try it out by accessing your server’s URL (its public IP address or domain name).

      Conclusion

      Congratulations! You now have your Node.js application running behind an Nginx reverse proxy on an Ubuntu 20.04 server. This reverse proxy setup is flexible enough to provide your users access to other applications or static web content that you want to share.



      Source link