yihep87324
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # LXC - Doc Technique Groupe : xxx / xxx ## I - Définition #### La virtualisation La virtualisation est une technologie utilisable dans le but de créer des représentations virtuelles de serveurs, de stockage, de réseaux et d'autres machines physiques. #### La conteneurisation La conteneurisation consiste à rassembler le code du logiciel et tous ses composants, bibliothèques, frameworks et autres dépendances, de manière à les isoler dans leur propre conteneur. #### LXC LXC ou encore LinuX Containers, est un système de virtualisation. Il est utilisé pour faire fonctionner des environnements Linux isolés les uns des autres dans des conteneurs partageant le même noyau. #### LoadBalancing Le LoadBalancing désigne le processus de répartition d'un ensemble de tâches sur un ensemble de ressources, dans le but d'en rendre le traitement global plus efficace. #### RoundRobin Le RoundRobin est un algorithme qui permet d'utiliser chaque serveur à tour de rôle dans un ordre défini. ## II - Pré-requis - Un ordinateur ou une machine virtuelle sous Ubuntu 22.02 - Un compte administrateur ou root - Un accès à internet #### Schéma réseau de l'architecture souhaitée : ![](https://i.imgur.com/P8hbaZ9.png) ## III - Installation Mettre à jour la machine ```bash sudo apt update && sudo apt full-upgrade ``` Installer LXC et LXD ```bash sudo apt install lxc snap install lxd lxd init ``` ## IV - Fonctionnalités #### A - Vérifier la configuration de LXC Afficher la configuration de LXC ```bash lxc-checkconfig ``` #### B - Afficher la liste des conteneurs Lister l'ensemble des conteners ```bash lxc-ls -1 -f ``` Lister uniquement les conteneurs à l'arrêt ```bash lxc-ls --stopped ``` Lister uniquement les conteneurs en cours d'execution ```bash lxc-ls --running ``` #### C - Créer un conteneur Créer un conteneur depuis un image en ligne ```bash lxc-create -t download -n CONTAINER_NAME ``` Créer un conteneur depuis un image en ligne depuis un serveur spécifique ```bash lxc-create -t download -n CONTAINER_NAME -- --keyserver keyserver.ubuntu.com ``` Créer un conteneur Debian immédiatement depuis un image en ligne ```bash lxc-create -t download -n CONTAINER_NAME -- -d debian -r bullseye -a amd64 ``` #### D - Gérer un containeur Démarrer un conteneur ```bash lxc-start -n CONTAINER_NAME ``` Arrêter un conteneur ```bash lxc-stop -n CONTAINER_NAME ``` Supprimer un conteneur ```bash lxc-destroy -n CONTAINER_NAME Accéder à un container ```bash lxc-attach -n container1 lxc-attach -n container2 --clear-env env HOME=/root USER=root TERM=xterm -- bash ``` Créer un réseau virtuelle ```bash lxc network create NETWORK_NAME ``` Disable DHCP service ```bash systemctl list-units | grep network systemctl disable systemd-networkd ``` Ajouter notre container à notre réseau et attribuer une ip statique ```bash ip -o -4 a show NETWORK_NAME | awk '{print $4}' nano /var/lib/lxc/container2/config ``` Modifier/Ajouter la configuration suivante (selon votre réseau) ```bash # Change network interface lxc.net.0.link = dmz # Change network ip configuration lxc.net.0.ipv4.address = 192.168.56.121/24 lxc.net.0.ipv4.gateway = 192.168.56.104 ``` ## V - Fonctionnement ### A - Création des réseaux 1) Pour mettre en place l'architecture réseau, nous allons commencer par créer nos 2 réseaux virtuelles (dmz1 et dmz2). Nous utiliserons pour cela une commande LXD pour simplifier leur création. ```bash lxc network create dmz1 lxc network create dmz2 ``` 2) Pour la suite des opérations nous aurons besoin de connaître le réseau et la passerelle de chacun de nos réseaux. Nous utiliserons la commande suivante pour récupérer la passerelle de chacun des réseaux. ```bash # Afficher l'adresse IP du réseau 'dmz1' ip -o -4 a show dmz1 | awk '{print $4}' | cut -d/ -f1 # Afficher l'adresse IP du réseau 'dmz2' ip -o -4 a show dmz2 | awk '{print $4}' | cut -d/ -f1 ``` Dans notre cas, l'adresse IP des passerelle réseau est : dmz1 : 10.1.0.1 dmz2 : 10.2.0.1 ### B - Création des conteneurs Maintenant que nous avons créer nos réseaux, nous allons pouvoir créer nos différentes machines. Nous recommencerons dons toutes ces étapes pour nos 4 conteneurs selon la configuration ci-dessous : | Nom | IP DMZ1 | IP DMZ2 | IP lxcbr0 (WAN) | | ------------- | --------- | --------- | ----------------------- | | server1 | 10.1.0.10 | 10.2.0.10 | | | server2 | 10.1.0.20 | 10.2.0.20 | | | serverProxy | 10.1.0.30 | | dynamique (par défault) | | serverBackup | | 10.2.0.40 | | 1) Pour créer nos conteneurs nous utiliserons une image Debian, nous utiliserons donc la commande suivante en remplaçant $NAME par le nom de la machine à créer. ```bash lxc-create -t download -n $NAME -- -d debian -r bullseye -a amd64 ``` 2) Une fois créer, avant de démarrer nos conteneurs, il va falloir modifier leur configuration pour leur attribuer leurs réseaux et leurs adresses IPs. Nous allons donc modifier le fichier suivant: ```bash nano /var/lib/lxc/$NAME/config ``` 3) Dans ce fichier nous modifirons/ajouterons les ligne suivante pour configurer une IP statique dans le bon réseau : (En remplacant les adresses et le lien par ceux correspondant à la première interface) ```bash lxc.net.0.link = dmz1 lxc.net.0.ipv4.address = 10.1.0.10/24 lxc.net.0.ipv4.gateway = 10.1.0.1 ``` 4) Dans le cas d'une seconde interface, il faudra ajouter le bloc suivant. (En remplacant les adresses et le lien par ceux correspondant à la deuxième interface) Le champ `hwaddr` correspond à l'adresse MAC, ce dernier peut être aléatoire mais doit être unique pour chaque interface du réseau. ```bash lxc.net.1.type = veth lxc.net.1.link = dmz2 lxc.net.1.flags = up lxc.net.1.hwaddr = 00:16:3e:aa:aa:aa lxc.net.1.ipv4.address = 10.2.0.10/24 ``` 5) Maintenant que le réseau est configurer, nous allons désormais pouvoir démarrer nos conteneurs avec la commande suivante : ```bash lxc-start -n $NAME ``` 6) Bien que nous ayons configurer le réseau de manière statique, la machine à quand même récupéré une adresse dynamique. Pour corriger cela, nous allons donc devoir désactiver le service `systemd-networkd` avec la commande suivante : ```bash lxc-attach -n $hostname -- bash -c 'systemctl disable systemd-networkd' ``` 7) Pour apppliquer le changement, il sera nécessaire de redémarrer la machine. ```bash lxc-stop -r -n $NAME ``` 8) Maintenant notre machine devrait possèder les bonnes adresses réseaux. Néanmoins comme nous avons désactiver le service `systemd-networkd`, la résolution DNS ne fonctionne plus en l'état. Il va donc falloir ajouter une ligne au fichier `/etc/resolv.conf` pour ajouter un nouveau serveur DNS. ```bash echo nameserver 8.8.8.8 >> /etc/resolv.conf ``` 9) La dernière étape de l'installation de notre conteneur sera de la mettre a jour avec les commandes `apt-get update` et `apt-get full-upgrade`. ```bash lxc-attach -n $NAME -- apt-get update lxc-attach -n $NAME -- apt-get full-upgrade ``` ### C - Configuration des serveurs web Maintenant que toutes nos machines sont opérationnelles, nous allons passer à leur configuration. 1) Pour nos deux serveur web, nous installerons un serveur apache ainsi que les différentes applications transervales. ```bash lxc-attach -n server1 -- apt-get install apache2 libapache2-mod-php php mariadb-server lxc-attach -n server2 -- apt-get install apache2 libapache2-mod-php php mariadb-server ``` 2) Nous pouvons vérifier le bon fonctionnement du serveur web avec la commande `curl` : ``` curl 10.1.0.10 curl 10.1.0.20 ``` ![](https://i.imgur.com/6W0QdGS.png) ### D - Configuration du reverse proxy 1) Pour ce qui est de notre serveur reverse proxy, nous installerons un serveur haproxy qui occupera également la fonction d'équilibreur de charge ```bash lxc-attach -n serverProxy -- apt-get install haproxy ``` 2) Pour mettre en place notre reverse proxy, nous allons devoir éditer son fichier de configuration ```bash nano /etc/haproxy/haproxy.cfg ``` 3) Dans ce fichier nous ajouterons les lignes ci-dessous. Nous avons d'un coté la configuration d'une page de logs pour suivre l'état du proxy en temps réél sur le port 8404. De l'autre nous avons la configuration de notre équilibrage de charge en mode RoundRobin sur le port 80. ```yaml frontend stats mode http bind *:8080 stats enable stats hide-version stats refresh 10s stats uri /stats stats auth admin:passwd123 stats admin if LOCALHOST frontend frontweb bind *:80 mode http default_backend backweb backend backweb balance roundrobin server server1 10.1.0.10:80 check server server2 10.2.0.20:80 check ``` 4) Après avoir modifier la configuration d'haproxy il est nécessaire de rédémarrer le service pour appliquer les modifications. ```bash lxc-attach -n serverProxy -- systemctl restart haproxy ``` 5) Nous allons désormais vérifier la configuration du haproxy, pour cela nous allons modifier le contenu des deux fichiers webs pour vérifier le load balancing ```bash lxc-attach -n server1 -- bash -c "echo server1 > /var/www/html/index.html" lxc-attach -n server2 -- bash -c "echo server2 > /var/www/html/index.html" curl 10.1.0.30 # x4 ``` ![](https://i.imgur.com/pxk989z.png) 6) Nous allons également vérifier le fonctionnement de notre page de statistiques en essayant à accéder à la page `/stats` de notre proxy. ![](https://i.imgur.com/hC2gf5h.png) 7) En utilisant les identifiants rentrées dans la configuration (admin:passwd123), nous pouvons accédez à notre page. Sur cette page nous voyons retrouvons l'état de nos serveurs ainsi que le nombre de requête envoyé sur chaque serveur. ![](https://i.imgur.com/JpERsGr.png) ### E - Mise en place de la synchronisation des fichiers 1) Nous allons désormais mettre en place la synchronisation des différents serveurs web depuis notre backup. Nous utiliserons pour cela la commande `scp`. La première chose à faire sera donc d'installer un serveur SSH sur nos serveurs web. ```bash lxc-attach -n server1 -- apt-get install openssh-server lxc-attach -n server2 -- apt-get install openssh-server ``` 2) Pour réaliser notre connexion SSH automatiquement, nous aurons besoin d'une paire de clés. Nous utiliserons donc le module `ssh-keygen` pour les créer. Nous créerons également au préalable le dossier `.ssh` qui contiendra notre clé car ce dernier n'existe pas par défaut. ```bash lxc-attach -n serverBackup -- mkdir -m700 /root/.ssh lxc-attach -n serverBackup -- ssh-keygen -t rsa -b 4096 -f /root/.ssh/id_rsa -N "" -q ``` 3) Maintenant que nous avons nos clés, nous allons pouvoir ajouté la clé publique à chacun de nos serveurs web. Nous créerons également au préalable le dossier `.ssh` sur chacun des serveurs car ces derniers n'existent pas par défaut. Enfin nous attribuerons les droits 600 sur le fichier pour que celui puisse fonctionner correctement. (La deuxième commande peut paraître un peu longue mais elle permet d'éviter de nombreuses étapes intermédiaires) ```bash lxc-attach -n $NAME -- mkdir -m700 /root/.ssh lxc-attach -n $NAME -- bash -c "echo $(lxc-attach -n serverBackup -- cat /root/.ssh/id_rsa.pub) >> /root/.ssh/authorized_keys" lxc-attach -n $NAME -- chmod 600 /root/.ssh/authorized_keys ``` 4) Maintenant que nous avons configurer la communication entre les machines, nous allons récupérer, nous récupérer le contenue d'un des sites web, qu'il pourra ensuite partager. ```bash lxc-attach -n serverBackup -- bash -c "scp -o StrictHostKeyChecking=accept-new -pqr root@10.2.0.10:/var/www/html /root/html" ``` <br/> 5) Pour executer notre commande de synchronisation périodiquement, nous allons utiliser le module `cron`. Nous allons donc l'installer. ```bash lxc-attach -n serverBackup -- apt-get install cron ``` 6) Pour ajouter une nouvelle instruction cron, il faut exectuer la commande ci dessous. Cette dernière ouvre le fichier de configuration ```bash crontab -e ``` 7) Pour executer la synchronisation des deux serveurs toutes les 5 minutes, nous ajouterons les deux lignes suivantes à la fin du fichier. ```crontab */5 * * * * scp -o StrictHostKeyChecking=accept-new -pqr /root/html/index.html root@10.2.0.10:/var/www/html/index.html */5 * * * * scp -o StrictHostKeyChecking=accept-new -pqr /root/html/index.html root@10.2.0.20:/var/www/html/index.html ``` 8) Pour verifier le bon fonctionnement, nous pouvons attendre 5 minutes et regarder de nouveau le contenu renvoyé par le serveur proxy. Le contenue devrait normalement être identique. ![](https://i.imgur.com/qfZudn2.png) ### F - Configuration du service Fail2Ban 1) Tout d'abord installer fail2ban sur votre serveur proxy. ```bash apt install fail2ban ``` 2) Rendez-vous ensuite dans le fichier défauts de configuration fail2ban. ```bash vim /etc/fail2ban/jail.conf ``` 3) Modifier ensuite le fichier en rajoutant ces quelques lignes. Tout d'abord descender un peu dans le fichier jusqu'à trouver la ligne "backend" et modifiez-la comme-ci dessous. Cela permettra de résoudre les problèmes de démarrage lié aux logs. ```bash backend = systemd ``` 4) Remonter ensuite dans le fichier jusqu'à trouver la ligne "bantime" et modifiez-la comme-ci dessous pour appliquer un banissement d'ip d'une semaine. ```bash bantime = 604800 ``` 5) Si l'on regarde un peu plus bas nous pouvons remarquer la ligne "maxretry" laisser la par défaut elle permettra de bannir une ip au bout de 5 essaie échoué de tentative de connexion. ```bash maxretry = 5 ``` 6) Votre service fail2ban est désormais opérationnel. ## VI - Scripts ```bash #!/bin/bash #### #### #### CONFIGURE NETWORK #### #### #### echo -n "Configure network : " # Create network if [ -z $(ip -o -4 a | awk '/dmz1/ {print $2}') ]; then lxc network create dmz1 1>/dev/null fi if [ -z $(ip -o -4 a | awk '/dmz2/ {print $2}') ]; then lxc network create dmz2 1>/dev/null fi echo "OK" #### #### #### CONFIGURE CONTAINER #### #### #### echo -n "Configure container : " # Get the gateway and network of network dmz GATEWAY=$(ip -o -4 a show dmz1 | awk '{print $4}' | cut -d/ -f1) NETWORK=$(echo $GATEWAY | cut -d. -f1,2,3) GATEWAY2=$(ip -o -4 a show dmz2 | awk '{print $4}' | cut -d/ -f1) NETWORK2=$(echo $GATEWAY2 | cut -d. -f1,2,3) for i in server1:10:dmz1:$NETWORK:$GATEWAY:dmz2:$NETWORK2 server2:20:dmz1:$NETWORK:$GATEWAY:dmz2:$NETWORK2 serverProxy:30:dmz1:$NETWORK:$GATEWAY serverBackup:40:dmz2:$NETWORK2:$GATEWAY2 do IFS=: read hostname hostIp int net gw int2 net2 <<< "${i}" # Create container lxc-create -t download -n $hostname -- -d debian -r bullseye -a amd64 1>/dev/null # Place the containers in the DMZ and assign them a static IP address to the servers sed -i "s/lxc.net.0.link = .*/lxc.net.0.link = $int/" "/var/lib/lxc/$hostname/config" echo "lxc.net.0.ipv4.address = $net.$hostIp/24" >> "/var/lib/lxc/$hostname/config" echo "lxc.net.0.ipv4.gateway = $gw" >> "/var/lib/lxc/$hostname/config" # Add a second network if needed if [ ! -z $int2 ]; then echo " lxc.net.1.type = veth lxc.net.1.link = $int2 lxc.net.1.flags = up lxc.net.1.hwaddr = 00:16:3e$(od -txC -An -N3 /dev/random|tr \ :) lxc.net.1.ipv4.address = $net2.$hostIp/24 " >> "/var/lib/lxc/$hostname/config" fi # Start container lxc-start -n $hostname && lxc-wait -n $hostname -s RUNNING sleep .5 # Disable the DHCP service and restart the machine to recover the static ip lxc-attach -n $hostname -- bash -c 'systemctl disable systemd-networkd 2>/dev/null' lxc-stop -r -n $hostname && lxc-wait -n $hostname -s RUNNING sleep .5 # Enable DNS resolution lxc-attach -n $hostname -- bash -c 'echo nameserver 8.8.8.8 >> /etc/resolv.conf' # Update container lxc-attach -n $hostname -- bash -c "sed -i 's/# fr_FR.UTF-8 UTF-8/fr_FR.UTF-8 UTF-8/' /etc/locale.gen && locale-gen fr_FR.UTF-8 >/dev/null && update-locale LANG=fr_FR.UTF-8" lxc-attach -n $hostname -- bash -c 'apt-get update 1>/dev/null && apt-get full-upgrade -y 1>/dev/null' echo -n "." done echo "OK" #### #### #### CONFIGURE WEB SERVER #### #### #### echo -n "Configure web server : " # Install the Apache web servers lxc-attach -n server1 -- bash -c 'apt-get install apache2 libapache2-mod-php php mariadb-server -y 1>/dev/null 2>/dev/null' lxc-attach -n server2 -- bash -c 'apt-get install apache2 libapache2-mod-php php mariadb-server -y 1>/dev/null 2>/dev/null' echo "OK" #### #### CONFIGURE REVERSE PROXY SERVER #### #### echo -n "Configure reverse proxy server : " # Install the haproxy server lxc-attach -n serverProxy -- bash -c 'apt-get install haproxy -y 1>/dev/null 2>/dev/null' # Configure haproxy to do load balancing in roundrobin mode, also add a stats page, on port 8404, to track proxy status lxc-attach -n serverProxy -- bash -c "echo \" frontend stats mode http bind *:8404 stats enable stats hide-version stats refresh 10s stats uri /stats stats auth admin:password stats admin if LOCALHOST frontend frontendserver bind *:80 mode http default_backend backendserver backend backendserver balance roundrobin server server1 $NETWORK.10:80 check server server2 $NETWORK.20:80 check \" >> /etc/haproxy/haproxy.cfg" # Restart haproxy to apply the configuration lxc-attach -n serverProxy -- systemctl restart haproxy echo "OK" #### #### ## CONFIGURE WEB FILES SYNC ## #### #### echo -n "Configure web files sync : " # Generate an RSA key pair for our master web server lxc-attach -n serverBackup -- mkdir -m700 /root/.ssh lxc-attach -n serverBackup -- ssh-keygen -t rsa -b 4096 -f /root/.ssh/id_rsa -N "" -q for i in server1:10 server2:20; do IFS=: read hostname hostIp <<< "${i}" # Install an openssh server on the machine retrieving the data lxc-attach -n $hostname -- bash -c 'apt-get install -y openssh-server 1>/dev/null 2>/dev/null' # Send our previously generated public key to the listening server lxc-attach -n $hostname -- bash -c "mkdir -m700 /root/.ssh 2>/dev/null" lxc-attach -n $hostname -- bash -c "echo $(lxc-attach -n serverBackup -- cat /root/.ssh/id_rsa.pub) >> /root/.ssh/authorized_keys && chmod 600 /root/.ssh/authorized_keys" # Get a unique configuration from a web server lxc-attach -n serverBackup -- bash -c "scp -pqr root@$NETWORK2.$hostIp:/var/www/html /root/html >> /dev/null" # Install cron to run commands periodically lxc-attach -n serverBackup -- bash -c 'apt-get install cron -y 1>/dev/null 2>/dev/null' # Add a cron line to perform a synchronization to the listening server every 5 minutes lxc-attach -n serverBackup -- bash -c "(crontab -l 2>/dev/null; echo \"*/5 * * * * scp -pqr /root/html root@$NETWORK2.$hostIp:/var/www/html >> /dev/null\") | crontab -" done echo "OK" ```

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully