diff --git a/03-bis-hestiacp.md b/03-bis-hestiacp.md index 6d5302f..4553801 100644 --- a/03-bis-hestiacp.md +++ b/03-bis-hestiacp.md @@ -1,5 +1,11 @@ # 3-bis. HestiaCP (Alternative a ISPConfig) +Le guide complet HestiaCP est maintenant dans le dossier [hestiacp/](hestiacp/README.md) : + +- `hestiacp/Guide-VPS-HestiaCP.pdf` : guide PDF generique, reutilisable pour tout VPS +- `hestiacp/chapitres/` : les sources Markdown (14 chapitres + annexe) +- `hestiacp/build/build.py` : generation d'un PDF personnalise (`--vars mon-vps.conf`) + ## Pourquoi HestiaCP ? | Aspect | ISPConfig | HestiaCP | @@ -8,48 +14,6 @@ | Performance | Plus lourd | Plus leger, plus rapide | | Multi-serveur | Oui | Non | | Mises a jour | Lentes | Frequentes | -| Communaute | Grande, ancienne | Plus petite, plus active | +| Permissions | clientX/webX, ACL | Un utilisateur Linux par client | -**Recommande pour un seul serveur.** - -## Installation - -```bash -wget https://raw.githubusercontent.com/hestiacp/hestiacp/release/install/hst-install.sh -bash hst-install.sh -``` - -Options recommandees : -- Apache + Nginx (Nginx en proxy) -- MariaDB -- Bind9 -- Exim + Dovecot + ClamAV + SpamAssassin -- Let's Encrypt - -## Configuration post-installation - -L'installeur configure automatiquement : -- Firewall (iptables) -- SSL Let's Encrypt pour le panel -- Bind9 pour DNS -- Mail complet - -## Acces - -``` -https://:8083 -``` - -## Documentation - -- Site : https://hestiacp.com/ -- Docs : https://docs.hestiacp.com/ -- GitHub : https://github.com/hestiacp/hestiacp - -## Differences avec ce guide - -Si vous utilisez HestiaCP, les sections DNS, Mail, et Sites Web seront differentes dans l'interface mais les concepts restent les memes : -- Creer une zone DNS avec les templates -- Creer un site web avec SSL Let's Encrypt -- Creer un domaine mail avec DKIM/DMARC -- Les services Docker (Portainer, Wireguard, etc.) restent identiques +**Recommande pour un seul serveur.** Commandes verifiees pour HestiaCP 1.10.4 (septembre 2026). diff --git a/hestiacp/Guide-VPS-HestiaCP-srv1.skglobalinstitute.com.pdf b/hestiacp/Guide-VPS-HestiaCP-srv1.skglobalinstitute.com.pdf new file mode 100644 index 0000000..5758462 Binary files /dev/null and b/hestiacp/Guide-VPS-HestiaCP-srv1.skglobalinstitute.com.pdf differ diff --git a/hestiacp/Guide-VPS-HestiaCP.pdf b/hestiacp/Guide-VPS-HestiaCP.pdf new file mode 100644 index 0000000..682b646 Binary files /dev/null and b/hestiacp/Guide-VPS-HestiaCP.pdf differ diff --git a/hestiacp/README.md b/hestiacp/README.md new file mode 100644 index 0000000..87443b6 --- /dev/null +++ b/hestiacp/README.md @@ -0,0 +1,34 @@ +# Guide VPS HestiaCP (PDF réutilisable) + +Guide pas à pas pour installer un serveur d'hébergement complet avec **HestiaCP** sur un VPS Ubuntu 24.04 : web, DNS, mail, SSL, sécurité, Docker, supervision et sauvegardes. + +Commandes vérifiées le 10 septembre 2026 pour **HestiaCP 1.10.4**, contre le code source officiel (en-têtes `# options:` des scripts `bin/v-*`). + +## Fichiers + +| Fichier | Rôle | +|---|---| +| `Guide-VPS-HestiaCP.pdf` | Version générique, avec les variables ``, ``… | +| `Guide-VPS-HestiaCP-.pdf` | Version personnalisée pour un VPS donné | +| `chapitres/*.md` | Contenu (un fichier par chapitre, variables `{{IP}}`…) | +| `vps.conf.exemple` | Modèle de fichier de variables | +| `skglobalinstitute.conf` | Variables du VPS skglobalinstitute.com | +| `build/` | Générateur (`build.py`), style, polices, `meta.conf` | + +## Générer le PDF + +```bash +sudo apt install -y python3-venv libpango-1.0-0 libpangoft2-1.0-0 +python3 -m venv ~/.venvs/guide +~/.venvs/guide/bin/pip install weasyprint markdown pygments + +~/.venvs/guide/bin/python build/build.py # générique +~/.venvs/guide/bin/python build/build.py --vars mon-vps.conf # personnalisé +``` + +## Syntaxe des chapitres + +- `# Titre` : un chapitre ; `## Titre` : une étape numérotée avec case à cocher. +- `{{VARIABLE}}` : une variable (liste dans `build/build.py`, `VARIABLES`). +- Encadrés : `> [!NOTE]`, `> [!TIP]`, `> [!WARNING]`, `> [!DANGER]`, `> [!CHECK]`, avec un titre optionnel après le type. +- Un fichier dont le nom commence par `99` est affiché comme annexe. diff --git a/hestiacp/build/build.py b/hestiacp/build/build.py new file mode 100644 index 0000000..72c8f73 --- /dev/null +++ b/hestiacp/build/build.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python3 +"""Génère le guide PDF HestiaCP à partir des chapitres Markdown. + +Usage : + python3 build/build.py # PDF générique (variables , ...) + python3 build/build.py --vars mon-vps.conf # PDF personnalisé avec les valeurs du VPS + python3 build/build.py --html # garde aussi le HTML intermédiaire (debug) + +Dépendances : pip install weasyprint markdown pygments +""" +import argparse +import datetime +import html +import re +import sys +from pathlib import Path + +import markdown +from weasyprint import HTML + +ROOT = Path(__file__).resolve().parent.parent +BUILD = ROOT / "build" +CHAPTERS = ROOT / "chapitres" + +# Variables du guide : clé -> (description, exemple) +VARIABLES = { + "IP": ("Adresse IPv4 publique du VPS", "203.0.113.10"), + "IPV6": ("Adresse IPv6 publique du VPS", "2001:db8::10"), + "DOMAINE": ("Domaine principal du serveur", "exemple.com"), + "FQDN": ("Nom complet du serveur (hostname)", "srv1.exemple.com"), + "HOST": ("Nom court du serveur", "srv1"), + "USER": ("Utilisateur Linux sudo (SSH)", "jean"), + "ADMIN": ("Administrateur du panel HestiaCP", "hadmin"), + "EMAIL": ("E-mail de l'administrateur", "admin@exemple.com"), + "SSH_PORT": ("Port SSH personnalisé", "50022"), + "PANEL_PORT": ("Port du panel HestiaCP", "8083"), + "CLIENT": ("Utilisateur Hestia qui héberge les projets", "client1"), + "SITE": ("Domaine d'un projet hébergé", "monprojet.com"), + "BACKUP_HOST": ("Serveur de sauvegarde distant (SFTP)", "backup.exemple.net"), + "BACKUP_USER": ("Utilisateur SFTP de la sauvegarde", "sauvegarde"), + "BACKUP_PORT": ("Port SSH du serveur de sauvegarde", "22"), +} + +TOKEN = "ZZV{}ZZ" +TOKEN_RE = re.compile(r"ZZV([A-Z0-9_]+?)ZZ") +PLACEHOLDER_RE = re.compile(r"\{\{([A-Z0-9_]+)\}\}") + +CALLOUTS = { + "NOTE": ("note", "À savoir"), + "TIP": ("tip", "Astuce"), + "WARNING": ("warning", "Attention"), + "DANGER": ("danger", "Danger"), + "CHECK": ("check", "Vérification"), +} + + +def load_vars(path): + values = {} + for line in Path(path).read_text(encoding="utf-8").splitlines(): + line = line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + key, val = line.split("=", 1) + values[key.strip().upper()] = val.strip().strip('"').strip("'") + return values + + +def protect_placeholders(text): + def repl(m): + key = m.group(1) + if key not in VARIABLES: + sys.exit(f"Variable inconnue dans le guide : {{{{{key}}}}}") + return TOKEN.format(key) + + return PLACEHOLDER_RE.sub(repl, text) + + +def render_placeholders(html_text, values): + def repl(m): + key = m.group(1) + if key in values and values[key]: + return f'{html.escape(values[key])}' + return f'<{key}>' + + return TOKEN_RE.sub(repl, html_text) + + +def convert_callouts(text): + """> [!WARNING] Titre optionnel -> bloc
.""" + out, lines, i = [], text.split("\n"), 0 + while i < len(lines): + m = re.match(r"^>\s*\[!(\w+)\]\s*(.*)$", lines[i]) + if m and m.group(1).upper() in CALLOUTS: + kind, label = CALLOUTS[m.group(1).upper()] + title = m.group(2).strip() or label + body = [] + i += 1 + while i < len(lines) and lines[i].startswith(">"): + body.append(re.sub(r"^>\s?", "", lines[i])) + i += 1 + out.append(f'
') + out.append(f'

{html.escape(title)}

\n') + out.extend(body) + out.append("
\n") + continue + out.append(lines[i]) + i += 1 + return "\n".join(out) + + +def slug(text, prefix): + s = re.sub(r"<[^>]+>", "", text) + s = re.sub(r"[^\w]+", "-", s.lower(), flags=re.UNICODE).strip("-") + return f"{prefix}-{s}"[:80] + + +def build_chapter(md_text, chap_no, label): + md_text = convert_callouts(protect_placeholders(md_text)) + body = markdown.markdown( + md_text, + extensions=["fenced_code", "tables", "attr_list", "md_in_html", "codehilite", "sane_lists"], + extension_configs={"codehilite": {"guess_lang": False, "css_class": "code"}}, + ) + toc = [] + step_counter = {"n": 0} + + def h1(m): + title = m.group(1) + cid = f"chap-{chap_no}" + toc.append(("chap", cid, title, "A" if label == "Annexe" else chap_no)) + return (f'

{label}' + f'{title}

') + + def h2(m): + title = m.group(1) + step_counter["n"] += 1 + sid = slug(title, f"s{chap_no}-{step_counter['n']}") + toc.append(("step", sid, title, chap_no)) + return f'

{title}

' + + body = re.sub(r"

(.*?)

", h1, body, count=1) + body = re.sub(r"

(.*?)

", h2, body) + return f'
{body}
', toc + + +def cover(values, version, date): + personal = bool(values) + subtitle = values.get("FQDN") if personal else "Guide réutilisable pour tout VPS Ubuntu 24.04" + return f""" +
+
+

Guide d'installation pas à pas

+

Serveur VPS
avec HestiaCP

+

{html.escape(subtitle)}

+
+
+
PanelHestiaCP {version}
+
SystèmeUbuntu 24.04 LTS
+
PileNginx · Apache · PHP-FPM · MariaDB · Exim · Dovecot · Bind
+
Commandes vérifiées le{date}
+
+

Web · DNS · Mail · SSL · Sécurité · Docker · Supervision · Sauvegardes

+
""" + + +def vars_page(values): + rows = [] + for key, (desc, example) in VARIABLES.items(): + val = values.get(key, "") + cell = f'{html.escape(val)}' if val else '' + rows.append(f"<{key}>{desc}" + f"{example}{cell}") + intro = ("Ce PDF a été généré avec les valeurs de ce serveur." + if values else + "Remplissez la dernière colonne avant de commencer. Dans tout le guide, remplacez chaque " + "<VARIABLE> par votre valeur. Pour obtenir un PDF déjà " + "personnalisé, voir l'annexe « Régénérer ce guide ».") + return f""" +
+

Fiche du serveur

+

{intro}

+ + {''.join(rows)}
VariableSignificationExempleVotre valeur
+
+

Conventions

+
    +
  • Les commandes se copient telles quelles (pas d'invite $). Les lignes grises qui commencent par # sont des commentaires.
  • +
  • Chapitres 1 à 3 : connecté en root. À partir du chapitre 4 : avec votre utilisateur <USER> et sudo.
  • +
  • Chaque étape a une case à cocher : avancez dans l'ordre, une étape à la fois.
  • +
  • Les encadrés Vérification indiquent le résultat attendu avant de passer à la suite.
  • +
+
+
""" + + +def toc_page(entries): + items = [] + for kind, anchor, title, chap in entries: + title_txt = re.sub(r"<[^>]+>", "", title) + if kind == "chap": + items.append(f'
  • {chap}{title_txt}
  • ') + else: + items.append(f'
  • {title_txt}
  • ') + return f'

    Sommaire

      {"".join(items)}
    ' + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--vars", help="fichier de variables (voir vps.conf.exemple)") + ap.add_argument("--out", help="chemin du PDF de sortie") + ap.add_argument("--html", action="store_true", help="écrire aussi le HTML intermédiaire") + args = ap.parse_args() + + values = load_vars(args.vars) if args.vars else {} + meta = load_vars(BUILD / "meta.conf") + version, date = meta.get("HESTIA_VERSION", "?"), meta.get("VERIFIED_ON", datetime.date.today().isoformat()) + + chapters, toc = [], [] + for no, path in enumerate(sorted(CHAPTERS.glob("*.md")), start=1): + label = "Annexe" if path.name.startswith("99") else f"Chapitre {no}" + html_chap, entries = build_chapter(path.read_text(encoding="utf-8"), no, label) + chapters.append(html_chap) + toc.extend(entries) + + body = cover(values, version, date) + vars_page(values) + toc_page(toc) + "".join(chapters) + body = render_placeholders(body, values) + doc_title = "Guide VPS HestiaCP" + (f" — {values['FQDN']}" if values.get("FQDN") else "") + page = f""" +{html.escape(doc_title)} +{body}""" + + if args.out: + out = Path(args.out) + elif values.get("FQDN"): + out = ROOT / f"Guide-VPS-HestiaCP-{values['FQDN']}.pdf" + else: + out = ROOT / "Guide-VPS-HestiaCP.pdf" + if args.html: + (BUILD / "guide.html").write_text(page, encoding="utf-8") + HTML(string=page, base_url=str(BUILD)).write_pdf(out) + print(f"PDF généré : {out}") + + +if __name__ == "__main__": + main() diff --git a/hestiacp/build/fonts/Inter-400.ttf b/hestiacp/build/fonts/Inter-400.ttf new file mode 100644 index 0000000..399a6e0 Binary files /dev/null and b/hestiacp/build/fonts/Inter-400.ttf differ diff --git a/hestiacp/build/fonts/Inter-500.ttf b/hestiacp/build/fonts/Inter-500.ttf new file mode 100644 index 0000000..4a57a1a Binary files /dev/null and b/hestiacp/build/fonts/Inter-500.ttf differ diff --git a/hestiacp/build/fonts/Inter-600.ttf b/hestiacp/build/fonts/Inter-600.ttf new file mode 100644 index 0000000..67fda28 Binary files /dev/null and b/hestiacp/build/fonts/Inter-600.ttf differ diff --git a/hestiacp/build/fonts/Inter-700.ttf b/hestiacp/build/fonts/Inter-700.ttf new file mode 100644 index 0000000..9c2f47d Binary files /dev/null and b/hestiacp/build/fonts/Inter-700.ttf differ diff --git a/hestiacp/build/fonts/Inter-800.ttf b/hestiacp/build/fonts/Inter-800.ttf new file mode 100644 index 0000000..8a9a1bc Binary files /dev/null and b/hestiacp/build/fonts/Inter-800.ttf differ diff --git a/hestiacp/build/fonts/JetBrainsMono-400.ttf b/hestiacp/build/fonts/JetBrainsMono-400.ttf new file mode 100644 index 0000000..129e882 Binary files /dev/null and b/hestiacp/build/fonts/JetBrainsMono-400.ttf differ diff --git a/hestiacp/build/fonts/JetBrainsMono-600.ttf b/hestiacp/build/fonts/JetBrainsMono-600.ttf new file mode 100644 index 0000000..003757e Binary files /dev/null and b/hestiacp/build/fonts/JetBrainsMono-600.ttf differ diff --git a/hestiacp/build/meta.conf b/hestiacp/build/meta.conf new file mode 100644 index 0000000..43cf83d --- /dev/null +++ b/hestiacp/build/meta.conf @@ -0,0 +1,2 @@ +HESTIA_VERSION=1.10.4 +VERIFIED_ON=10 septembre 2026 diff --git a/hestiacp/build/style.css b/hestiacp/build/style.css new file mode 100644 index 0000000..1f664e6 --- /dev/null +++ b/hestiacp/build/style.css @@ -0,0 +1,254 @@ +/* ---------- Polices (embarquées, licence OFL) ---------- */ +@font-face { font-family: "Inter"; font-weight: 400; src: url(fonts/Inter-400.ttf); } +@font-face { font-family: "Inter"; font-weight: 500; src: url(fonts/Inter-500.ttf); } +@font-face { font-family: "Inter"; font-weight: 600; src: url(fonts/Inter-600.ttf); } +@font-face { font-family: "Inter"; font-weight: 700; src: url(fonts/Inter-700.ttf); } +@font-face { font-family: "Inter"; font-weight: 800; src: url(fonts/Inter-800.ttf); } +@font-face { font-family: "JetBrains Mono"; font-weight: 400; src: url(fonts/JetBrainsMono-400.ttf); } +@font-face { font-family: "JetBrains Mono"; font-weight: 600; src: url(fonts/JetBrainsMono-600.ttf); } + +:root { + --ink: #0f172a; + --text: #1e293b; + --muted: #64748b; + --line: #e2e8f0; + --surface: #f8fafc; + --accent: #0f766e; + --accent-2: #14b8a6; + --accent-soft: #ccfbf1; + --code-bg: #0d1117; + --var: #b45309; + --var-bg: #fef3c7; +} + +/* ---------- Pages ---------- */ +@page { + size: A4; + margin: 22mm 18mm 20mm 18mm; + @top-left { + content: string(chapter); + font: 500 7.5pt "Inter", sans-serif; + color: #94a3b8; + vertical-align: bottom; + padding-bottom: 4mm; + } + @top-right { + content: "HestiaCP · Ubuntu 24.04"; + font: 500 7.5pt "Inter", sans-serif; + color: #94a3b8; + vertical-align: bottom; + padding-bottom: 4mm; + } + @bottom-right { + content: counter(page); + font: 700 8pt "Inter", sans-serif; + color: var(--accent); + } + @bottom-left { + content: "Guide VPS HestiaCP"; + font: 400 7.5pt "Inter", sans-serif; + color: #94a3b8; + } +} +@page cover { margin: 0; @top-left { content: none; } @top-right { content: none; } @bottom-right { content: none; } @bottom-left { content: none; } } +@page front { @top-left { content: none; } @top-right { content: none; } } + +html { font-family: "Inter", "DejaVu Sans", sans-serif; font-size: 9.4pt; line-height: 1.55; color: var(--text); } +body { margin: 0; } + +/* ---------- Couverture ---------- */ +.cover { + page: cover; + height: 297mm; + box-sizing: border-box; + padding: 34mm 22mm 20mm; + background: var(--ink); + background-image: + radial-gradient(circle at 88% 12%, rgba(20,184,166,.35) 0, rgba(20,184,166,0) 38%), + radial-gradient(circle at 5% 95%, rgba(20,184,166,.18) 0, rgba(20,184,166,0) 40%); + color: #e2e8f0; + position: relative; +} +.kicker { text-transform: uppercase; letter-spacing: .18em; font-size: 8.5pt; font-weight: 600; color: var(--accent-2); margin: 0 0 8mm; } +.cover-title { font-size: 40pt; line-height: 1.05; font-weight: 800; color: #fff; margin: 0; letter-spacing: -.02em; } +.cover-title span { color: var(--accent-2); } +.cover-sub { font-size: 13pt; color: #94a3b8; margin: 7mm 0 0; font-weight: 500; } +.cover-meta { position: absolute; left: 22mm; right: 22mm; bottom: 42mm; border-top: 1px solid #334155; padding-top: 7mm; } +.cover-meta div { display: flex; justify-content: space-between; padding: 2.2mm 0; border-bottom: 1px solid #1e293b; font-size: 9.5pt; } +.cover-meta span { color: #94a3b8; } +.cover-meta strong { color: #f1f5f9; font-weight: 600; text-align: right; max-width: 60%; } +.cover-foot { position: absolute; left: 22mm; bottom: 20mm; margin: 0; font-size: 8.5pt; letter-spacing: .06em; color: var(--accent-2); } + +/* ---------- Fiche serveur & sommaire ---------- */ +.vars-page, .toc { page: front; break-before: page; } +h1.plain { font-size: 22pt; font-weight: 800; color: var(--ink); margin: 0 0 5mm; letter-spacing: -.01em; } +table.vars td.val { border-bottom: 1px dashed #94a3b8; min-width: 45mm; width: 32%; } +table.vars td.val.filled { font-family: "JetBrains Mono", monospace; font-size: 8pt; color: var(--var); border-bottom: 1px solid var(--line); } +table.vars td.ex { color: var(--muted); font-size: 8pt; } +td:first-child { white-space: nowrap; } +td:first-child code { white-space: nowrap; overflow-wrap: normal; } +.legend { margin-top: 8mm; background: var(--surface); border: 1px solid var(--line); border-radius: 3mm; padding: 3mm 5mm; } +.legend p { margin: 1mm 0; } +.legend ul { margin: 1mm 0 1mm; padding-left: 5mm; } + +.toc ol { list-style: none; padding: 0; margin: 0; } +.toc a { color: var(--text); text-decoration: none; } +.toc a::after { content: leader(dotted) target-counter(attr(href), page); color: var(--muted); } +.toc-chap { margin-top: 3.2mm; font-weight: 700; font-size: 10pt; color: var(--ink); break-after: avoid; } +.toc-chap a { color: var(--ink); } +.toc-chap .n { display: inline-block; width: 8mm; color: var(--accent); } +.toc-step { padding-left: 8mm; font-size: 8.6pt; line-height: 1.7; } + +/* ---------- Chapitres ---------- */ +.chapter { break-before: page; } +h1.chapter-title { margin: 0 0 7mm; padding-bottom: 5mm; border-bottom: 2px solid var(--ink); } +.chap-no { display: block; text-transform: uppercase; letter-spacing: .16em; font-size: 8pt; font-weight: 700; color: var(--accent); margin-bottom: 1.5mm; } +.chap-name { display: block; font-size: 21pt; font-weight: 800; color: var(--ink); line-height: 1.15; letter-spacing: -.01em; string-set: chapter content(); } + +body { counter-reset: step; } +h2.step { + counter-increment: step; + font-size: 12.5pt; font-weight: 700; color: var(--ink); + margin: 8mm 0 2.5mm; padding-top: 1mm; + break-after: avoid; +} +h2.step::before { + content: "Étape " counter(step); + display: inline-block; + font-size: 7.5pt; font-weight: 700; letter-spacing: .04em; + color: #fff; background: var(--accent); + border-radius: 1.2mm; padding: .6mm 2mm; margin-right: 2.5mm; + vertical-align: 1.2pt; +} +h2.step .box { float: right; width: 3.6mm; height: 3.6mm; border: 1.3px solid #94a3b8; border-radius: .8mm; margin-top: 1mm; } +.toc-step a::before { content: "Étape " target-counter(attr(href), step) " · "; color: var(--muted); font-weight: 500; } + +h3 { font-size: 10.2pt; font-weight: 700; color: var(--ink); margin: 5mm 0 1.5mm; break-after: avoid; } +h4 { font-size: 9.4pt; font-weight: 600; color: var(--accent); margin: 4mm 0 1mm; break-after: avoid; } +p { margin: 1.6mm 0 2.4mm; orphans: 3; widows: 3; } +ul, ol { margin: 1.5mm 0 2.5mm; padding-left: 5.5mm; } +li { margin: .6mm 0; } +a { color: var(--accent); text-decoration: none; } +strong { color: var(--ink); font-weight: 600; } +hr { border: 0; border-top: 1px solid var(--line); margin: 6mm 0; } + +/* ---------- Code ---------- */ +code, pre, .var { font-variant-ligatures: none; font-feature-settings: "liga" 0, "calt" 0; } +code { font-family: "JetBrains Mono", "DejaVu Sans Mono", monospace; font-size: 8pt; } +p code, li code, td code { + background: #eef2f7; color: #0f172a; border-radius: 1mm; padding: .2mm 1.1mm; + overflow-wrap: anywhere; +} +.code, pre { + background: var(--code-bg); border-radius: 2.2mm; + margin: 2mm 0 3.5mm; padding: 3mm 4mm; + break-inside: avoid; +} +.code pre { margin: 0; padding: 0; background: none; } +pre, .code pre { + color: #e6edf3; font-family: "JetBrains Mono", "DejaVu Sans Mono", monospace; font-size: 7.9pt; line-height: 1.55; + white-space: pre-wrap; overflow-wrap: anywhere; +} +pre code { font-size: inherit; background: none; padding: 0; color: inherit; } + +/* ---------- Variables ---------- */ +.var { font-family: "JetBrains Mono", monospace; font-size: .92em; color: var(--var); background: var(--var-bg); border-radius: .8mm; padding: 0 .6mm; } +pre .var, .code .var { background: rgba(251,191,36,.14); color: #fbbf24; } +.var.filled { background: none; padding: 0; } +pre .var.filled, .code .var.filled { background: none; color: #fcd34d; } + +/* ---------- Tableaux ---------- */ +table { width: 100%; border-collapse: collapse; margin: 2.5mm 0 4mm; font-size: 8.6pt; break-inside: auto; } +th { text-align: left; font-weight: 700; color: var(--ink); background: var(--surface); border-bottom: 1.5px solid var(--ink); padding: 1.8mm 2.2mm; } +td { border-bottom: 1px solid var(--line); padding: 1.6mm 2.2mm; vertical-align: top; } +tr { break-inside: avoid; } + +/* ---------- Encadrés ---------- */ +.callout { border-left: 3px solid; border-radius: 0 2mm 2mm 0; padding: 2.2mm 4mm 1.5mm; margin: 3mm 0 4mm; break-inside: avoid; } +.callout p { margin: .8mm 0 1.6mm; } +.callout .callout-title { font-weight: 700; font-size: 8pt; text-transform: uppercase; letter-spacing: .08em; margin: 0 0 1mm; } +.callout .code { margin: 1.5mm 0 2mm; } +.callout.note { background: #eff6ff; border-color: #3b82f6; } .callout.note .callout-title { color: #1d4ed8; } +.callout.tip { background: #f0fdfa; border-color: var(--accent-2); } .callout.tip .callout-title { color: var(--accent); } +.callout.warning { background: #fffbeb; border-color: #f59e0b; } .callout.warning .callout-title { color: #b45309; } +.callout.danger { background: #fef2f2; border-color: #ef4444; } .callout.danger .callout-title { color: #b91c1c; } +.callout.check { background: #f0fdf4; border-color: #22c55e; } .callout.check .callout-title { color: #15803d; } + +/* ---------- Coloration syntaxique (Pygments github-dark) ---------- */ +.code .c { color: #8B949E; font-style: italic } /* Comment */ +.code .err { color: #F85149 } /* Error */ +.code .esc { color: #E6EDF3 } /* Escape */ +.code .g { color: #E6EDF3 } /* Generic */ +.code .k { color: #FF7B72 } /* Keyword */ +.code .l { color: #A5D6FF } /* Literal */ +.code .n { color: #E6EDF3 } /* Name */ +.code .o { color: #FF7B72; font-weight: bold } /* Operator */ +.code .x { color: #E6EDF3 } /* Other */ +.code .p { color: #E6EDF3 } /* Punctuation */ +.code .ch { color: #8B949E; font-style: italic } /* Comment.Hashbang */ +.code .cm { color: #8B949E; font-style: italic } /* Comment.Multiline */ +.code .cp { color: #8B949E; font-weight: bold; font-style: italic } /* Comment.Preproc */ +.code .cpf { color: #8B949E; font-style: italic } /* Comment.PreprocFile */ +.code .c1 { color: #8B949E; font-style: italic } /* Comment.Single */ +.code .cs { color: #8B949E; font-weight: bold; font-style: italic } /* Comment.Special */ +.code .ge { color: #E6EDF3; font-style: italic } /* Generic.Emph */ +.code .ges { color: #E6EDF3; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.code .gr { color: #FFA198 } /* Generic.Error */ +.code .gh { color: #79C0FF; font-weight: bold } /* Generic.Heading */ +.code .go { color: #8B949E } /* Generic.Output */ +.code .gp { color: #8B949E } /* Generic.Prompt */ +.code .gs { color: #E6EDF3; font-weight: bold } /* Generic.Strong */ +.code .gu { color: #79C0FF } /* Generic.Subheading */ +.code .gt { color: #FF7B72 } /* Generic.Traceback */ +.code .g-Underline { color: #E6EDF3; text-decoration: underline } /* Generic.Underline */ +.code .kc { color: #79C0FF } /* Keyword.Constant */ +.code .kd { color: #FF7B72 } /* Keyword.Declaration */ +.code .kn { color: #FF7B72 } /* Keyword.Namespace */ +.code .kp { color: #79C0FF } /* Keyword.Pseudo */ +.code .kr { color: #FF7B72 } /* Keyword.Reserved */ +.code .kt { color: #FF7B72 } /* Keyword.Type */ +.code .ld { color: #79C0FF } /* Literal.Date */ +.code .m { color: #A5D6FF } /* Literal.Number */ +.code .s { color: #A5D6FF } /* Literal.String */ +.code .na { color: #E6EDF3 } /* Name.Attribute */ +.code .nb { color: #E6EDF3 } /* Name.Builtin */ +.code .nc { color: #F0883E; font-weight: bold } /* Name.Class */ +.code .no { color: #79C0FF; font-weight: bold } /* Name.Constant */ +.code .nd { color: #D2A8FF; font-weight: bold } /* Name.Decorator */ +.code .ni { color: #FFA657 } /* Name.Entity */ +.code .ne { color: #F0883E; font-weight: bold } /* Name.Exception */ +.code .nf { color: #D2A8FF; font-weight: bold } /* Name.Function */ +.code .nl { color: #79C0FF; font-weight: bold } /* Name.Label */ +.code .nn { color: #FF7B72 } /* Name.Namespace */ +.code .nx { color: #E6EDF3 } /* Name.Other */ +.code .py { color: #79C0FF } /* Name.Property */ +.code .nt { color: #7EE787 } /* Name.Tag */ +.code .nv { color: #79C0FF } /* Name.Variable */ +.code .ow { color: #FF7B72; font-weight: bold } /* Operator.Word */ +.code .pm { color: #E6EDF3 } /* Punctuation.Marker */ +.code .w { color: #6E7681 } /* Text.Whitespace */ +.code .mb { color: #A5D6FF } /* Literal.Number.Bin */ +.code .mf { color: #A5D6FF } /* Literal.Number.Float */ +.code .mh { color: #A5D6FF } /* Literal.Number.Hex */ +.code .mi { color: #A5D6FF } /* Literal.Number.Integer */ +.code .mo { color: #A5D6FF } /* Literal.Number.Oct */ +.code .sa { color: #79C0FF } /* Literal.String.Affix */ +.code .sb { color: #A5D6FF } /* Literal.String.Backtick */ +.code .sc { color: #A5D6FF } /* Literal.String.Char */ +.code .dl { color: #79C0FF } /* Literal.String.Delimiter */ +.code .sd { color: #A5D6FF } /* Literal.String.Doc */ +.code .s2 { color: #A5D6FF } /* Literal.String.Double */ +.code .se { color: #79C0FF } /* Literal.String.Escape */ +.code .sh { color: #79C0FF } /* Literal.String.Heredoc */ +.code .si { color: #A5D6FF } /* Literal.String.Interpol */ +.code .sx { color: #A5D6FF } /* Literal.String.Other */ +.code .sr { color: #79C0FF } /* Literal.String.Regex */ +.code .s1 { color: #A5D6FF } /* Literal.String.Single */ +.code .ss { color: #A5D6FF } /* Literal.String.Symbol */ +.code .bp { color: #E6EDF3 } /* Name.Builtin.Pseudo */ +.code .fm { color: #D2A8FF; font-weight: bold } /* Name.Function.Magic */ +.code .vc { color: #79C0FF } /* Name.Variable.Class */ +.code .vg { color: #79C0FF } /* Name.Variable.Global */ +.code .vi { color: #79C0FF } /* Name.Variable.Instance */ +.code .vm { color: #79C0FF } /* Name.Variable.Magic */ +.code .il { color: #A5D6FF } /* Literal.Number.Integer.Long */ diff --git a/hestiacp/chapitres/01-avant-de-commencer.md b/hestiacp/chapitres/01-avant-de-commencer.md new file mode 100644 index 0000000..d4dd7f1 --- /dev/null +++ b/hestiacp/chapitres/01-avant-de-commencer.md @@ -0,0 +1,73 @@ +# Avant de commencer + +Ce guide installe un serveur d'hébergement complet sur un VPS neuf, piloté par **HestiaCP**. Il couvre les sites web, le DNS, la messagerie, le SSL, la sécurité, Docker, la supervision et les sauvegardes. Il remplace ISPConfig pour un serveur unique : HestiaCP est plus léger, son interface est moderne et il est mis à jour régulièrement. + +| Aspect | ISPConfig | HestiaCP | +|---|---|---| +| Interface | Ancienne | Moderne, en français | +| Poids | Plus lourd | Léger (Nginx + Apache + PHP-FPM) | +| Multi-serveur | Oui | Non (un serveur = un panel) | +| Mises à jour | Lentes | Fréquentes, automatiques par défaut | +| Permissions des sites | Groupes `clientX` / `webX`, ACL | Un utilisateur Linux par client : plus simple | + +## Choisir le système d'exploitation + +HestiaCP **1.10.4** accepte Debian 12 et 13, ainsi qu'Ubuntu 22.04, 24.04 et 26.04 LTS, en 64 bits (x86_64 ou ARM64). + +**Choisissez Ubuntu 24.04 LTS.** Elle est supportée par HestiaCP depuis la version 1.9.0 (février 2025) et est donc bien rodée. Le support d'Ubuntu 26.04 n'est arrivé qu'en août 2026 (version 1.10.0). + +> [!DANGER] Un système neuf, obligatoirement +> HestiaCP doit s'installer sur un système **fraîchement installé**. N'installez ni Apache, ni Nginx, ni MariaDB, ni Postfix avant lui. Si le VPS a déjà servi, réinstallez-le depuis le panel de l'hébergeur. + +## Commander et réinstaller le VPS + +Ressources conseillées par HestiaCP : 4 cœurs, 4 Go de RAM, 40 Go SSD. Avec moins de 3 Go de RAM, l'installeur désactive tout seul l'antivirus ClamAV. + +1. Commandez le VPS. Exemple : Contabo *Cloud VPS 6* avec 6 cœurs, 12 Go de RAM et 200 Go de disque. +2. Choisissez l'image **Ubuntu 24.04**, sans panel ni application préinstallés. +3. Définissez un mot de passe root solide, et ajoutez votre clé SSH publique si l'interface le propose. +4. Notez l'**IPv4** et l'**IPv6** du VPS dans la fiche du serveur, au début de ce guide. + +## Choisir le nom du serveur + +Le serveur a besoin d'un nom complet (FQDN) du type `nom.domaine.tld` : il faut **au moins deux points**. C'est aussi l'adresse du panel. + +Nous utilisons {{FQDN}}. + +> [!WARNING] N'utilisez pas « mail. » ni « www. » comme nom de serveur +> HestiaCP crée lui-même `mail.` et `webmail.` pour chaque domaine de messagerie, et `www.` pour chaque site. Un nom de serveur neutre évite les conflits de certificats. Exemples : `srv1.domaine.com`, `panel.domaine.com`. + +## Préparer le DNS chez le registrar (OVH) + +Au départ, le DNS du domaine reste chez OVH. Il faut juste que le nom du serveur pointe déjà vers le VPS, sinon le certificat SSL du panel ne pourra pas être créé. Le basculement du DNS vers le serveur se fait au chapitre 5. + +Dans l'espace client OVH, allez dans **Noms de domaine › {{DOMAINE}} › Zone DNS › Ajouter une entrée** et ajoutez : + +| Type | Sous-domaine | Cible | +|---|---|---| +| A | {{HOST}} | {{IP}} | + +> [!WARNING] Pas d'enregistrement AAAA (IPv6) +> HestiaCP ne sert les sites et les mails qu'en **IPv4**. Si un enregistrement AAAA existe, Let's Encrypt essaie l'IPv6 en priorité et la validation échoue. Supprimez tout AAAA existant pour {{HOST}}, `@` et `www`. + +> [!CHECK] +> Depuis votre ordinateur, `nslookup {{FQDN}}` doit répondre {{IP}}. La propagation prend de quelques minutes à une heure. + +## Configurer le reverse DNS (PTR) chez l'hébergeur + +Gmail et Outlook rejettent ou classent en spam les mails d'un serveur dont l'IP ne « répond » pas au bon nom. Chez Contabo, allez dans **Customer Control Panel › Reverse DNS Management**. Pour l'**IPv4** {{IP}}, indiquez : + +```text +{{FQDN}} +``` + +> [!NOTE] +> Le PTR doit correspondre exactement au nom du serveur, car c'est le nom qu'Exim annonce en envoyant les mails. Exim n'utilise que l'IPv4 sous HestiaCP ; le PTR de l'IPv6 est facultatif, et vous pouvez y mettre le même nom. + +## Se connecter au VPS + +Les chapitres 2 et 3 se font connecté en **root**. Depuis votre ordinateur : + +```bash +ssh root@{{IP}} +``` diff --git a/hestiacp/chapitres/02-preparation-systeme.md b/hestiacp/chapitres/02-preparation-systeme.md new file mode 100644 index 0000000..762a9bb --- /dev/null +++ b/hestiacp/chapitres/02-preparation-systeme.md @@ -0,0 +1,173 @@ +# Préparer le système + +On part d'un Ubuntu 24.04 neuf, connecté en **root**. Ce chapitre prépare le terrain sans rien installer qui gênerait HestiaCP. + +## Mettre le système à jour + +```bash +apt update && apt full-upgrade -y +apt autoremove -y +``` + +Si un nouveau noyau a été installé, redémarrez puis reconnectez-vous : + +```bash +[ -f /var/run/reboot-required ] && reboot +``` + +## Définir le nom du serveur + +```bash +hostnamectl set-hostname {{FQDN}} +``` + +Ajoutez le nom du serveur dans `/etc/hosts` : + +```bash +cat >> /etc/hosts << 'EOF' +{{IP}} {{FQDN}} {{HOST}} +{{IPV6}} {{FQDN}} {{HOST}} +EOF +``` + +Les images Contabo utilisent **cloud-init**, qui peut remettre l'ancien nom du serveur au redémarrage. Bloquez ce comportement : + +```bash +[ -d /etc/cloud/cloud.cfg.d ] && echo 'preserve_hostname: true' > /etc/cloud/cloud.cfg.d/99-preserve-hostname.cfg +``` + +> [!CHECK] +> `hostname -f` doit afficher {{FQDN}}. + +## Régler le fuseau horaire + +Adaptez la zone à votre pays. La liste complète s'obtient avec `timedatectl list-timezones`. + +```bash +timedatectl set-timezone Africa/Abidjan +timedatectl +``` + +## Supprimer UFW + +HestiaCP gère son propre pare-feu (iptables + Fail2ban). L'installeur considère UFW comme un paquet en conflit : supprimez-le avant. + +```bash +ufw disable +apt purge -y ufw +``` + +## Vérifier les noms réservés + +HestiaCP crée un compte Linux pour son administrateur. Le nom {{ADMIN}} ne doit exister **ni comme utilisateur, ni comme groupe**, et doit être différent de {{USER}}. + +```bash +getent passwd {{ADMIN}}; getent group {{ADMIN}} +``` + +> [!CHECK] +> Aucune ligne ne doit s'afficher. + +> [!NOTE] Pourquoi pas « admin » ? +> Sur les images Ubuntu de Contabo, un **groupe `admin` existe déjà**, et l'installeur refuse un nom déjà pris. Choisissez un nom personnel (ex. `hadmin`, `skadmin`) : c'est aussi un nom de moins à deviner pour les robots. + +## Éviter le blocage IPv6 pendant l'installation + +Sur certains VPS, l'installeur se bloque en téléchargeant ses paquets par IPv6. Dites au système de préférer l'IPv4 pour les connexions sortantes (l'IPv6 reste active) : + +```bash +sed -i 's|^#precedence ::ffff:0:0/96 100|precedence ::ffff:0:0/96 100|' /etc/gai.conf +grep "^precedence" /etc/gai.conf +``` + +> [!CHECK] +> La commande `grep` affiche `precedence ::ffff:0:0/96 100`. + +## Vérifier la configuration réseau (Netplan) + +L'installeur s'attend à trouver la configuration réseau dans `/etc/netplan`. + +```bash +ls /etc/netplan/ +ip -6 addr show scope global +``` + +> [!CHECK] +> Au moins un fichier `.yaml` dans `/etc/netplan/`, et votre IPv6 {{IPV6}} dans la sortie de `ip`. + +## Ajouter un fichier d'échange (swap) + +HestiaCP ne crée un swap que sur les très petits serveurs. Un swap de 4 Go évite qu'un pic de mémoire ne fasse planter MariaDB ou ClamAV. + +```bash +swapon --show +``` + +Si rien ne s'affiche : + +```bash +fallocate -l 4G /swapfile +chmod 600 /swapfile +mkswap /swapfile +swapon /swapfile +echo '/swapfile none swap sw 0 0' >> /etc/fstab +echo 'vm.swappiness = 10' > /etc/sysctl.d/99-swap.conf +sysctl --system > /dev/null +``` + +> [!CHECK] +> `free -h` affiche une ligne `Swap:` de 4,0 Gi. + +## Installer les outils de base + +Uniquement des outils sans conflit avec HestiaCP : ni serveur web, ni base de données, ni serveur mail. + +```bash +apt install -y curl wget git micro htop tmux unzip ca-certificates gnupg lsb-release dnsutils +``` + +Faites de `micro` l'éditeur par défaut : + +```bash +update-alternatives --install /usr/bin/editor editor /usr/bin/micro 60 +update-alternatives --set editor /usr/bin/micro +``` + +## Créer votre utilisateur sudo + +Vous ne vous connecterez plus en root après le chapitre 4. Créez votre compte d'administration système : + +```bash +adduser {{USER}} +usermod -aG sudo {{USER}} +``` + +Autorisez `sudo` sans mot de passe pour ce compte. C'est pratique, mais cela suppose une connexion par **clé SSH uniquement** (chapitre 4). + +```bash +echo '{{USER}} ALL=(ALL:ALL) NOPASSWD: ALL' > /etc/sudoers.d/010-{{USER}} +chmod 440 /etc/sudoers.d/010-{{USER}} +visudo -c +``` + +> [!CHECK] +> `visudo -c` répond `parsed OK` pour chaque fichier. + +## Installer votre clé SSH + +**Sur votre ordinateur** (pas sur le serveur), créez une clé si vous n'en avez pas : + +```bash +ssh-keygen -t ed25519 -C "{{USER}}@{{HOST}}" +``` + +Puis envoyez-la au serveur : + +```bash +ssh-copy-id {{USER}}@{{IP}} +``` + +Sous Windows sans `ssh-copy-id`, copiez le contenu de `C:\Users\\.ssh\id_ed25519.pub` dans le fichier `/home/{{USER}}/.ssh/authorized_keys` du serveur. + +> [!CHECK] +> Dans un **nouveau** terminal, `ssh {{USER}}@{{IP}}` vous connecte sans mot de passe, puis `sudo whoami` répond `root`. Gardez la session root ouverte tant que ce test n'est pas réussi. diff --git a/hestiacp/chapitres/03-installation-hestiacp.md b/hestiacp/chapitres/03-installation-hestiacp.md new file mode 100644 index 0000000..8bd9faf --- /dev/null +++ b/hestiacp/chapitres/03-installation-hestiacp.md @@ -0,0 +1,122 @@ +# Installer HestiaCP + +Toujours connecté en **root**. L'installation dure 15 à 30 minutes. + +## Ouvrir une session tmux + +Si la connexion SSH coupe pendant l'installation, `tmux` la garde en vie sur le serveur. + +```bash +tmux new -s hestia +``` + +> [!TIP] +> Si vous êtes déconnecté, reconnectez-vous puis tapez `tmux attach -t hestia` pour retrouver l'installation en cours. + +## Télécharger l'installeur officiel + +```bash +cd /root +wget https://raw.githubusercontent.com/hestiacp/hestiacp/release/install/hst-install.sh +``` + +> [!NOTE] +> En cas d'erreur SSL : `apt-get update && apt-get install -y ca-certificates`, puis relancez le `wget`. +> Le site officiel propose aussi un générateur de commande : . + +## Lancer l'installation + +Chaque option est expliquée juste après la commande. Copiez la commande **en entier** : + +```bash +bash hst-install.sh \ + --hostname {{FQDN}} \ + --email {{EMAIL}} \ + --username {{ADMIN}} \ + --port {{PANEL_PORT}} \ + --lang fr \ + --apache yes --phpfpm yes --multiphp '8.3,8.4' \ + --vsftpd no --proftpd no \ + --named yes \ + --mysql yes --postgresql no \ + --exim yes --dovecot yes --sieve yes \ + --clamav yes --spamassassin yes \ + --iptables yes --fail2ban yes \ + --quota no --resourcelimit no --webterminal no \ + --api no +``` + +| Option | Choix | Pourquoi | +|---|---|---| +| `--hostname` | {{FQDN}} | Nom du serveur et adresse du panel | +| `--email` | {{EMAIL}} | Reçoit les identifiants et les alertes. Prenez une adresse **externe** (Gmail…) : le mail du serveur n'existe pas encore | +| `--username` | {{ADMIN}} | Administrateur du panel. Pas `admin` (voir chapitre 2) | +| `--port` | {{PANEL_PORT}} | Port du panel (8083 par défaut) | +| `--apache`
    `--phpfpm` | yes | Nginx en façade + Apache derrière : les `.htaccess` (Symfony, WordPress) fonctionnent | +| `--multiphp` | 8.3, 8.4 | Versions de PHP installées ; la plus haute (8.4) devient celle par défaut. D'autres versions peuvent s'ajouter plus tard | +| `--vsftpd`
    `--proftpd` | no | Pas de FTP (non chiffré) : on déploie par SSH/SFTP et git | +| `--named` | yes | Serveur DNS Bind : vos domaines ont leurs propres serveurs de noms | +| `--mysql` | yes | MariaDB 11.8 | +| `--exim`
    `--dovecot`
    `--sieve` | yes | Envoi, réception et filtres de messagerie | +| `--clamav`
    `--spamassassin` | yes | Antivirus et antispam (demandent au moins 3 Go de RAM) | +| `--iptables`
    `--fail2ban` | yes | Pare-feu et bannissement automatique des attaques | +| `--resourcelimit` | no | Encore instable d'après les notes de version (1.9.0) | +| `--webterminal` | no | Pas de terminal dans le navigateur : on utilise SSH | +| `--api` | no | L'API distante ne sert pas ici ; moins de surface d'attaque | + +Déroulement : + +1. L'installeur affiche le récapitulatif des composants. Répondez `y` pour continuer. +2. Il demande le **mot de passe administrateur**. Tapez un mot de passe long et unique (au moins 16 caractères) et notez-le dans votre gestionnaire de mots de passe. Il s'affiche en clair à l'écran : c'est normal. +3. Si l'installeur signale des paquets en conflit, répondez `y` pour les supprimer. Sur un système neuf préparé au chapitre 2, il ne devrait pas y en avoir. + +> [!WARNING] +> Ne touchez à rien pendant l'installation. À la fin, l'installeur affiche « Congratulations! » avec l'adresse du panel, le nom d'utilisateur et le mot de passe, et envoie le même message à {{EMAIL}}. + +## Redémarrer le serveur + +L'installeur demande d'appuyer sur une touche, puis redémarre. Sinon : + +```bash +reboot +``` + +Au premier démarrage, une tâche programmée par HestiaCP demande automatiquement un certificat **Let's Encrypt** pour {{FQDN}}. Cela ne marche que si l'enregistrement DNS du chapitre 1 est en place. + +> [!NOTE] +> Le journal complet de l'installation est dans `/root/hst_install_backups/`, et les journaux Let's Encrypt dans `/var/log/hestia/`. + +## Se connecter au panel + +Ouvrez dans votre navigateur : + +```text +https://{{FQDN}}:{{PANEL_PORT}} +``` + +Connectez-vous avec {{ADMIN}} et votre mot de passe. + +> [!CHECK] +> Le cadenas du navigateur indique un certificat valide (émis par Let's Encrypt). Si le navigateur signale un certificat non valide, passez à l'étape suivante. + +## Réparer le certificat du panel (si besoin) + +Uniquement si le certificat n'est pas valide. Reconnectez-vous en SSH en root, vérifiez que le nom du serveur pointe bien vers le VPS, puis demandez le certificat : + +```bash +dig +short {{FQDN}} +/usr/local/hestia/bin/v-change-sys-hostname {{FQDN}} +/usr/local/hestia/bin/v-add-letsencrypt-host +``` + +> [!CHECK] +> `dig` doit répondre {{IP}}, et `v-add-letsencrypt-host` ne doit afficher aucune erreur. Rechargez ensuite le panel dans le navigateur. + +## Découvrir l'interface + +- **Utilisateurs** : les comptes d'hébergement. Chaque utilisateur Hestia est un utilisateur Linux avec son dossier `/home/`. +- **Web**, **DNS**, **Messagerie**, **Bases de données**, **Tâches cron**, **Sauvegardes** : les ressources de l'utilisateur sélectionné. +- **Roue dentée (Paramètres du serveur)** : services, pare-feu, mises à jour, configuration. Le bouton **Moniteur de tâches** affiche les graphiques de charge. + +> [!DANGER] N'hébergez rien sous le compte administrateur +> La documentation officielle recommande de créer un utilisateur dédié pour vos sites, domaines et mails (chapitre 5). Le compte {{ADMIN}} sert uniquement à administrer. diff --git a/hestiacp/chapitres/04-securisation.md b/hestiacp/chapitres/04-securisation.md new file mode 100644 index 0000000..1a17759 --- /dev/null +++ b/hestiacp/chapitres/04-securisation.md @@ -0,0 +1,204 @@ +# Sécuriser le serveur + +À partir de maintenant, on travaille avec votre utilisateur {{USER}} et `sudo`, plus jamais en root direct. HestiaCP a déjà mis en place un pare-feu (iptables) et Fail2ban ; ce chapitre complète ce qu'il ne fait pas. + +## Se connecter avec votre utilisateur + +Depuis votre ordinateur : + +```bash +ssh {{USER}}@{{IP}} +``` + +## Rendre les commandes HestiaCP utilisables avec sudo + +Les commandes HestiaCP (`v-…`) sont dans `/usr/local/hestia/bin`, mais `sudo` ne cherche pas dans ce dossier. Sans ce réglage, `sudo v-list-users` répond « command not found ». + +```bash +echo 'Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:/usr/local/hestia/bin"' | sudo tee /etc/sudoers.d/020-hestia-path +sudo chmod 440 /etc/sudoers.d/020-hestia-path +sudo visudo -c +``` + +> [!CHECK] +> `sudo v-list-users` affiche la liste des utilisateurs, dont {{ADMIN}}. + +## Activer la double authentification du panel + +Dans le panel, connecté en {{ADMIN}} : + +1. Cliquez sur votre nom d'utilisateur en haut à droite (**Modifier l'utilisateur**). +2. Cochez **Activer l'authentification à deux facteurs** et enregistrez. +3. Scannez le QR code avec une application TOTP (Aegis, Google Authenticator, 2FAS…). +4. **Notez le code de récupération** dans votre gestionnaire de mots de passe. + +> [!TIP] +> Si vous perdez votre téléphone, vous pouvez désactiver la double authentification en SSH avec `sudo v-delete-user-2fa {{ADMIN}}`. + +## Fermer l'IPv6 entrante + +Le pare-feu de HestiaCP ne gère **que l'IPv4**. En IPv6, le SSH et le panel (qui écoute aussi sur `[::]:{{PANEL_PORT}}`) seraient joignables sans pare-feu ni Fail2ban. Comme HestiaCP ne sert rien en IPv6, on bloque tout le trafic IPv6 entrant, sauf les réponses et l'ICMPv6, indispensable au fonctionnement de l'IPv6. Le serveur peut toujours sortir en IPv6. + +Créez le script de pare-feu IPv6 : + +```bash +sudo tee /usr/local/sbin/ip6-firewall.sh > /dev/null << 'EOF' +#!/bin/sh +# Pare-feu IPv6 : HestiaCP ne filtre que l'IPv4. +# Bloque tout l'entrant IPv6 sauf loopback, réponses et ICMPv6. +ip6tables -P INPUT ACCEPT +ip6tables -F INPUT +ip6tables -A INPUT -i lo -j ACCEPT +ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT +ip6tables -A INPUT -p ipv6-icmp -j ACCEPT +ip6tables -P INPUT DROP +EOF +sudo chmod 750 /usr/local/sbin/ip6-firewall.sh +``` + +Puis le service qui l'applique à chaque démarrage : + +```bash +sudo tee /etc/systemd/system/ip6-firewall.service > /dev/null << 'EOF' +[Unit] +Description=Pare-feu IPv6 (entrant bloque) +DefaultDependencies=no +Wants=network-pre.target +Before=network-pre.target +After=local-fs.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/local/sbin/ip6-firewall.sh + +[Install] +WantedBy=multi-user.target +EOF +sudo systemctl daemon-reload +sudo systemctl enable --now ip6-firewall.service +``` + +> [!CHECK] +> `sudo ip6tables -S INPUT` commence par `-P INPUT DROP`. Depuis votre ordinateur, si vous avez l'IPv6, `ssh -6 {{USER}}@{{IPV6}}` ne doit **pas** répondre, alors que `ssh {{USER}}@{{IP}}` fonctionne toujours. + +## Changer le port SSH (1/2) : ouvrir le nouveau port + +On procède en deux temps pour ne jamais se retrouver bloqué dehors : d'abord SSH écoute sur **les deux ports**, puis on ferme le port 22. + +Le fichier commence par `00-` pour passer avant les fichiers de cloud-init : pour un même réglage, OpenSSH garde la première valeur lue. + +```bash +sudo tee /etc/ssh/sshd_config.d/00-securite.conf > /dev/null << 'EOF' +Port 22 +Port {{SSH_PORT}} +EOF +sudo sshd -t && echo "Configuration SSH valide" +``` + +Ubuntu 24.04 démarre SSH à la demande via `ssh.socket` : il faut recharger systemd pour que le nouveau port soit pris en compte. + +```bash +systemctl is-active ssh.socket +sudo systemctl daemon-reload +sudo systemctl restart ssh.socket +``` + +> [!NOTE] +> Si `systemctl is-active ssh.socket` répond `inactive`, votre image utilise le service classique : remplacez les deux dernières lignes par `sudo systemctl restart ssh`. + +Mettez à jour le pare-feu HestiaCP. Il détecte tout seul les ports SSH et met à jour sa règle « SSH » et Fail2ban : + +```bash +sudo v-update-firewall +sudo v-list-sys-sshd-port +sudo v-list-firewall | grep SSH +``` + +> [!CHECK] +> `v-list-sys-sshd-port` affiche `22` et {{SSH_PORT}} ; la règle SSH du pare-feu contient `22,{{SSH_PORT}}`. Dans un **nouveau terminal**, `ssh -p {{SSH_PORT}} {{USER}}@{{IP}}` fonctionne. + +## Changer le port SSH (2/2) : fermer le port 22 et durcir + +Seulement après avoir réussi la connexion sur le port {{SSH_PORT}}. On ferme le port 22, on interdit la connexion root et on n'accepte plus que les clés SSH : + +```bash +sudo tee /etc/ssh/sshd_config.d/00-securite.conf > /dev/null << 'EOF' +Port {{SSH_PORT}} +PermitRootLogin no +PasswordAuthentication no +KbdInteractiveAuthentication no +PubkeyAuthentication yes +EOF +sudo sshd -t && sudo systemctl daemon-reload && sudo systemctl restart ssh.socket +sudo v-update-firewall +``` + +> [!DANGER] Gardez votre session ouverte +> Ne fermez pas le terminal actuel. Testez dans un nouveau terminal : `ssh -p {{SSH_PORT}} {{USER}}@{{IP}}` doit fonctionner, et `ssh root@{{IP}} -p {{SSH_PORT}}` doit être refusé. En cas de problème, la console VNC du panel Contabo permet toujours de se connecter. + +> [!CHECK] +> `sudo sshd -T | grep -E "^(port|permitrootlogin|passwordauthentication)"` affiche `port {{SSH_PORT}}`, `permitrootlogin no` et `passwordauthentication no`. + +Sur votre ordinateur, ajoutez un raccourci dans `~/.ssh/config` : + +```text +Host {{HOST}} + HostName {{IP}} + Port {{SSH_PORT}} + User {{USER}} +``` + +Il suffira ensuite de taper `ssh {{HOST}}`. + +## Vérifier le pare-feu et Fail2ban + +```bash +sudo v-list-firewall +sudo fail2ban-client status +``` + +Ports ouverts par HestiaCP : ping, {{PANEL_PORT}} (panel), 80/443 (web), 25/465/587 (SMTP), 143/993 (IMAP), 110/995 (POP3), 53 TCP/UDP (DNS) et {{SSH_PORT}} (SSH). + +Prisons Fail2ban actives : `ssh-iptables`, `exim-iptables`, `dovecot-iptables`, `hestia-iptables` (panel), `phpmyadmin-auth` et `recidive`. Une IP bannie 5 fois en une journée est bloquée 10 jours sur tous les ports. + +> [!TIP] Débannir une IP (la vôtre, par exemple) +> `sudo v-list-firewall-ban` affiche les IP bannies. `sudo v-delete-firewall-ban ALL` débannit une IP partout. + +> [!WARNING] Ne réactivez jamais UFW +> HestiaCP et UFW se marchent dessus. Toute règle se gère dans le panel (**roue dentée › Pare-feu**) ou avec `v-add-firewall-rule`. + +## Cacher phpMyAdmin + +phpMyAdmin est accessible sur **tous** vos domaines à l'adresse `/phpmyadmin`, que les robots testent en permanence. Remplacez ce chemin par un nom difficile à deviner : + +```bash +sudo v-change-sys-db-alias pma gestion-bdd-7f3k9 +``` + +phpMyAdmin sera alors accessible sur `https://{{FQDN}}/gestion-bdd-7f3k9/`. Choisissez votre propre suffixe. + +## Vérifier les mises à jour automatiques + +Deux mécanismes complémentaires : + +- **unattended-upgrades** (Ubuntu) installe chaque jour les correctifs de sécurité du système ; +- **HestiaCP** se met à jour lui-même chaque nuit (tâche `v-update-sys-hestia-all`). + +```bash +systemctl is-enabled unattended-upgrades +cat /etc/apt/apt.conf.d/20auto-upgrades +sudo v-list-sys-hestia-autoupdate +``` + +> [!CHECK] +> `enabled` ; les deux lignes `APT::Periodic` valent `"1"` ; l'autoupdate HestiaCP est `Enabled`. + +Si `20auto-upgrades` n'existe pas : + +```bash +sudo dpkg-reconfigure -plow unattended-upgrades +``` + +> [!NOTE] Mises à jour manuelles +> Pour PHP, Nginx et MariaDB, qui viennent de dépôts externes, lancez de temps en temps `sudo apt update && sudo apt upgrade`. Si apt demande quoi faire d'un fichier de configuration modifié, répondez toujours **garder la version actuelle** (N). diff --git a/hestiacp/chapitres/05-utilisateur-hebergement.md b/hestiacp/chapitres/05-utilisateur-hebergement.md new file mode 100644 index 0000000..d7c10e7 --- /dev/null +++ b/hestiacp/chapitres/05-utilisateur-hebergement.md @@ -0,0 +1,84 @@ +# Créer l'utilisateur d'hébergement + +Dans HestiaCP, **chaque utilisateur du panel est un vrai utilisateur Linux**. PHP-FPM, les tâches cron et la connexion SSH de ses sites tournent sous ce même compte. + +Tous les fichiers d'un site appartiennent donc à un seul utilisateur. Les problèmes de droits connus sous ISPConfig (`webX:clientY`, ACL, `chmod 777` sur le cache Symfony) disparaissent. + +| Élément | Emplacement | +|---|---| +| Fichiers d'un site | `/home/{{CLIENT}}/web//public_html/` | +| Fichiers privés (hors web) | `/home/{{CLIENT}}/web//private/` | +| Journaux du site | `/home/{{CLIENT}}/web//logs/` | +| Boîtes mail | `/home/{{CLIENT}}/mail//` | +| Sauvegardes | `/backup/` | + +## Créer un forfait « projets » + +Un **forfait** (*package*) définit les limites et les réglages par défaut d'un utilisateur. Le forfait `default` interdit le SSH et ne garde qu'une sauvegarde : on en crée un plus adapté à partir de lui. + +```bash +sudo cp /usr/local/hestia/data/packages/default.pkg /tmp/projets.pkg +sudo sed -i "s/^SHELL=.*/SHELL='bash'/; s/^BACKUPS=.*/BACKUPS='7'/" /tmp/projets.pkg +sudo v-add-user-package /tmp/projets.pkg projets +sudo rm /tmp/projets.pkg +sudo v-list-user-package projets +``` + +> [!CHECK] +> Dans la sortie, la ligne `SHELL` indique `bash`, la ligne `BACKUPS` indique `7` et la ligne `NS` indique `ns1.{{DOMAINE}},ns2.{{DOMAINE}}`. + +> [!NOTE] +> `BACKUPS='7'` garde 7 sauvegardes quotidiennes locales. Sur un petit disque, réduisez ce nombre. Le forfait se modifie aussi dans le panel : **roue dentée › Forfaits**. + +## Créer l'utilisateur {{CLIENT}} + +Choisissez un mot de passe fort, que vous notez dans votre gestionnaire de mots de passe. L'espace avant la commande l'empêche d'être enregistré dans l'historique du shell : + +```bash + sudo v-add-user {{CLIENT}} 'MOT_DE_PASSE_FORT' {{EMAIL}} projets +``` + +> [!WARNING] +> Depuis HestiaCP 1.10, un nom qui existe déjà comme utilisateur Linux est refusé. {{CLIENT}} doit donc être différent de {{USER}} et de {{ADMIN}}. + +## Vérifier les serveurs de noms de l'utilisateur + +Les zones DNS créées par {{CLIENT}} utiliseront ces serveurs de noms : + +```bash +sudo v-change-user-ns {{CLIENT}} ns1.{{DOMAINE}} ns2.{{DOMAINE}} +sudo v-list-user {{CLIENT}} json | grep -E '"(PACKAGE|SHELL|NS)"' +``` + +> [!CHECK] +> `"PACKAGE": "projets"`, `"NS": "ns1.{{DOMAINE}},ns2.{{DOMAINE}}"`, `"SHELL": "bash"`. + +## Autoriser votre clé SSH pour {{CLIENT}} + +Pour déployer vos projets, vous vous connecterez directement en {{CLIENT}}. Ajoutez votre clé publique, celle du fichier `~/.ssh/id_ed25519.pub` de votre ordinateur : + +```bash +sudo v-add-user-ssh-key {{CLIENT}} "$(cat /home/{{USER}}/.ssh/authorized_keys | head -1)" +``` + +> [!CHECK] +> Depuis votre ordinateur, `ssh -p {{SSH_PORT}} {{CLIENT}}@{{IP}}` ouvre une session dans `/home/{{CLIENT}}`. + +## Installer Composer pour {{CLIENT}} + +```bash +sudo v-add-user-composer {{CLIENT}} +``` + +Cette commande installe Composer 2 dans `/home/{{CLIENT}}/.composer/` et crée l'alias `composer` pour cet utilisateur. + +## Choisir la version de PHP en ligne de commande + +Par défaut, la commande `php` utilise la version la plus récente installée. Pour que {{CLIENT}} utilise PHP 8.4 en SSH : + +```bash +sudo v-change-user-php-cli {{CLIENT}} 8.4 +``` + +> [!WARNING] Cron et scripts : chemin complet obligatoire +> Cette commande crée seulement un **alias** dans `~/.bash_aliases`, que les tâches cron et les scripts ne lisent pas. Dans ces cas, écrivez toujours le chemin complet : `/usr/bin/php8.4`. diff --git a/hestiacp/chapitres/06-dns.md b/hestiacp/chapitres/06-dns.md new file mode 100644 index 0000000..cdfc015 --- /dev/null +++ b/hestiacp/chapitres/06-dns.md @@ -0,0 +1,115 @@ +# Héberger son propre DNS + +Le serveur devient le serveur DNS de vos domaines. {{DOMAINE}} a ses propres serveurs de noms, `ns1.{{DOMAINE}}` et `ns2.{{DOMAINE}}`, déclarés chez le registrar par des **enregistrements glue**. Vos autres domaines, par exemple en `.ci`, utilisent ensuite ces mêmes serveurs de noms. + +> [!WARNING] L'ordre compte +> Créez toujours la **zone DNS avant le domaine mail** : HestiaCP n'ajoute la clé DKIM que dans une zone qui existe déjà. + +## Créer la zone du domaine principal + +La zone appartient à {{CLIENT}}, comme le site et les mails de {{DOMAINE}}. Le modèle `child-ns` est prévu pour un domaine qui sert lui-même de serveur de noms : il crée les enregistrements `ns1` et `ns2` qui pointent vers {{IP}}. + +```bash +sudo v-add-dns-domain {{CLIENT}} {{DOMAINE}} {{IP}} +sudo v-change-dns-domain-tpl {{CLIENT}} {{DOMAINE}} child-ns +``` + +> [!DANGER] +> Changer de modèle **efface les enregistrements ajoutés à la main**. Faites-le uniquement juste après la création, comme ici. + +Ajoutez l'enregistrement du nom du serveur, dont dépendent le panel et son certificat : + +```bash +sudo v-add-dns-record {{CLIENT}} {{DOMAINE}} {{HOST}} A {{IP}} +sudo v-list-dns-records {{CLIENT}} {{DOMAINE}} +``` + +> [!CHECK] +> La liste contient les NS `ns1.{{DOMAINE}}.` et `ns2.{{DOMAINE}}.`, les A `@`, `ns1`, `ns2`, `mail` et {{HOST}}, le CNAME `www`, le MX `mail.{{DOMAINE}}.`, le TXT SPF et le TXT `_dmarc`. + +## Tester la zone sur le serveur + +```bash +dig @{{IP}} {{DOMAINE}} SOA +short +dig @{{IP}} ns1.{{DOMAINE}} +short +dig @{{IP}} {{FQDN}} +short +``` + +> [!CHECK] +> La première commande affiche une ligne commençant par `ns1.{{DOMAINE}}.` ; les deux suivantes affichent {{IP}}. + +## Déclarer les glue records chez OVH + +Dans l'espace client OVH : **Web Cloud › Noms de domaine › {{DOMAINE}} › onglet GLUE › Ajouter**. + +| Nom d'hôte | Adresse IP | +|---|---| +| `ns1.{{DOMAINE}}` | {{IP}} | +| `ns2.{{DOMAINE}}` | {{IP}} | + +Ne mettez que l'IPv4 : l'IPv6 entrante est bloquée par le pare-feu (chapitre 4), donc une glue IPv6 rendrait le DNS injoignable pour une partie d'Internet. + +## Basculer les serveurs DNS chez OVH + +Toujours sur {{DOMAINE}} : **onglet Serveurs DNS › Modifier les serveurs DNS**. Remplacez les serveurs d'OVH par : + +```text +ns1.{{DOMAINE}} +ns2.{{DOMAINE}} +``` + +> [!NOTE] +> OVH vérifie que vos serveurs répondent avant d'accepter le changement : c'est pour cela que la zone doit exister avant. La propagation prend de quelques heures à 48 h. + +## Vérifier la délégation + +Depuis le serveur ou votre ordinateur : + +```bash +dig +trace NS {{DOMAINE}} | tail -n 8 +dig NS {{DOMAINE}} +short +``` + +> [!CHECK] +> La fin du `+trace` montre `ns1.{{DOMAINE}}` et `ns2.{{DOMAINE}}` avec la réponse de {{IP}}. Un contrôle complet est possible sur . + +## Ajouter un autre domaine (ex. un .ci) + +Pour un domaine de projet {{SITE}} enregistré chez un autre registrar, utilisez le modèle par défaut, qui inclut aussi les enregistrements SRV de la messagerie. Ses serveurs de noms seront ceux de l'utilisateur, c'est-à-dire `ns1.{{DOMAINE}}` et `ns2.{{DOMAINE}}`. + +```bash +sudo v-add-dns-domain {{CLIENT}} {{SITE}} {{IP}} +sudo v-list-dns-records {{CLIENT}} {{SITE}} +``` + +Chez le registrar de {{SITE}}, remplacez ensuite les serveurs DNS par `ns1.{{DOMAINE}}` et `ns2.{{DOMAINE}}`. **Pas de glue** ici : elle ne sert que pour le domaine qui porte les serveurs de noms. + +> [!NOTE] +> Si {{SITE}} est le même domaine que {{DOMAINE}}, cette étape est déjà faite : passez à la suite. + +## Ajouter un sous-domaine dans le DNS + +HestiaCP **ne crée pas** l'enregistrement DNS quand vous ajoutez un site en sous-domaine. Ajoutez-le vous-même avant de créer le site, puis vérifiez : + +```bash +sudo v-add-dns-record {{CLIENT}} {{DOMAINE}} app A {{IP}} +dig @{{IP}} app.{{DOMAINE}} +short +``` + +## Activer DNSSEC (facultatif) + +DNSSEC signe la zone pour empêcher la falsification des réponses DNS. HestiaCP ne l'autorise qu'en mode `hestia-zone`, un réglage de cluster sans effet sur un serveur seul. + +```bash +sudo v-change-sys-config-value DNS_CLUSTER_SYSTEM hestia-zone +sudo v-change-dns-domain-dnssec {{CLIENT}} {{DOMAINE}} yes +sudo v-list-dnssec-public-key {{CLIENT}} {{DOMAINE}} shell +``` + +La dernière commande affiche l'enregistrement **DS**, avec le key tag, le flag 257, l'algorithme 13 et la clé publique. Chez OVH : **{{DOMAINE}} › onglet Enregistrements DS › Modifier**, puis copiez-y ces valeurs. + +> [!DANGER] Désactiver DNSSEC dans le bon ordre +> Pour désactiver DNSSEC plus tard, supprimez **d'abord** le DS chez le registrar et attendez 48 h. Sinon le domaine devient injoignable. Sur un ancien serveur, OVH a déjà refusé un DS avec l'erreur « Error while checking DNSKEYS » : dans ce cas, réessayez plus tard ou contactez le support. + +> [!CHECK] +> affiche une chaîne de confiance verte pour {{DOMAINE}}. diff --git a/hestiacp/chapitres/07-sites-web.md b/hestiacp/chapitres/07-sites-web.md new file mode 100644 index 0000000..090e014 --- /dev/null +++ b/hestiacp/chapitres/07-sites-web.md @@ -0,0 +1,93 @@ +# Sites web et certificats SSL + +Chaque site passe par **Nginx** (en façade, pour les fichiers statiques, le SSL et le cache), puis **Apache** (qui lit les `.htaccess`), puis **PHP-FPM**. Le PHP-FPM tourne sous l'utilisateur {{CLIENT}}, avec un pool par domaine. + +> [!NOTE] Prérequis +> Le domaine doit déjà pointer vers le serveur (chapitre 6), sinon Let's Encrypt ne peut pas valider le certificat. Vérifiez avec `dig +short {{SITE}}` et `dig +short www.{{SITE}}` : les deux doivent répondre {{IP}}. + +## Créer le site + +```bash +sudo v-add-web-domain {{CLIENT}} {{SITE}} +``` + +L'alias `www.{{SITE}}` est ajouté automatiquement. Le site se trouve dans `/home/{{CLIENT}}/web/{{SITE}}/public_html/`, avec une page d'accueil HestiaCP par défaut. + +## Activer le certificat Let's Encrypt + +Indiquez explicitement l'alias `www`, sinon le certificat ne couvre que {{SITE}} : + +```bash +sudo v-add-letsencrypt-domain {{CLIENT}} {{SITE}} www.{{SITE}} +``` + +Forcez ensuite le HTTPS, et activez HSTS pour que les navigateurs refusent ensuite le HTTP : + +```bash +sudo v-add-web-domain-ssl-force {{CLIENT}} {{SITE}} +sudo v-add-web-domain-ssl-hsts {{CLIENT}} {{SITE}} +``` + +> [!CHECK] +> `curl -sI http://{{SITE}} | head -3` renvoie une redirection `301` vers `https://`, et `curl -sI https://www.{{SITE}} | head -1` renvoie `HTTP/2 200`. + +> [!TIP] +> Le renouvellement est automatique. Dans le panel, la même chose se fait en modifiant le domaine : **Activer le SSL pour ce domaine**, **Utiliser Let's Encrypt pour obtenir un certificat SSL**, **Activer la redirection automatique en HTTPS**. + +## Choisir la version de PHP du site + +Listez les versions disponibles, puis appliquez-en une au site : + +```bash +sudo v-list-sys-php +sudo v-list-web-templates-backend +sudo v-change-web-domain-backend-tpl {{CLIENT}} {{SITE}} PHP-8_4 +``` + +Pour installer une version supplémentaire plus tard (ex. 8.5) : + +```bash +sudo v-add-web-php 8.5 +``` + +> [!NOTE] Réglages PHP par site +> Pour modifier une valeur PHP d'un seul site (ex. `memory_limit`), créez un fichier `.user.ini` dans le dossier servi par le web (pour Symfony : `public_html/public/`) : +> +> ```ini +> memory_limit = 512M +> upload_max_filesize = 64M +> post_max_size = 64M +> ``` +> +> Il est pris en compte après environ 5 minutes. HestiaCP règle déjà `upload_max_filesize` et `post_max_size` à 100M. + +## Créer un site en sous-domaine + +Exemple avec `app.{{DOMAINE}}` : d'abord l'enregistrement DNS (chapitre 6), puis le site et son certificat. Pour un sous-domaine, pas d'alias `www` : + +```bash +sudo v-add-dns-record {{CLIENT}} {{DOMAINE}} app A {{IP}} +sudo v-add-web-domain {{CLIENT}} app.{{DOMAINE}} +sudo v-add-letsencrypt-domain {{CLIENT}} app.{{DOMAINE}} +sudo v-add-web-domain-ssl-force {{CLIENT}} app.{{DOMAINE}} +``` + +## Installer WordPress en un clic (facultatif) + +HestiaCP intègre un installeur d'applications : WordPress, Laravel, Symfony, Nextcloud, Joomla, Drupal, PrestaShop, entre autres. + +1. **Web** › survolez le domaine › **Installer une application**. +2. Choisissez **WordPress**, remplissez le formulaire (titre, compte administrateur WordPress, e-mail, version de PHP) et laissez cochée la création de la base de données. +3. Validez : la base est créée et WordPress est installé dans `public_html`. + +> [!TIP] +> Pour un WordPress, le modèle proxy **caching** de Nginx accélère nettement les pages publiques : `sudo v-change-web-domain-proxy-tpl {{CLIENT}} {{SITE}} caching`. + +## Consulter les journaux d'un site + +```bash +sudo tail -f /var/log/apache2/domains/{{SITE}}.error.log +sudo tail -f /var/log/nginx/domains/{{SITE}}.error.log +``` + +Ces journaux sont aussi visibles dans `/home/{{CLIENT}}/web/{{SITE}}/logs/` et dans le panel, via l'icône journal du domaine. diff --git a/hestiacp/chapitres/08-bases-de-donnees.md b/hestiacp/chapitres/08-bases-de-donnees.md new file mode 100644 index 0000000..494bb6c --- /dev/null +++ b/hestiacp/chapitres/08-bases-de-donnees.md @@ -0,0 +1,55 @@ +# Bases de données MariaDB + +HestiaCP installe **MariaDB 11.8**. Chaque base appartient à un utilisateur Hestia, et son nom comme celui de son utilisateur MariaDB sont **préfixés** par le nom de l'utilisateur Hestia. Le port 3306 reste fermé vers l'extérieur. + +## Créer une base pour un projet + +```bash + sudo v-add-database {{CLIENT}} app app 'MOT_DE_PASSE_BDD' +sudo v-list-databases {{CLIENT}} +``` + +> [!CHECK] +> La base `{{CLIENT}}_app` apparaît, avec l'utilisateur `{{CLIENT}}_app`. Ce sont ces noms complets qu'il faut utiliser dans l'application. + +Le jeu de caractères par défaut est `utf8mb4`. Dans une application, l'hôte est `127.0.0.1` et le port `3306`. + +## Accéder à phpMyAdmin + +Ouvrez `https://{{FQDN}}//` et connectez-vous avec l'utilisateur de la base (`{{CLIENT}}_app`) et son mot de passe. + +## Exporter une base + +```bash +sudo v-dump-database {{CLIENT}} {{CLIENT}}_app > ~/{{CLIENT}}_app.sql +``` + +Pour une version compressée, utilisez `sudo v-dump-database {{CLIENT}} {{CLIENT}}_app file zstd`. La commande affiche le chemin du fichier créé dans `/backup/`, qui est supprimé automatiquement au bout d'une heure. + +## Importer une base existante (migration) + +Depuis l'ancien serveur, exportez la base puis copiez-la sur le nouveau : + +```bash +# Sur l'ancien serveur +mysqldump --single-transaction --routines --triggers ancienne_base > ancienne_base.sql +scp -P {{SSH_PORT}} ancienne_base.sql {{USER}}@{{IP}}:~/ +``` + +Sur le nouveau serveur, importez-la dans la base créée plus haut : + +```bash +mariadb -u {{CLIENT}}_app -p {{CLIENT}}_app < ~/ancienne_base.sql +rm ~/ancienne_base.sql +``` + +> [!WARNING] +> Un export contient souvent des données personnelles. Supprimez les fichiers `.sql` une fois l'import terminé, sur les deux serveurs. + +## Changer le mot de passe d'une base + +```bash + sudo v-change-database-password {{CLIENT}} {{CLIENT}}_app 'NOUVEAU_MOT_DE_PASSE' +``` + +Pensez à mettre à jour le fichier `.env.local` de l'application ensuite. diff --git a/hestiacp/chapitres/09-messagerie.md b/hestiacp/chapitres/09-messagerie.md new file mode 100644 index 0000000..1236db9 --- /dev/null +++ b/hestiacp/chapitres/09-messagerie.md @@ -0,0 +1,157 @@ +# Messagerie + +La messagerie repose sur **Exim** (envoi et réception), **Dovecot** (IMAP/POP3), **SpamAssassin** et **ClamAV** (antispam et antivirus), et **Roundcube** (webmail). HestiaCP signe les mails avec **DKIM**. Les enregistrements **SPF** et **DMARC** viennent déjà du modèle DNS. + +> [!WARNING] Prérequis +> - La zone DNS du domaine existe (chapitre 6). Sinon la clé DKIM ne sera pas publiée. +> - Le PTR de {{IP}} vaut {{FQDN}} (chapitre 1). + +## Créer le domaine de messagerie + +Antispam, antivirus et DKIM (2048 bits) sont activés par défaut : + +```bash +sudo v-add-mail-domain {{CLIENT}} {{DOMAINE}} +sudo v-list-dns-records {{CLIENT}} {{DOMAINE}} | grep -E "_domainkey|_dmarc|spf" +``` + +> [!CHECK] +> Trois enregistrements TXT apparaissent : +> - `mail._domainkey` : `v=DKIM1; k=rsa; p=…` ; +> - `_dmarc` : `v=DMARC1; p=quarantine; pct=100` ; +> - `@` : `v=spf1 a mx ip4:{{IP}} -all`. + +## Activer le certificat SSL de la messagerie + +Ce certificat couvre `mail.{{DOMAINE}}` et `webmail.{{DOMAINE}}`. Exim et Dovecot le présentent automatiquement aux clients mail qui se connectent à `mail.{{DOMAINE}}`. + +```bash +sudo v-add-letsencrypt-domain {{CLIENT}} {{DOMAINE}} '' yes +``` + +> [!CHECK] +> `https://webmail.{{DOMAINE}}` affiche la page de connexion Roundcube avec un cadenas valide. + +## Créer les boîtes mail + +```bash + sudo v-add-mail-account {{CLIENT}} {{DOMAINE}} contact 'MOT_DE_PASSE_MAIL' + sudo v-add-mail-account {{CLIENT}} {{DOMAINE}} noreply 'MOT_DE_PASSE_NOREPLY' +sudo v-list-mail-accounts {{CLIENT}} {{DOMAINE}} +``` + +Ajouter un alias ou une redirection : + +```bash +sudo v-add-mail-account-alias {{CLIENT}} {{DOMAINE}} contact info +sudo v-add-mail-account-forward {{CLIENT}} {{DOMAINE}} contact adresse@externe.com +``` + +## Ajouter les enregistrements SRV (domaine principal) + +Les enregistrements SRV aident les logiciels de messagerie à se configurer seuls. Le modèle `child-ns` du domaine principal n'en contient pas ; les autres domaines, créés avec le modèle `default`, les ont déjà. + +```bash +sudo v-add-dns-record {{CLIENT}} {{DOMAINE}} _imaps._tcp SRV '0 993 mail.{{DOMAINE}}.' 1 +sudo v-add-dns-record {{CLIENT}} {{DOMAINE}} _submission._tcp SRV '0 587 mail.{{DOMAINE}}.' 1 +sudo v-add-dns-record {{CLIENT}} {{DOMAINE}} _submissions._tcp SRV '0 465 mail.{{DOMAINE}}.' 1 +``` + +## Configurer un logiciel de messagerie + +| Réglage | Valeur | +|---|---| +| Identifiant | adresse complète, ex. `contact@{{DOMAINE}}` | +| Serveur entrant (IMAP) | `mail.{{DOMAINE}}`, port **993**, SSL/TLS | +| Serveur sortant (SMTP) | `mail.{{DOMAINE}}`, port **465** SSL/TLS (ou 587 STARTTLS) | +| Authentification | mot de passe normal | +| Webmail | `https://webmail.{{DOMAINE}}` | + +## Tester l'envoi et la réception + +Vérifiez d'abord que le port 25 sortant n'est pas bloqué par l'hébergeur : + +```bash +nc -vz -w 5 aspmx.l.google.com 25 +``` + +> [!CHECK] +> `Connection to aspmx.l.google.com 25 port [tcp/smtp] succeeded!` + +Envoyez ensuite un mail depuis le webmail : + +1. vers l'adresse fournie par : visez **10/10** ; +2. vers une adresse Gmail : dans **Afficher l'original**, SPF, DKIM et DMARC doivent être **PASS**. + +Répondez aussi depuis Gmail pour tester la réception. + +## Utiliser Spamhaus avec une clé gratuite (recommandé) + +Spamhaus bloque les requêtes qui passent par les résolveurs DNS publics, ce qui rend son filtre antispam inopérant et peut fausser les résultats. La documentation HestiaCP recommande un compte gratuit **Spamhaus DQS** (), qui fournit une clé. + +```bash +sudo v-list-sys-mail-dnsbl +sudo v-delete-sys-mail-dnsbl zen.spamhaus.org +sudo v-add-sys-mail-dnsbl VOTRE_CLE.zen.dq.spamhaus.net +``` + +## Envoyer des mails depuis une application (Symfony…) + +**Méthode recommandée :** envoi authentifié avec une vraie boîte, par exemple `noreply`. Le certificat correspond au nom utilisé, et les limites d'envoi par compte s'appliquent. Dans `.env.local` : + +```text +MAILER_DSN=smtp://noreply%40{{DOMAINE}}:MOT_DE_PASSE_NOREPLY@mail.{{DOMAINE}}:587 +``` + +> [!NOTE] +> Dans le DSN, `@` s'écrit `%40`. Tout caractère spécial du mot de passe doit aussi être encodé : `%23` pour `#`, `%2F` pour `/`, `%3A` pour `:`. + +**Alternative sans mot de passe :** Exim accepte les mails envoyés depuis `127.0.0.1` sans authentification. Désactivez le TLS automatique, car le certificat ne correspond pas à `127.0.0.1` : + +```text +MAILER_DSN=smtp://127.0.0.1:25?auto_tls=false +``` + +> [!WARNING] +> N'utilisez pas `sendmail://` : les fonctions PHP qu'il utilise (`proc_open`…) sont désactivées dans PHP-FPM par HestiaCP. N'utilisez pas `localhost` non plus : Exim n'écoute qu'en IPv4, alors que `localhost` peut se résoudre en IPv6 (`::1`). + +> [!TIP] Limite d'envoi +> Par défaut, chaque compte peut envoyer **200 mails par heure**. Pour une application qui envoie beaucoup : `sudo v-change-mail-account-rate-limit {{CLIENT}} {{DOMAINE}} noreply 1000`. + +## Configuration automatique pour Thunderbird (facultatif) + +HestiaCP ne gère pas l'autoconfiguration. Thunderbird la cherche sur `autoconfig.` : un petit site statique suffit. + +```bash +sudo v-add-dns-record {{CLIENT}} {{DOMAINE}} autoconfig A {{IP}} +sudo v-add-web-domain {{CLIENT}} autoconfig.{{DOMAINE}} +sudo v-add-letsencrypt-domain {{CLIENT}} autoconfig.{{DOMAINE}} +sudo mkdir -p /home/{{CLIENT}}/web/autoconfig.{{DOMAINE}}/public_html/mail +sudo tee /home/{{CLIENT}}/web/autoconfig.{{DOMAINE}}/public_html/mail/config-v1.1.xml > /dev/null << 'EOF' + + + + {{DOMAINE}} + {{DOMAINE}} + + mail.{{DOMAINE}} + 993 + SSL + %EMAILADDRESS% + password-cleartext + + + mail.{{DOMAINE}} + 465 + SSL + %EMAILADDRESS% + password-cleartext + + + +EOF +sudo chown -R {{CLIENT}}:{{CLIENT}} /home/{{CLIENT}}/web/autoconfig.{{DOMAINE}}/public_html/mail +``` + +> [!CHECK] +> `curl -s https://autoconfig.{{DOMAINE}}/mail/config-v1.1.xml | head -3` affiche le début du fichier XML. diff --git a/hestiacp/chapitres/10-deployer-symfony.md b/hestiacp/chapitres/10-deployer-symfony.md new file mode 100644 index 0000000..9cf2736 --- /dev/null +++ b/hestiacp/chapitres/10-deployer-symfony.md @@ -0,0 +1,157 @@ +# Déployer un projet Symfony + +Exemple : déployer depuis GitHub un projet Symfony sur {{SITE}}, avec sa base `{{CLIENT}}_app`. + +Avec HestiaCP, PHP-FPM, le cron et SSH tournent **tous** sous {{CLIENT}}. Il n'y a donc plus de cron de permissions sur `var/cache`, plus d'ACL et plus de `chmod 777`. + +> [!NOTE] Prérequis +> - Le site {{SITE}} existe, avec SSL (chapitre 7). +> - La base `{{CLIENT}}_app` existe (chapitre 8). +> - La boîte `noreply` existe (chapitre 9). + +## Se connecter en tant que {{CLIENT}} + +Depuis votre ordinateur : + +```bash +ssh -p {{SSH_PORT}} {{CLIENT}}@{{IP}} +``` + +Toutes les commandes de ce chapitre se lancent **en {{CLIENT}}**, sauf mention contraire. + +## Créer une clé de déploiement GitHub + +```bash +ssh-keygen -t ed25519 -f ~/.ssh/github_deploy -N "" -C "deploy@{{SITE}}" +cat >> ~/.ssh/config << 'EOF' +Host github.com + IdentityFile ~/.ssh/github_deploy + IdentitiesOnly yes +EOF +chmod 600 ~/.ssh/config +cat ~/.ssh/github_deploy.pub +``` + +Sur GitHub, dans le dépôt : **Settings › Deploy keys › Add deploy key**. Collez la clé affichée et laissez « Allow write access » **décoché**. + +> [!CHECK] +> `ssh -T git@github.com` répond `Hi /! You've successfully authenticated…` (tapez `yes` à la première connexion). + +## Cloner le projet + +Le dossier `public_html` contient une page d'accueil HestiaCP par défaut : videz-le, puis clonez le projet dedans. + +```bash +cd ~/web/{{SITE}}/public_html +rm -f index.html robots.txt +# Remplacez COMPTE/PROJET par votre dépôt GitHub +git clone git@github.com:COMPTE/PROJET.git . +ls +``` + +> [!CHECK] +> Vous voyez `bin/`, `config/`, `public/`, `src/`, `composer.json`… + +> [!WARNING] Le fichier .htaccess de Symfony +> Apache a besoin de `public/.htaccess` pour router les URL vers `index.php`. S'il n'existe pas dans votre dépôt, ajoutez-le **sur votre poste** avec `composer require symfony/apache-pack`, puis commitez-le. + +## Configurer l'environnement de production + +Générez un secret : + +```bash +php -r 'echo bin2hex(random_bytes(16)), PHP_EOL;' +``` + +Relevez la version exacte de MariaDB : + +```bash +mariadb --version +``` + +Créez `.env.local` avec vos valeurs. `serverVersion` reprend la version affichée, par exemple `11.8.3-MariaDB` : + +```bash +cat > .env.local << 'EOF' +APP_ENV=prod +APP_DEBUG=0 +APP_SECRET=collez_le_secret_ici +DATABASE_URL="mysql://{{CLIENT}}_app:MOT_DE_PASSE_BDD@127.0.0.1:3306/{{CLIENT}}_app?serverVersion=11.8.3-MariaDB&charset=utf8mb4" +MAILER_DSN=smtp://noreply%40{{DOMAINE}}:MOT_DE_PASSE_NOREPLY@mail.{{DOMAINE}}:587 +EOF +chmod 600 .env.local +``` + +## Installer les dépendances et la base + +```bash +composer install --no-dev --optimize-autoloader +php bin/console doctrine:migrations:migrate --no-interaction +php bin/console cache:clear +``` + +> [!CHECK] +> `php bin/console about` affiche `Environment: prod` et `Debug: false`. + +## Pointer le site sur le dossier public/ + +À faire **avec {{USER}}** (dans une autre session SSH). Le dernier argument `yes` est indispensable : sans lui, PHP n'aurait plus accès à `vendor/`, `var/` ni `config/`, et le site planterait. + +```bash +sudo v-change-web-domain-docroot {{CLIENT}} {{SITE}} {{SITE}} public yes +``` + +> [!CHECK] +> `curl -sI https://{{SITE}} | head -1` renvoie `HTTP/2 200`, et la page d'accueil de l'application s'affiche dans le navigateur. + +> [!TIP] +> Dans le panel, c'est **Modifier le domaine › Options avancées › Racine de document personnalisée**, avec **Pointer vers** = {{SITE}} et **Répertoire** = `public`. + +## Créer le script de déploiement + +En {{CLIENT}}, créez `~/deploy-{{SITE}}.sh`. Il utilise des chemins complets, car les alias (`composer`, `php`) ne fonctionnent pas dans un script. + +```bash +cat > ~/deploy-{{SITE}}.sh << 'EOF' +#!/bin/bash +set -euo pipefail +PHP=/usr/bin/php8.4 +COMPOSER="$PHP $HOME/.composer/composer" +cd "$HOME/web/{{SITE}}/public_html" + +git pull --ff-only +$COMPOSER install --no-dev --optimize-autoloader --no-interaction +$PHP bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration +$PHP bin/console cache:clear +echo "Déploiement terminé : $(git log -1 --format='%h %s')" +EOF +chmod 700 ~/deploy-{{SITE}}.sh +``` + +Pour déployer une nouvelle version, depuis votre ordinateur : + +```bash +ssh -p {{SSH_PORT}} {{CLIENT}}@{{IP}} ./deploy-{{SITE}}.sh +``` + +> [!NOTE] +> OPcache vérifie la date des fichiers : le nouveau code est pris en compte en quelques secondes, sans redémarrer PHP-FPM. + +## Ajouter les tâches planifiées + +À faire avec {{USER}}. Les `*` doivent être entre guillemets. Exemple : une commande toutes les 5 minutes, et le worker Messenger, relancé toutes les 5 minutes pendant 4 min 50 s. + +```bash +sudo v-add-cron-job {{CLIENT}} '*/5' '*' '*' '*' '*' "/usr/bin/php8.4 /home/{{CLIENT}}/web/{{SITE}}/public_html/bin/console app:ma-commande" +sudo v-add-cron-job {{CLIENT}} '*/5' '*' '*' '*' '*' "/usr/bin/php8.4 /home/{{CLIENT}}/web/{{SITE}}/public_html/bin/console messenger:consume async --time-limit=290 --quiet" +sudo v-list-cron-jobs {{CLIENT}} +``` + +## Connaître les limites de PHP-FPM + +Pour la sécurité, HestiaCP désactive certaines fonctions dans le PHP **web** (PHP-FPM) : `exec`, `system`, `shell_exec`, `passthru`, `proc_open`, `popen`. Tout ce qui lance un processus depuis une page web échouera, par exemple le composant Process ou le transport mail `sendmail://`. Faites ces traitements en console (cron, Messenger), où ces fonctions restent disponibles. + +Les chemins accessibles à PHP-FPM (`open_basedir`) se limitent à `public_html/`, `private/`, `~/tmp` et `~/.composer`. Gardez `var/` (cache, logs) **dans** le projet. Pour les fichiers envoyés par les utilisateurs, `public_html/public/uploads` ou `private/` conviennent. + +> [!TIP] Journaux +> Erreurs Symfony : `~/web/{{SITE}}/public_html/var/log/prod.log`. Erreurs Apache/PHP : `~/web/{{SITE}}/logs/`. diff --git a/hestiacp/chapitres/11-docker.md b/hestiacp/chapitres/11-docker.md new file mode 100644 index 0000000..420c769 --- /dev/null +++ b/hestiacp/chapitres/11-docker.md @@ -0,0 +1,221 @@ +# Docker et applications en conteneurs + +Docker fait tourner des applications prêtes à l'emploi : Portainer, Uptime Kuma, Gitea, Vaultwarden… Chaque conteneur écoute **uniquement sur 127.0.0.1**, et Nginx de HestiaCP le publie sur un sous-domaine avec un certificat Let's Encrypt. + +> [!DANGER] Docker contourne le pare-feu +> Un port publié par Docker (`-p 9000:9000`) passe **avant** le pare-feu de HestiaCP et devient accessible depuis Internet. Publiez toujours sur `127.0.0.1:` ; la configuration ci-dessous l'impose par défaut. + +## Installer Docker (dépôt officiel) + +Ces commandes viennent de la documentation officielle de Docker pour Ubuntu : + +```bash +sudo apt update +sudo apt install -y ca-certificates curl +sudo install -m 0755 -d /etc/apt/keyrings +sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc +sudo chmod a+r /etc/apt/keyrings/docker.asc + +sudo tee /etc/apt/sources.list.d/docker.sources < [!CHECK] +> `sudo docker run --rm hello-world` affiche `Hello from Docker!`, et `docker compose version` affiche une version 2.x ou plus. + +## Configurer Docker : localhost et rotation des journaux + +Ce fichier fait publier les ports sur `127.0.0.1` par défaut, et limite la taille des journaux des conteneurs, qui peuvent sinon remplir le disque : + +```bash +sudo tee /etc/docker/daemon.json > /dev/null << 'EOF' +{ + "ip": "127.0.0.1", + "log-driver": "json-file", + "log-opts": { "max-size": "10m", "max-file": "3" } +} +EOF +sudo systemctl restart docker +sudo systemctl enable docker containerd +``` + +Autorisez votre utilisateur à utiliser Docker sans `sudo`, puis déconnectez-vous et reconnectez-vous : + +```bash +sudo usermod -aG docker {{USER}} +``` + +> [!WARNING] +> Le groupe `docker` donne des droits équivalents à root. Ne l'accordez qu'à {{USER}}, jamais à {{CLIENT}}. + +> [!NOTE] Pare-feu HestiaCP et Docker +> `v-update-firewall` ne touche pas aux règles de Docker. En revanche, `sudo systemctl restart hestia-iptables` recharge un ancien instantané des règles : lancez `sudo systemctl restart docker` juste après. + +## Créer le générateur de modèles de proxy + +HestiaCP publie un site à l'aide de **modèles Nginx**. Ce petit script crée un modèle `proxy-` qui redirige un domaine vers `127.0.0.1:`. Il gère le HTTPS forcé, HSTS, Let's Encrypt et les WebSockets. + +D'abord, la variable Nginx nécessaire aux WebSockets : + +```bash +sudo tee /etc/nginx/conf.d/websocket-map.conf > /dev/null << 'EOF' +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} +EOF +sudo nginx -t && sudo systemctl reload nginx +``` + +Puis le générateur : + +```bash +sudo tee /usr/local/sbin/hestia-proxy-tpl > /dev/null << 'SCRIPT' +#!/bin/bash +# Usage : hestia-proxy-tpl PORT -> crée le modèle proxy Nginx "proxy-PORT" +set -euo pipefail +PORT="${1:?Usage : hestia-proxy-tpl PORT}" +[[ "$PORT" =~ ^[0-9]{2,5}$ ]] || { echo "Port invalide : $PORT"; exit 1; } +DIR=/usr/local/hestia/data/templates/web/nginx +NAME="proxy-$PORT" + +LOCATION=' + location / { + proxy_pass http://127.0.0.1:__PORT__; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + client_max_body_size 512m; + } + + location /error/ { + alias %home%/%user%/web/%domain%/document_errors/; + } +' + +cat > "$DIR/$NAME.tpl" << EOF +# Modèle généré par hestia-proxy-tpl : proxy vers 127.0.0.1:__PORT__ +server { + listen %ip%:%proxy_port%; + server_name %domain_idn% %alias_idn%; + access_log /var/log/%web_system%/domains/%domain%.log combined; + error_log /var/log/%web_system%/domains/%domain%.error.log error; + + include %home%/%user%/conf/web/%domain%/nginx.forcessl.conf*; +$LOCATION + include %home%/%user%/conf/web/%domain%/nginx.conf_*; +} +EOF + +cat > "$DIR/$NAME.stpl" << EOF +# Modèle généré par hestia-proxy-tpl : proxy vers 127.0.0.1:__PORT__ +server { + listen %ip%:%proxy_ssl_port% ssl; + server_name %domain_idn% %alias_idn%; + access_log /var/log/%web_system%/domains/%domain%.log combined; + error_log /var/log/%web_system%/domains/%domain%.error.log error; + + ssl_certificate %ssl_pem%; + ssl_certificate_key %ssl_key%; + + if (\$anti_replay = 307) { return 307 https://\$host\$request_uri; } + if (\$anti_replay = 425) { return 425; } + + include %home%/%user%/conf/web/%domain%/nginx.hsts.conf*; +$LOCATION + include %home%/%user%/conf/web/%domain%/nginx.ssl.conf_*; +} +EOF + +sed -i "s/__PORT__/$PORT/g" "$DIR/$NAME.tpl" "$DIR/$NAME.stpl" +chmod 644 "$DIR/$NAME.tpl" "$DIR/$NAME.stpl" +echo "Modèle $NAME créé." +SCRIPT +sudo chmod 750 /usr/local/sbin/hestia-proxy-tpl +``` + +> [!NOTE] +> Les modèles portent des noms personnels (`proxy-9000`…) : les mises à jour de HestiaCP ne les écrasent pas. Ne modifiez jamais les modèles `default`. + +## Publier une application : la méthode + +Pour chaque application, le même schéma en 5 commandes. Exemple : un conteneur sur le port `PORT`, publié sur `appli.{{DOMAINE}}`. + +```bash +sudo hestia-proxy-tpl PORT +sudo v-add-dns-record {{CLIENT}} {{DOMAINE}} appli A {{IP}} +sudo v-add-web-domain {{CLIENT}} appli.{{DOMAINE}} +sudo v-change-web-domain-proxy-tpl {{CLIENT}} appli.{{DOMAINE}} proxy-PORT +sudo v-add-letsencrypt-domain {{CLIENT}} appli.{{DOMAINE}} +sudo v-add-web-domain-ssl-force {{CLIENT}} appli.{{DOMAINE}} +``` + +## Installer Portainer (gestion de Docker) + +Portainer est une interface web pour gérer vos conteneurs. On utilise son port HTTP 9000, uniquement sur localhost, derrière le proxy HTTPS : + +```bash +docker volume create portainer_data +docker run -d --name portainer --restart=always \ + -p 127.0.0.1:9000:9000 \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v portainer_data:/data \ + portainer/portainer-ce:lts +``` + +Publiez-le sur `portainer.{{DOMAINE}}` : + +```bash +sudo hestia-proxy-tpl 9000 +sudo v-add-dns-record {{CLIENT}} {{DOMAINE}} portainer A {{IP}} +sudo v-add-web-domain {{CLIENT}} portainer.{{DOMAINE}} +sudo v-change-web-domain-proxy-tpl {{CLIENT}} portainer.{{DOMAINE}} proxy-9000 +sudo v-add-letsencrypt-domain {{CLIENT}} portainer.{{DOMAINE}} +sudo v-add-web-domain-ssl-force {{CLIENT}} portainer.{{DOMAINE}} +``` + +> [!WARNING] Créez le compte tout de suite +> Ouvrez `https://portainer.{{DOMAINE}}` **dans les 5 minutes** et créez le compte administrateur avec un mot de passe fort. Passé ce délai, Portainer se verrouille par sécurité : relancez-le avec `docker restart portainer`. + +> [!CHECK] +> `ss -tlnp | grep 9000` montre `127.0.0.1:9000` (et non `0.0.0.0:9000`), et `https://portainer.{{DOMAINE}}` affiche Portainer avec un cadenas valide. + +## Utiliser Docker Compose pour les autres applications + +Pour une application décrite par un `compose.yaml`, placez-la dans `/opt//` et publiez toujours ses ports sur `127.0.0.1` : + +```yaml +services: + appli: + image: editeur/appli:version + restart: unless-stopped + ports: + - "127.0.0.1:8080:8080" + volumes: + - ./data:/data +``` + +```bash +cd /opt/appli && docker compose up -d +``` + +Suivez ensuite la méthode ci-dessus avec le port `8080`. + +> [!TIP] Mettre à jour un conteneur +> Avec Compose : `docker compose pull && docker compose up -d`. Portainer propose aussi un bouton **Recreate › Pull latest image**. diff --git a/hestiacp/chapitres/12-supervision.md b/hestiacp/chapitres/12-supervision.md new file mode 100644 index 0000000..4f8d30a --- /dev/null +++ b/hestiacp/chapitres/12-supervision.md @@ -0,0 +1,86 @@ +# Supervision + +Trois niveaux, du plus simple au plus complet : + +1. les outils intégrés à HestiaCP ; +2. **Uptime Kuma**, qui vérifie que vos sites répondent et vous alerte ; +3. **Netdata**, qui affiche en temps réel le processeur, la mémoire, le disque et les services. + +## Utiliser les outils intégrés à HestiaCP + +- **Roue dentée** (page des services) : l'état de chaque service (Nginx, Apache, PHP-FPM, MariaDB, Exim, Dovecot, Bind…), avec redémarrage en un clic. +- **Roue dentée › Moniteur de tâches** : graphiques de charge, mémoire, réseau, web, mail et bases de données. +- **Statistiques** : bande passante et espace disque par utilisateur. + +En ligne de commande : + +```bash +sudo v-list-sys-services +sudo v-list-sys-info +df -h / && free -h +``` + +## Installer Uptime Kuma (disponibilité et alertes) + +Uptime Kuma teste vos sites, vos certificats SSL, vos ports et vos DNS toutes les minutes. Il vous alerte par e-mail, Telegram, Discord… + +```bash +docker run -d --name uptime-kuma --restart=always \ + -p 127.0.0.1:3001:3001 \ + -v uptime-kuma:/app/data \ + louislam/uptime-kuma:2 +``` + +Publiez-le sur `status.{{DOMAINE}}` (chapitre 11) : + +```bash +sudo hestia-proxy-tpl 3001 +sudo v-add-dns-record {{CLIENT}} {{DOMAINE}} status A {{IP}} +sudo v-add-web-domain {{CLIENT}} status.{{DOMAINE}} +sudo v-change-web-domain-proxy-tpl {{CLIENT}} status.{{DOMAINE}} proxy-3001 +sudo v-add-letsencrypt-domain {{CLIENT}} status.{{DOMAINE}} +sudo v-add-web-domain-ssl-force {{CLIENT}} status.{{DOMAINE}} +``` + +Ouvrez `https://status.{{DOMAINE}}` et créez le compte administrateur. Ajoutez ensuite une sonde **HTTP(s)** pour chaque site, en cochant l'option d'alerte d'expiration du certificat. + +Configurez les notifications, par exemple en SMTP avec la boîte `noreply@{{DOMAINE}}` : serveur `mail.{{DOMAINE}}`, port 465, sécurité SSL/TLS. + +> [!TIP] Une sonde externe en plus +> Si le serveur tombe, Uptime Kuma tombe avec lui et ne peut plus alerter. Ajoutez une surveillance **externe** gratuite, par exemple UptimeRobot ou Better Stack, au moins sur {{SITE}}. + +## Installer Netdata (métriques en temps réel) + +Netdata détecte seul Nginx, Apache, PHP-FPM, MariaDB, Exim, Docker… + +```bash +wget -O /tmp/netdata-kickstart.sh https://get.netdata.cloud/kickstart.sh +sudo sh /tmp/netdata-kickstart.sh --stable-channel --disable-telemetry --non-interactive +rm /tmp/netdata-kickstart.sh +``` + +Netdata écoute sur le port 19999, qui reste **fermé** par le pare-feu de HestiaCP (IPv4) et par le pare-feu IPv6 (chapitre 4). Son tableau de bord n'a pas de mot de passe : on y accède par un **tunnel SSH**, sans rien exposer. + +Depuis votre ordinateur : + +```bash +ssh -N -L 19999:127.0.0.1:19999 {{HOST}} +``` + +Laissez ce terminal ouvert et ouvrez `http://localhost:19999` dans votre navigateur. + +> [!CHECK] +> Depuis l'extérieur, `curl -m 5 http://{{IP}}:19999` doit échouer (délai dépassé), alors que le tableau de bord s'affiche via le tunnel. + +> [!NOTE] +> Netdata propose un compte « Cloud » facultatif : le tableau de bord local fonctionne sans. Il utilise environ 150 à 300 Mo de RAM ; pour l'arrêter, `sudo systemctl disable --now netdata`. + +## Recevoir les alertes du serveur + +HestiaCP envoie ses notifications (mises à jour, sauvegardes, erreurs) à l'adresse de {{ADMIN}}. Vérifiez qu'elle est correcte : + +```bash +sudo v-list-user {{ADMIN}} | grep EMAIL +``` + +Pour la changer : `sudo v-change-user-contact {{ADMIN}} nouvelle@adresse.com`. diff --git a/hestiacp/chapitres/13-sauvegardes.md b/hestiacp/chapitres/13-sauvegardes.md new file mode 100644 index 0000000..3f0abb2 --- /dev/null +++ b/hestiacp/chapitres/13-sauvegardes.md @@ -0,0 +1,120 @@ +# Sauvegardes + +HestiaCP sauvegarde chaque utilisateur **tous les jours à 05h10**. La sauvegarde contient ses sites, bases de données, boîtes mail, zones DNS, tâches cron, certificats et son dossier personnel. + +Deux compléments sont indispensables : + +- une **copie hors du serveur**, car une sauvegarde qui reste sur le serveur disparaît avec lui ; +- la sauvegarde de la **configuration système**, que HestiaCP ne sauvegarde pas : `/etc`, la configuration de HestiaCP, vos modèles, les volumes Docker. + +## Vérifier la sauvegarde locale + +Lancez une sauvegarde manuelle de {{CLIENT}} : + +```bash +sudo v-backup-user {{CLIENT}} yes +sudo v-list-user-backups {{CLIENT}} +ls -lh /backup/ +``` + +> [!CHECK] +> Un fichier `{{CLIENT}}.AAAA-MM-JJ_HH-MM-SS.tar` apparaît dans `/backup/`. Le nombre de sauvegardes conservées est celui du forfait (7 pour `projets`). + +> [!NOTE] +> Pour exclure un dossier volumineux (cache, fichiers temporaires) : **Sauvegardes › Exclusions**, dans le compte de l'utilisateur. + +## Préparer une clé SSH pour le serveur de sauvegarde + +La copie distante se fait en SFTP vers {{BACKUP_HOST}}, avec une clé SSH dédiée, sans mot de passe : + +```bash +sudo ssh-keygen -t ed25519 -f /root/.ssh/hestia-backup -N "" -C "backup@{{FQDN}}" +sudo cat /root/.ssh/hestia-backup.pub +``` + +Ajoutez cette clé publique au fichier `~/.ssh/authorized_keys` du compte {{BACKUP_USER}} sur {{BACKUP_HOST}}. Sur un hébergement mutualisé, passez par l'interface (cPanel : **Accès SSH › Gérer les clés SSH › Importer**, puis **Autoriser**). + +Testez la connexion et créez le dossier de destination : + +```bash +sudo ssh -i /root/.ssh/hestia-backup -p {{BACKUP_PORT}} {{BACKUP_USER}}@{{BACKUP_HOST}} "mkdir -p hestia-{{HOST}}/systeme && ls" +``` + +> [!CHECK] +> Répondez `yes` à la question sur l'empreinte du serveur. La commande affiche le contenu du dossier distant, dont `hestia-{{HOST}}`, sans demander de mot de passe. + +## Envoyer les sauvegardes HestiaCP hors du serveur + +```bash +sudo v-add-backup-host sftp {{BACKUP_HOST}} {{BACKUP_USER}} /root/.ssh/hestia-backup hestia-{{HOST}} {{BACKUP_PORT}} +sudo v-list-backup-host sftp +``` + +Les sauvegardes sont maintenant copiées aussi vers `hestia-{{HOST}}/` sur {{BACKUP_HOST}}, avec la même durée de conservation. Testez tout de suite : + +```bash +sudo v-backup-user {{CLIENT}} yes +sudo ssh -i /root/.ssh/hestia-backup -p {{BACKUP_PORT}} {{BACKUP_USER}}@{{BACKUP_HOST}} "ls -lh hestia-{{HOST}}" +``` + +> [!CHECK] +> L'archive `{{CLIENT}}.….tar` du jour est présente sur le serveur distant, et {{EMAIL}} reçoit un e-mail de confirmation de la sauvegarde. + +## Sauvegarder la configuration système + +Ce script copie chaque nuit vers le serveur distant (qui doit disposer de `rsync`) `/etc`, la configuration et les modèles de HestiaCP, les volumes Docker et `/opt` : + +```bash +sudo tee /usr/local/sbin/backup-systeme.sh > /dev/null << 'EOF' +#!/bin/bash +# Copie de la configuration système vers le serveur de sauvegarde +set -euo pipefail +DEST="{{BACKUP_USER}}@{{BACKUP_HOST}}:hestia-{{HOST}}/systeme/" +SSH="ssh -i /root/.ssh/hestia-backup -p {{BACKUP_PORT}}" +SOURCES=() +for p in /etc /usr/local/hestia/conf /usr/local/hestia/data /usr/local/sbin \ + /opt /var/lib/docker/volumes /root/.ssh; do + if [ -e "$p" ]; then SOURCES+=("$p"); fi +done +rsync -aR --delete -e "$SSH" "${SOURCES[@]}" "$DEST" +EOF +sudo chmod 700 /usr/local/sbin/backup-systeme.sh +sudo /usr/local/sbin/backup-systeme.sh && echo "Sauvegarde système OK" +``` + +Programmez-le chaque nuit à 04h30 (avant la sauvegarde HestiaCP) : + +```bash +echo '30 4 * * * root /usr/local/sbin/backup-systeme.sh > /var/log/backup-systeme.log 2>&1' | sudo tee /etc/cron.d/backup-systeme +``` + +> [!WARNING] +> Ces copies contiennent des secrets : clés SSH, mots de passe de bases dans `/usr/local/hestia/conf`, fichiers `.env.local`. Le compte de sauvegarde doit être protégé comme le serveur lui-même. + +## Restaurer + +Lister les sauvegardes d'un utilisateur : + +```bash +sudo v-list-user-backups {{CLIENT}} +``` + +Restaurer **un seul** site, une base ou un domaine mail, depuis une archive locale ou distante : + +```bash +sudo v-restore-web-domain {{CLIENT}} {{CLIENT}}.AAAA-MM-JJ_HH-MM-SS.tar {{SITE}} +sudo v-restore-database {{CLIENT}} {{CLIENT}}.AAAA-MM-JJ_HH-MM-SS.tar {{CLIENT}}_app +sudo v-restore-mail-domain {{CLIENT}} {{CLIENT}}.AAAA-MM-JJ_HH-MM-SS.tar {{DOMAINE}} +``` + +Restaurer **tout** l'utilisateur (sur ce serveur ou sur un nouveau serveur HestiaCP, où l'utilisateur sera recréé) : + +```bash +sudo v-restore-user {{CLIENT}} {{CLIENT}}.AAAA-MM-JJ_HH-MM-SS.tar +``` + +> [!TIP] Testez une restauration +> Une sauvegarde jamais testée n'est pas une sauvegarde. Une fois par trimestre, restaurez une base dans une base de test, ou le site complet sur un VPS temporaire. + +> [!NOTE] Pour aller plus loin : sauvegardes incrémentales +> Depuis la version 1.9, HestiaCP sait aussi faire des sauvegardes **incrémentales** avec restic (commande `v-add-backup-host-restic`), vers un stockage compatible rclone (S3, Backblaze B2…). Elles prennent beaucoup moins de place que des archives complètes quotidiennes. Pensez alors à sauvegarder les fichiers `/usr/local/hestia/data/users/*/restic.conf`, sans lesquels la restauration est impossible : le script `backup-systeme.sh` les inclut déjà. diff --git a/hestiacp/chapitres/14-maintenance.md b/hestiacp/chapitres/14-maintenance.md new file mode 100644 index 0000000..d06f2b6 --- /dev/null +++ b/hestiacp/chapitres/14-maintenance.md @@ -0,0 +1,115 @@ +# Maintenance et dépannage + +## Faire les mises à jour mensuelles + +HestiaCP et les correctifs de sécurité d'Ubuntu s'installent seuls. Une fois par mois, mettez aussi à jour le reste (PHP, Nginx, MariaDB, Docker) : + +```bash +sudo apt update && sudo apt upgrade +[ -f /var/run/reboot-required ] && echo "Redémarrage nécessaire" +sudo v-list-sys-hestia-updates +``` + +> [!WARNING] +> Si apt demande quoi faire d'un fichier de configuration modifié, répondez **N** (garder la version actuelle). HestiaCP gère lui-même ces fichiers. + +Mettez à jour les conteneurs de la même façon : `docker compose pull && docker compose up -d` dans chaque dossier `/opt/`, ou le bouton de mise à jour de Portainer. + +## Mémo des commandes utiles + +| Besoin | Commande | +|---|---| +| État des services | `sudo v-list-sys-services` | +| Redémarrer un service | `sudo v-restart-service nginx` (ou `apache2`, `php-fpm`, `exim4`, `dovecot`, `mariadb`, `bind9`) | +| Lister les utilisateurs / sites | `sudo v-list-users` · `sudo v-list-web-domains {{CLIENT}}` | +| Reconstruire la config des sites | `sudo v-rebuild-web-domains {{CLIENT}}` | +| Reconstruire tout un utilisateur | `sudo v-rebuild-user {{CLIENT}}` | +| Pare-feu, IP bannies | `sudo v-list-firewall` · `sudo v-list-firewall-ban` | +| Débannir une IP | `sudo v-delete-firewall-ban ALL` | +| Renouveler un certificat | `sudo v-add-letsencrypt-domain {{CLIENT}} www.` | +| Aide d'une commande | ouvrir le script, ex. `head -12 /usr/local/hestia/bin/v-add-web-domain` | + +## Où trouver les journaux + +| Journal | Emplacement | +|---|---| +| Actions HestiaCP | `/var/log/hestia/system.log`, `/var/log/hestia/error.log` | +| Connexions au panel | `/var/log/hestia/auth.log` | +| Let's Encrypt | `/var/log/hestia/LE--.log` | +| Sites (Nginx / Apache) | `/var/log/nginx/domains/`, `/var/log/apache2/domains/` | +| Mail envoyé et reçu | `/var/log/exim4/mainlog` | +| IMAP / POP3 | `/var/log/dovecot.log` | +| Fail2ban | `/var/log/fail2ban.log` | +| SSH | `/var/log/auth.log` | +| Installation | `/root/hst_install_backups/` | + +## Résoudre les problèmes fréquents + +### Le certificat Let's Encrypt échoue + +1. `dig +short ` doit répondre {{IP}}, et `dig +short AAAA ` ne doit **rien** répondre (chapitre 1). +2. Le port 80 doit être ouvert (`sudo v-list-firewall | grep WEB`). +3. Lisez le journal `/var/log/hestia/LE-{{CLIENT}}-.log`. +4. Après trop d'échecs, Let's Encrypt bloque le domaine pendant une heure : attendez avant de réessayer. + +### Erreur 502 ou 503 sur un site + +PHP-FPM est arrêté ou saturé : + +```bash +sudo v-restart-service php-fpm +sudo tail -n 50 /var/log/apache2/domains/{{SITE}}.error.log +``` + +### Erreur 500 sur une application Symfony + +Connecté en {{CLIENT}} : + +```bash +tail -n 50 ~/web/{{SITE}}/public_html/var/log/prod.log +cd ~/web/{{SITE}}/public_html && php bin/console cache:clear +``` + +### Les mails arrivent en spam + +1. Testez sur et suivez ses indications. +2. Vérifiez le PTR : `dig +short -x {{IP}}` doit répondre `{{FQDN}}.`. +3. Vérifiez que l'IP n'est pas sur liste noire : . +4. Vérifiez SPF, DKIM et DMARC : `sudo v-list-dns-records {{CLIENT}} {{DOMAINE}}`. + +### Vous êtes bloqué dehors (SSH refusé) + +Votre IP a sans doute été bannie par Fail2ban. Connectez-vous par la **console VNC** du panel Contabo, puis : + +```bash +sudo v-list-firewall-ban +sudo v-delete-firewall-ban VOTRE_IP ALL +``` + +### Le disque se remplit + +```bash +df -h / +sudo du -sh /backup /home/* /var/lib/docker /var/log 2>/dev/null | sort -h +docker system prune +``` + +Réduisez si besoin le nombre de sauvegardes locales dans le forfait (`BACKUPS`), puisqu'une copie existe sur le serveur distant. + +## Vérifier que tout est en place + +| ✓ | Vérification | +|---|---| +| ☐ | Panel accessible en HTTPS avec certificat valide et double authentification | +| ☐ | SSH uniquement par clé, sur le port {{SSH_PORT}}, root interdit | +| ☐ | IPv6 entrante bloquée (`sudo ip6tables -S INPUT`) | +| ☐ | UFW absent, pare-feu HestiaCP et Fail2ban actifs | +| ☐ | `dig NS {{DOMAINE}}` renvoie `ns1` et `ns2.{{DOMAINE}}` | +| ☐ | PTR de {{IP}} = {{FQDN}} | +| ☐ | Sites en HTTPS forcé, sans enregistrement AAAA | +| ☐ | mail-tester.com : 10/10 ; Gmail : SPF, DKIM et DMARC en PASS | +| ☐ | Conteneurs Docker publiés uniquement sur 127.0.0.1 (`ss -tlnp`) | +| ☐ | Sauvegarde HestiaCP présente sur {{BACKUP_HOST}} | +| ☐ | Sauvegarde système (`/var/log/backup-systeme.log` sans erreur) | +| ☐ | Uptime Kuma et une surveillance externe actifs | +| ☐ | Une restauration de test réussie | diff --git a/hestiacp/chapitres/99-annexe.md b/hestiacp/chapitres/99-annexe.md new file mode 100644 index 0000000..29c18e0 --- /dev/null +++ b/hestiacp/chapitres/99-annexe.md @@ -0,0 +1,61 @@ +# Annexe : adresses et régénération du guide + +## Récapitulatif des adresses + +| Service | Adresse | +|---|---| +| Panel HestiaCP | `https://{{FQDN}}:{{PANEL_PORT}}` | +| SSH administrateur | `ssh -p {{SSH_PORT}} {{USER}}@{{IP}}` | +| SSH déploiement | `ssh -p {{SSH_PORT}} {{CLIENT}}@{{IP}}` | +| Webmail | `https://webmail.{{DOMAINE}}` | +| Mail (IMAP / SMTP) | `mail.{{DOMAINE}}` : 993 / 465 ou 587 | +| phpMyAdmin | `https://{{FQDN}}//` | +| Portainer | `https://portainer.{{DOMAINE}}` | +| Uptime Kuma | `https://status.{{DOMAINE}}` | +| Netdata | tunnel SSH puis `http://localhost:19999` | +| Serveurs DNS | `ns1.{{DOMAINE}}`, `ns2.{{DOMAINE}}` → {{IP}} | + +## Générer ce guide pour un autre VPS + +Ce PDF est produit à partir de fichiers Markdown, qui se trouvent dans le dépôt `vps-docs`, dossier `hestiacp/` : + +| Élément | Rôle | +|---|---| +| `chapitres/*.md` | Le contenu, un fichier par chapitre ; `{{…}}` marque une variable | +| `vps.conf.exemple` | Modèle de fichier de variables | +| `build/build.py` | Le générateur (Markdown → HTML → PDF avec WeasyPrint) | +| `build/style.css`, `build/fonts/` | La mise en page et les polices | +| `build/meta.conf` | Version de HestiaCP et date de vérification affichées en couverture | + +Installez une fois les outils (Ubuntu ou Debian) : + +```bash +sudo apt install -y python3-venv libpango-1.0-0 libpangoft2-1.0-0 +python3 -m venv ~/.venvs/guide +~/.venvs/guide/bin/pip install weasyprint markdown pygments +``` + +Puis, pour un nouveau VPS : + +```bash +cd vps-docs/hestiacp +cp vps.conf.exemple nouveau-vps.conf +micro nouveau-vps.conf +~/.venvs/guide/bin/python build/build.py --vars nouveau-vps.conf +``` + +Le fichier `Guide-VPS-HestiaCP-.pdf` est créé, avec toutes les valeurs déjà remplies. Sans `--vars`, vous obtenez la version générique. + +> [!WARNING] Avant chaque nouvelle installation +> HestiaCP évolue vite. Avant d'installer un nouveau serveur : +> - consultez les notes de version sur ; +> - vérifiez les options de l'installeur avec `bash hst-install.sh -h` ; +> - en cas de doute sur une commande `v-…`, lisez son en-tête : `head -12 /usr/local/hestia/bin/` ; +> - mettez à jour `build/meta.conf` après vérification. + +## Sources officielles + +- Documentation HestiaCP : +- Code source et scripts `v-*` : +- Forum : +- Installation de Docker : diff --git a/hestiacp/skglobalinstitute.conf b/hestiacp/skglobalinstitute.conf new file mode 100644 index 0000000..f83b52b --- /dev/null +++ b/hestiacp/skglobalinstitute.conf @@ -0,0 +1,19 @@ +# VPS Contabo Cloud VPS 6 (2026) — 6 cœurs, 12 Go RAM, 200 Go +# Génération : python3 build/build.py --vars skglobalinstitute.conf + +IP=144.91.70.25 +IPV6=2a02:c207:2356:8609::1 +DOMAINE=skglobalinstitute.com +FQDN=srv1.skglobalinstitute.com +HOST=srv1 +USER=scriptshadow +ADMIN=skadmin +# Adresse externe (Gmail...) qui reçoit les identifiants et alertes du panel +EMAIL=issazana.ia@gmail.com +SSH_PORT=50022 +PANEL_PORT=8083 +CLIENT=skglobal +SITE=skglobalinstitute.com +BACKUP_HOST=node21-eu.n0c.com +BACKUP_USER=qmwwranm +BACKUP_PORT=5022 diff --git a/hestiacp/vps.conf.exemple b/hestiacp/vps.conf.exemple new file mode 100644 index 0000000..cb7d335 --- /dev/null +++ b/hestiacp/vps.conf.exemple @@ -0,0 +1,21 @@ +# Valeurs d'un VPS pour générer un guide personnalisé. +# Copier ce fichier (ex : cp vps.conf.exemple mon-vps.conf), remplir, puis : +# python3 build/build.py --vars mon-vps.conf +# Une variable laissée vide reste affichée sous la forme dans le PDF. +# Ne mettez JAMAIS de mot de passe dans ce fichier. + +IP=203.0.113.10 +IPV6=2001:db8::10 +DOMAINE=exemple.com +FQDN=srv1.exemple.com +HOST=srv1 +USER=jean +ADMIN=hadmin +EMAIL=admin@exemple.com +SSH_PORT=50022 +PANEL_PORT=8083 +CLIENT=client1 +SITE=monprojet.com +BACKUP_HOST=backup.exemple.net +BACKUP_USER=sauvegarde +BACKUP_PORT=22