HTTPS pentru o aplicație Node.js: certificat ACME cu acme.sh
TipărireO aplicație Node.js de pe un VPS primește HTTPS în unul din două moduri: puneți Nginx în fața ei și lăsați Certbot să se ocupe de certificat (alegerea obișnuită în producție) sau lăsați procesul Node.js să servească singur HTTPS, cu un certificat obținut prin acme.sh. Ambele funcționează cu abonamentul Sectigo ACME și se reînnoiesc automat. Mai jos aflați când se potrivește fiecare, iar a doua variantă este descrisă complet.
Pe pachetele Web Hosting SSD de la TopHost, aplicațiile Node.js se creează în cPanel cu Setup Node.js App și rulează în spatele serverului web al găzduirii, deci folosesc certificatul domeniului emis de AutoSSL sau unul instalat ca în articolul acme.sh pe găzduirea cPanel. Acolo nu aveți nevoie de nimic din această pagină.
Ce variantă să alegeți
| Nginx în fața Node.js | Node.js servește singur HTTPS | |
|---|---|---|
| Cine ocupă porturile 80 și 443 | Nginx; aplicația ascultă pe un port local, de exemplu 3000 | Procesul Node.js |
| Certificatul | Certbot cu --nginx | acme.sh, fișierele sunt citite de aplicație |
| După reînnoire | Nginx se reîncarcă, aplicația nu se repornește | Aplicația se repornește prin PM2 |
| Potrivit pentru | Site-uri în producție, mai multe aplicații pe un server | O singură aplicație mică, fără alt server web |
Varianta 1: Nginx ca proxy invers
Faceți aplicația să asculte doar local, de exemplu cu app.listen(3000, '127.0.0.1'), apoi creați în Nginx un bloc server pentru domeniu:
server {
listen 80;
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Reîncărcați Nginx și obțineți certificatul cu Certbot în modul automat, exact ca în articolul Certbot cu Sectigo ACME pe Apache și Nginx. Certbot adaugă partea HTTPS în acest bloc și reînnoiește singur certificatul.
Varianta 2: Node.js cu propriul server HTTPS
Exemplul presupune o aplicație Express în /home/appuser/myapp, rulată sub PM2 de utilizatorul obișnuit (non-root) appuser. acme.sh se instalează sub același utilizator, ca sarcina lui de reînnoire să repornească procesul PM2 corect.
Pasul 1. Permiteți Node.js să folosească porturile 80 și 443
Porturile sub 1024 sunt rezervate pentru root. În loc să rulați aplicația ca root, dați fișierului executabil Node.js doar această permisiune:
sudo setcap 'cap_net_bind_service=+ep' "$(readlink -f "$(which node)")"
Repetați comanda după fiecare actualizare Node.js, pentru că noul executabil nu păstrează permisiunea. Deschideți porturile 80 și 443 în firewall-ul serverului.
Pasul 2. Serviți prin HTTP dosarul de validare
Pentru primul certificat, aplicația trebuie să răspundă pe portul 80 și să servească fișierele din public/.well-known/acme-challenge/. Express ascunde dosarele al căror nume începe cu punct, așa că montați acel dosar explicit:
const express = require('express');
const http = require('http');
const path = require('path');
const webroot = path.join(__dirname, 'public');
const app = express();
app.use('/.well-known/acme-challenge',
express.static(path.join(webroot, '.well-known', 'acme-challenge')));
app.use(express.static(webroot));
http.createServer(app).listen(80);
Pasul 3. Porniți aplicația cu PM2
sudo npm install -g pm2
pm2 start server.js --name myapp
pm2 startup systemd
pm2 save
pm2 startup afișează o comandă care începe cu sudo; copiați-o și rulați-o, ca aplicația să pornească din nou după o repornire a serverului. pm2 save memorează lista curentă de procese.
Pasul 4. Instalați acme.sh și înregistrați contul
Ca utilizator appuser:
curl https://get.acme.sh | sh -s email=admin@example.com
source ~/.bashrc
acme.sh --register-account --server https://acme.sectigo.com/v2/DV \
--eab-kid YOUR_EAB_MAC_ID --eab-hmac-key YOUR_EAB_MAC_KEY
Valorile le găsiți în contul de client, la Sectigo ACME Certificate-as-a-Service.
Pasul 5. Emiteți certificatul și dați-l aplicației
acme.sh --issue --server https://acme.sectigo.com/v2/DV \
-d example.com -d www.example.com \
-w /home/appuser/myapp/public
mkdir -p /home/appuser/myapp/cert
chmod 700 /home/appuser/myapp/cert
acme.sh --install-cert -d example.com \
--key-file /home/appuser/myapp/cert/privkey.pem \
--fullchain-file /home/appuser/myapp/cert/fullchain.pem \
--reloadcmd "pm2 restart myapp"
Dosarul de după -w trebuie să fie același pe care îl servește aplicația la pasul 2. acme.sh scrie cheia privată cu permisiuni restrânse; țineți dosarul cert în afara lui public și în afara depozitului Git.
Pasul 6. Treceți aplicația pe HTTPS
Înlocuiți server.js cu o variantă care păstrează validarea pe HTTP, trimite restul cererilor pe HTTPS și încarcă certificatul:
const express = require('express');
const fs = require('fs');
const http = require('http');
const https = require('https');
const path = require('path');
const webroot = path.join(__dirname, 'public');
const certDir = path.join(__dirname, 'cert');
// aplicația, servită prin HTTPS
const app = express();
app.use(express.static(webroot));
app.get('/', function (req, res) {
res.send('Hello over HTTPS');
});
// portul 80: validarea ACME rămâne pe HTTP, restul se redirecționează
const plain = express();
plain.use('/.well-known/acme-challenge',
express.static(path.join(webroot, '.well-known', 'acme-challenge')));
plain.use(function (req, res) {
res.redirect(301, 'https://' + req.hostname + req.originalUrl);
});
http.createServer(plain).listen(80);
https.createServer({
key: fs.readFileSync(path.join(certDir, 'privkey.pem')),
cert: fs.readFileSync(path.join(certDir, 'fullchain.pem'))
}, app).listen(443);
Aplicați modificarea cu pm2 restart myapp. Node.js citește certificatul doar la pornire; de aceea --reloadcmd de la pasul 5 repornește aplicația după fiecare reînnoire.
Pasul 7. Verificați HTTPS și reînnoirea
- Deschideți
https://example.comși verificați detaliile certificatului de sub lacăt. Deschideți șihttp://example.com: trebuie să vă ducă pe HTTPS. - Ca
appuser, rulațicrontab -lși căutați linia cuacme.sh --cron. - Rulați o dată
acme.sh --renew -d example.com --forceca să testați reînnoirea și repornirea automată, apoi verificațipm2 status.
Depanare
- Error: listen EACCES pe portul 80 sau 443. Lipsește permisiunea de la pasul 1, de obicei după o actualizare Node.js sau când Node.js e instalat printr-un manager de versiuni, în altă cale.
- Error: listen EADDRINUSE. Alt program, adesea Nginx sau Apache, ocupă deja portul. Opriți-l sau folosiți varianta 1.
- Validarea întoarce 404. Dosarul din
-wnu este cel servit la/.well-known/acme-challenge/sau o redirecționare spre HTTPS prinde cererea de validare înainte să existe certificatul. - Aplicația nu s-a repornit după reînnoire. acme.sh și PM2 rulează sub utilizatori diferiți. Instalați-le pe amândouă sub același utilizator sau puneți comanda corectă în
--reloadcmd. - Domain not found la rularea
--renewsau--install-cert. Adăugați--ecc, deoarece versiunile actuale de acme.sh emit implicit certificate ECC.
Dacă vă blocați, deschideți un tichet cu comanda, rezultatul ei și jurnalul PM2 (pm2 logs myapp), fără EAB MAC Key.