Le guide
  • 📖Qui sommes-nous ?
  • prĂ©vention
    • đŸ“±Les dangers des rĂ©seaux sociaux
    • 🔑Qu'est ce qu'un ransomware
    • 👀C'est quoi le phishing ?
    • 🌐Que peut-on faire avec une IP ?
    • Comment se rĂ©tracter lors d'un achat (ecommerce) ?
  • urgence
    • ⚡Cybermalveillance
    • đŸ©čKit d'aide digital
    • 🚹Infos d'urgences
    • 📘Dernier poste
  • HELP
    • Dont ask to ask
    • Une alternative ?
    • 🎓Apprendre
      • 🇬🇧 Apprendre autre chose
      • Apprendre la cybersĂ©curitĂ©
      • Apprendre le dĂ©veloppement
      • liens utiles cybersec
      • Apprendre la crĂ©ation de jeux
      • Apprendre l’intelligence artificiel
      • Sauvegarder sous toutes les formes
  • Projets
    • Blukeys (en cours)
    • CommunautĂ©s
      • Quark
  • Français
    • Comment bien Ă©crire une liste ?
  • CybersĂ©curitĂ© - Hack
    • Outils
      • Introduction au reverse engineering
      • Outils de Base (introduction au Pentesting et la cybersĂ©curitĂ©)
      • BurpSuite - Intercepter toutes les requĂȘtes HTTP
      • 🇬🇧 fabacab/awesome-cybersecurity-blueteam
      • 🇬🇧 fabionoth/awesome-cyber-security
      • 🇬🇧 Bypassing Cloudflare WAF with the origin server IP address | Detectify Blog
    • OWASP Top 10
    • On dit chiffrer plutĂŽt que crypter
    • Web security academy - WSA
    • Write-up ctf
      • TryHackMe - Retro
    • SANS - Formations en cybersĂ©curitĂ©
  • DĂ©veloppement logiciels
    • Par oĂč commencer ?
    • Principes et bonnes pratiques
      • POO (Programmation OrientĂ© Objet)
    • Langages informatique
      • CSS, SASS
      • HTML
      • L'algorithmique
      • La famille C
      • La famille JS, TS
      • La famille JVM
      • PHP
      • Python
      • Ruby
      • RUST
      • SQL
    • Technologies
      • C'est quoi le Cloud Native ?
      • Installation Sqlite3 avec NPM ou Yarn
      • Les IDE recommandĂ©s
      • Base de donnĂ©es
      • DĂ©veloppement web sur windows
    • Toute les documentations
  • Cryptomonnaie
    • Tips navigateur Brave | Laisser un pourboire
  • Infrastructure
    • GĂ©nĂ©rateur de configuration SSL
  • QualitĂ© ingĂ©nierie logicielle
    • CI/CD DĂ©v
    • CI/CD d'une infra
    • 🇬🇧 Introduction Mangle enables you to run chaos engineering experiments
      • Overview
      • Mangle Deployment and Administration Guide
      • Mangle Users Guide
      • Mangle Troubleshooting Guide
      • Mangle Developers' Guide
      • Contributing to Mangle
      • mangle-administration
        • Supported Deployment Models
          • Advanced Cassandra Configuration
        • Admin Settings
      • sre-developers-and-users
        • Adding Endpoints
        • Injecting Faults
          • Infrastructure Faults
          • Application Faults
          • Custom Faults
        • Requests and Reports
      • troubleshooting-guide
        • Deployment Stage
        • Boot/Initialization Stage
        • Endpoint Addition Stage
        • Fault Injection Stage
  • Web
    • Inspecteur de code web
    • Liste des APIs publique ( en cours )
    • Apprendre le SEO naturel
    • Tips pour fusionner des communautĂ©s Discord
    • Qu’est-ce que le rp ?
    • Google Admin Toolbox HAR Analyzer
  • Couche haute
    • HUGO
      • 🇬🇧 Quick Start
      • Mettre en place son premier site sous Hugo
    • 🇬🇧 Deno Introduction with Practical Examples
    • React-native
    • 🇬🇧 Angular HttpClient v9/8
    • 🇬🇧 Compiler un front Angular variabilisĂ© comme un chef
    • 🇬🇧 Applying Angular Runtime Configurations in Dockerized Environments | Hacker Noon
  • Gaming
    • Installer Shadow Linux et Chrome book
    • Comment choisir son alimentation d'ordi ?
  • Linux
    • Git
    • Bash
    • Bases de linux
    • Installation LEMP sur Ubuntu 20.04 Digitalocean
    • 🇬🇧 Wireguard VPN on Ubuntu 20.04
    • Comment installer et utiliser Docker sur Ubuntu 20.04 | DigitalOcean
    • SĂ©curiser son serveur Linux sous Ubuntu
    • Introduction Ă  tmux (terminal multiplexer)
  • Bot
    • HĂ©berger son bot Discord
  • setup
    • Rainmeter personnalise ton Win10
  • Virtualisation
    • 🇬🇧 Dockerize Angular 9 App With Nginx
    • 🇬🇧 How To Remove Docker Images, Containers, Networks & Volumes
  • Architecture
    • L'Art De CrĂ©er Des Diagrammes d'Architecture
  • Marketing
    • Tips sur les rĂ©seaux sociaux 2021
  • Gestions IT (pro/perso)
    • MĂ©thodes Agiles
    • Savoir utiliser la mĂ©thode GTD (Getting Things Done)
    • MĂ©thode QQCOQP : analyse et rĂ©solution des problĂšmes
  • NouveautĂ©s
    • 🇬🇧 Netflix-technologies
      • How Netflix Scales its API with GraphQL Federation (Part 1)
      • How Netflix Scales its API with GraphQL Federation (Part 2)
  • Autres
    • A classer
  • How to install Windows 10 root certificates [EASY STEPS]
  • 💕Nous soutenons
    • đŸ’»Azales
    • Papi Uzumaki
    • đŸ’»Beau de l'aire
    • Toshi
    • đŸ›Ąïž FCC (French Communuty Cybersecurity)
    • đŸ›ĄïžSharpforce
Propulsé par GitBook
Sur cette page
  • Utiliser un serveur statique comme image de base
  • Personnalisation du docker-entrypoint
  • Reality Check

Cet article vous a-t-il été utile ?

Exporter en PDF
  1. Couche haute

🇬🇧 Compiler un front Angular variabilisĂ© comme un chef

Posté le 11/08/2020 par Florent Jaby

PrĂ©cĂ©dent🇬🇧 Angular HttpClient v9/8Suivant🇬🇧 Applying Angular Runtime Configurations in Dockerized Environments | Hacker Noon

DerniĂšre mise Ă  jour il y a 4 ans

Cet article vous a-t-il été utile ?

Si vous vivez dans le prĂ©sent, voire un peu dans le passĂ©, vous avez sĂ»rement une application de type rĂ©alisĂ©e avec le framework Angular. Vu que vous vivez dans le prĂ©sent, vous avez sĂ»rement envie de suivre un processus de dĂ©veloppement et de livraison sain, avec promotion d’une mĂȘme version d’un artefact Ă  travers plusieurs environnements (test, intĂ©gration, recette, prĂ©production, production, etc.)

Seulement voilĂ , Angular, Ă  l’instar de beaucoup d’autres frameworks front-end, vous inflige de construire/compiler votre application une fois par environnement en collant la configuration associĂ©e dans un fichier qui porte le nom de l’environnement cible.

Cela pousse en général aux défauts suivants :

  • Des secrets ajoutĂ©s au dĂ©pĂŽt de code dans ces fichiers de configuration

  • Un livrable diffĂ©rent par environnement, avec potentiellement des comportements diffĂ©rents

  • Aucune injection possible de configuration sans ajouter au code

  • Pas possible d’imaginer d’autres environnements sans ajouter au code, par exemple des pour votre application

Si vous voulez plus d’informations sur pourquoi ces points sont des dĂ©fauts, je vous invite Ă  lire les chapitres , et de (la rĂ©fĂ©rence en ce qui concerne les applications Cloud Ready / Native).

Nous souhaitons les caractéristiques suivantes pour notre application :

  • Constuire un seul artefact pour toutes nos cibles de dĂ©ploiement

  • Injecter la configuration spĂ©cifique par les variables d’environnement

  • Adapter le comportement de l’application, typiquement la balise en fonction d’une configuration

  • Un dĂ©ploiement identique Ă  n’importe quel autre type d’application

Sur mon projet, tous nos applicatifs sont packagĂ©s avec Docker et deployĂ©s soit sur des VMs avec Ansible soit dans un cluster Kubernetes. On a donc cherchĂ© Ă  packager notre application Angular de la mĂȘme maniĂšre.

Utiliser un serveur statique comme image de base

Nous avons choisi comme image de base. Dans notre SI, tous les applicatifs parlent HTTP. Pour faire rentrer notre application web dans le moule, il fallait donc que son image suive les mĂȘmes principes que nos briques d’API par exemple :

  • J’écoute sur un PORT connu (ici 80 car la terminaison TLS est faite soit par ingress soit par un F5)

  • Je sais sur quel prĂ©fixe d’URI je suis exposĂ© via la configuration BASE_URL (par exemple : https://www.domaine.fr/mon-appli)

Voici donc Ă  quoi ressemble notre Dockerfile :

# BUILD
FROM node:slim AS build

COPY . /app
WORKDIR /app
RUN npm ci && npx ng build --prod --base-href '\${BASE_URL}'

# RUN
FROM nginx:alpine AS run

COPY ./docker/docker-entrypoint.sh /usr/bin/
COPY ./docker/nginx.front.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist/my-app /usr/share/nginx/html

EXPOSE 80
ENV PORT=80
ENV BASE_URL=/
ENV API_BASE_URL # ex: https://api.domaine.fr/mon-api
ENV AUTH_BASE_URL # ex: https://auth.domaine.fr/oidc
ENV AUTH_CLIENT_ID

ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

Multi Stage Build

Nous pouvons faire ceci car l’output de la commande ng build se trouve entiĂšrement dans le dossier /app/dist/ et ne nĂ©cessite aucune execution par le serveur. (pas besoin non plus de nettoyer les dĂ©pendances avec npm prune --production)

La derniĂšre ligne CMD ["nginx", "-g", "daemon off;"] quant Ă  elle indique simplement qu’on dĂ©marre le serveur Nginx au premier plan avec la configuration que nous lui avons copiĂ©e. C’est le dossier dist/ qui sera servi statiquement pour toutes les requĂȘtes entrantes sur le port 80.

Personnalisation du docker-entrypoint

Je n’ai pas encore parlĂ© de la curieuse ligne RUN npm ci && npx ng build --prod --base-href '\${BASE_URL}'. Attention ici, l’objectif n’est pas de faire la substitution de valeur au moment du docker build mais bien d’inscrire dans le fichier gĂ©nĂ©rĂ© le placeholder ${BASE_URL}. Il est donc important de respecter l’echappement. L’option --base-href '\${BASE_URL}' aura pour consĂ©quence de gĂ©nerer un index.html qui a cette tĂȘte lĂ .

<!DOCTYPE html>
<html lang="fr">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />

    <title>My App</title>
    <base href="${BASE_URL}"> <!-- Notez ici le placeholder -->

    <link rel="icon" type="image/x-icon" href="favicon.ico" />
    <script type="text/javascript" src="assets/config.js"></script>
    <link rel="stylesheet" href="styles.6eceeb0d6ef142ef60a2.css">
  </head>
  <body>
    <app-root></app-root>
    <script src="runtime.c51bd5b1c616d9ffddc1.js" defer></script>
    <script src="polyfills.fb97d1264b2f08af2d29.js" defer></script>
    <script src="main.06f0c73f95c40c9adfb7.js" defer></script>
  </body>
</html>

On peut remarquer la ligne <base href="${BASE_URL}">. C’est vraiment cette version avec un placeholder qui est stockĂ©e dans l’image. En revanche, ce ne sera jamais cette version qui sera servie. En effet, dans notre Dockerfile nous avons prĂ©cisĂ© aussi :

COPY ./docker/docker-entrypoint.sh /usr/bin/
# ...
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]

Pour Docker, l’entrypoint est l’executable qui sera lancĂ©e dans le conteneur une fois l’image lancĂ©e. Ses arguments seront le contenu de CMD. En pratique, nous l’utilisons pour exĂ©cuter du code avant de lancer rĂ©ellement Nginx. Voici le contenu de notre entrypoint

#!/bin/sh
set -e

INDEX_PATH=/usr/share/nginx/html/index.html
TEMP_INDEX=$(mktemp)

envsubst < ${INDEX_PATH} > ${TEMP_INDEX}
mv ${TEMP_INDEX} ${INDEX_PATH}
chmod a+r ${INDEX_PATH}

exec "$@" # Lancer CMD dans le shell courant

envsubst est un petit programme, installĂ© sur Ă  peu prĂšs toutes les distributions *nix, qui se charge de remplacer les chaĂźnes de caractĂšres de la forme ${MA_VARIABLE} par la valeur prĂ©sente dans l’environnement. On change donc le contenu de notre index.html juste avant de lancer Nginx pour modifier cette ligne

    <base href="${BASE_URL}">

en cette ligne

    <base href="https://www.domaine.fr/mon-appli/">

Templatisation de config.js

En ce qui concerne la configuration spécifique à notre application (API_BASE_URL, AUTH_BASE_URL, etc.), nous utilisons un service Angular ConfigService

@Injectable()
export class ConfigService implements Configuration {
  baseApiUrl: string
  baseUrl: string
  authBaseUrl: string
  authClientId: string

  constructor () {
    const browserWindow = window || {}
    const browserWindowConfig = browserWindow['config'] || {}
    for (const key in browserWindowConfig) {
      if (browserWindowConfig.hasOwnProperty(key)) {
        this[key] = browserWindowConfig[key]
      }
    }
  }
}

Ce service cherche dans l’espace global (window) un objet nommĂ© 'config' et en copie toutes les clĂ©s. Cet objet est initialisĂ© par notre fichier assets/config.js importĂ© depuis notre index.html. En revanche, lorsque nous construisons l’image, c’est seulement template.config.js que nous incorporons dans notre container. Voici Ă  quoi il ressemble :

(function (window) {
  const config = {
    baseUrl: '${BASE_URL}',
    baseApiUrl: '${API_BASE_URL}',
    authBaseUrl: '${AUTH_BASE_URL}'
    authClientId: '${AUTH_CLIENT_ID}'
  }
  window.config = window.config || config
})(this)

Nous pouvons modifier notre docker-entrypoint.sh pour utiliser ce template :

#!/bin/sh
set -e

INDEX_PATH=/usr/share/nginx/html/index.html
TEMP_INDEX=$(mktemp)

envsubst < ${INDEX_PATH} > ${TEMP_INDEX}
mv ${TEMP_INDEX} ${INDEX_PATH}
chmod a+r ${INDEX_PATH}

CONFIG_PATH=/usr/share/nginx/html/assets/config.js
CONFIG_TEMPLATE_PATH=/usr/share/nginx/html/assets/template.config.js
if test -f ${CONFIG_TEMPLATE_PATH}; then
  envsubst < ${CONFIG_TEMPLATE_PATH} > ${CONFIG_PATH}
fi

exec "$@" # Lancer CMD dans le shell courant

Et zou !

Reality Check

Maintenant que nous avons fait tout ça, vérifions donc que nos critÚres sont bien remplis :

  • Notre artefact est une image Docker que l’on peut balader sur plusieurs environnements, facilement transmissible.

  • Nous pouvons configurer notre application grĂące aux variables d’environnements que l’on donne au dĂ©marrage de cette image afin d’adapter le comportement de l’application Ă  son dĂ©ploiement.

  • Nous dĂ©ployons un service HTTP tout simple, il suffit donc de diriger les connexions vers notre container.

  • Aucune incidence pour travailler localement avec ng serve

Cette approche nous permet également de faire plus facilement certaines choses :

  • Utiliser un registre docker pour la promotion d’une version de notre application avec les tags

  • Faire une configuration aux petits oignons de notre exposition HTTP, notamment en ce qui concerne les stratĂ©gies de cache ou des redirections

  • Rajouter une protection de type BasicAuth Ă  notre application si on veut limiter la population qui y a accĂšs

  • GĂ©rer la terminaison TLS directement dans nginx pour un chiffrement en profondeur

Cette maniĂšre de packager et livrer nos applications Angular nous a bien aidĂ©, en particulier en rentrant dans le moule de tous nos processus de livraison pour la gestion de la configuration et l’exposition. Je vous recommande d’essayer, en particulier si vous entrez dans une logique de review apps et que vous souhaitez livrer exactement ce que vous avez validĂ© !

J’imagine que cette approche est transposable à d’autres frameworks front. Comment cela se passe-t-il pour vous avec Vue, React ou Ember ?

Ce Dockerfile dĂ©crit un . C’est une mĂ©canisme proposĂ© pour utiliser des images de bases diffĂ©rentes pour la phase de construction de l’image Ă  proprement parler et la phase d’execution. On utilise node:slim pour construire l’application Angular car nous avons besoin des executables node, npm et npx. Ensuite, nous utilisons l’image de base nginx:alpine pour disposer d’un serveur HTTP statique plutĂŽt lĂ©ger. nous utilisons une configuration spĂ©ciale nginx.front.conf qui vient de notre base de code. Elle ne fait rien d’intĂ©ressant Ă  part le strict minimum, ce qui n’est pas forcĂ©ment le cas de la configuration de base. Elle sert Ă©galement Ă  servir le mĂȘme index.html mĂȘme pour les routes enfants afin que ce soit bien notre application quoi soit chargĂ©e Ă  chaque fois et prenne en charge le routage cĂŽtĂ© navigateur.

Pour avoir plus d’informations sur cette commande, tapez man envsubst dans votre terminal prĂ©fĂ©rĂ©

Ici nous bĂ©nĂ©ficions du fait que notre application n’est exposĂ©e que sur un seul prĂ©fixe d’URL ce qui n’est pas forcĂ©ment le cas pour vos applications. Dans un cas pareil, vous pourrez soit dĂ©ployer 2 fois la mĂȘme image avec 2 configurations diffĂ©rentes (complexitĂ© dans le dĂ©ploiement), ou alors vous orienter vers la solutions de la plus adaptĂ©e Ă  votre framework (complexitĂ© dans l’applicatif)

Source :

Multi Stage Build
Server-Side Rendering
https://blog.octo.com/compiler-un-front-angular-variabilise-comme-un-chef/
SPA
review apps
3
5
10
twelve-factor apps
<base href="...">
nginx:alpine
😉