Sécurisation et mise en place de l'intégration continue pour une agence web via gitlab CE


    par Pilulu

    Contexte

    Quelques chiffres d'Axess OWS

    • ~300 jobs de déploiement sur jenkins dont 150 encore à migrer
    • ~140 déploiements via Gitlab CI le 05/12/2024
    • ~85 projets concernés
    • 22 développeurs
    • 4 architectes
    • 15 lignes pour ajouter une nouvelle CI

    Besoins initiaux

    • 1 seul outil à maintenir (ex: Jenkins/Gitlab)
    • Déployer sur nos machines ou celles des clients
    • Ne pas exposer les clés SSH lors du déploiement ou dans la configuration gitlab
    • Suivi détaillé des déploiements
    • Minimiser la configurations sur chaque projet
    • Mettre en pause
    • Déployer via SSH sur n'importe quel type d'environnement cible (bastion, VPN IPSEC)

    Besoins complémentaires

    • Conserver une méthodologie de déploiement la plus commune possible (appeler un script propre au projet)
    • Evolution commune des options/capacités de déploiements. Ex:
      • Initialisation (05/03/2024)
      • Choix de version NVM (11/04/2024)
      • Liste des commits déployés (26/07/2024)
      • Flush OPCache (22/09/2024)
      • Choix de version PHP (25/10/2024)
      • Ajout d'arguments pour le script de déploiement (25/10/2024)
    • Conserver Gitlab CE sans payer une version entreprise / premium / ultimate

    Evolutions possibles

    • Backup prod vers preprod
    • Affichage des fichiers non versionnés sur le serveur cible
    • Option de blocage du déploiement selon le point précédent

    Mise en oeuvre

    Downstream pipelines

    https://docs.gitlab.com/ee/ci/pipelines/downstream_pipelines.html
              
                deploy-dev:
                  stage: deploy
                  trigger:
                    project: ows/deploy
                    branch: 2.x
                    strategy: depend
                  rules:
                    - if: $CI_COMMIT_BRANCH == "dev"
                  variables:
                    HOST_SERVER: 'vmXX.ows.ows.fr'
                    SCRIPT: 'scripts/deploy.sh'
                    DEPLOY_TYPE: 'ows-deploy'
                    GITROOT: '/home/vhosts/PROJET'
                    TRIGGER_CI_JOB_NAME: ${CI_JOB_NAME}
                    TRIGGER_CI_PROJECT_ID: ${CI_PROJECT_ID}
              
            
    • Appel de la CI du projet ows/deploy sur sa branche 2.x
    • Passage du minimum d'informations nécessaire aux déploiement en tant que variable
    • Condition de déclenchement du job de déploiement sur sa branche de dev

    Avantages

    • Séparation des droits entre le projet à déployer et le projet de déploiement
    • Renseignement des paramètres au minimum vital
    • Conservation d'une maitrise des évolutions du projet de déploiement via la notion de version

    Inconvénients

    • Parcours plus complexe de la pipeline et des jobs
    • Cout de mise en oeuvre sur le socle commun plus important pour toute nouvelle fonctionnalité
    • Les développeurs doivent avoir un role minimum de développeur sur le projet de déploiement

    Sécurisation du projet Gitlab

    • Seuls les maintainers du projet peuvent configurer les variables d'environnement de la CI
    • Seul les branches protégées peuvent avoir la clé privée ssh injectée
    • Seul les maintainers peuvent contribuer sur les branches protégées
    • Les développeurs ont seulement le droit de fusionner (suffit pour voir la CI de déploiement)
    • Les fusions et forks (merge depuis un fork) sont désactivés

    Sécurisation du code

    • Utilisation d'expressions régulières sur les variables d'environnement
    • Ne jamais faire confiance à des variables injectées depuis l'extérieur
    • Limiter chaque variable à son strict usage
                
                  variables:
                    FF_SCRIPT_SECTIONS: 1
                    FF_TIMESTAMPS: 1
                    HOST_PATTERN: "/[a-zA-Z0-9.\\-]+/"
                    VARIABLES_PATTERN: "/[a-zA-Z0-9_\\-._\\\/ ]+/"
                    INTEGER_PATTERN: "/[0-9]+/"
                  ...
                  deploy:
                    script:
                      - bashenv < scripts/diff_commits.sh|ssh ${HOST_SERVER} -tt
                      - bashenv < scripts/opcache_reset.sh|bash
                      - bashenv < scripts/deploy.sh|ssh ${HOST_SERVER} -tt
                    stage: deploy
                    rules:
                      - if: $HOST_SERVER != '' &&
                            $SCRIPT != '' &&
                            $HOST_SERVER =~ $HOST_PATTERN &&
                            $SCRIPT =~ $VARIABLES_PATTERN &&
                            ($SCRIPT_ARGS == null || $SCRIPT_ARGS =~ $VARIABLES_PATTERN) &&
                            $GITROOT =~ $VARIABLES_PATTERN &&
                            ($NVM_VERSION == null || $NVM_VERSION =~ $INTEGER_PATTERN)
                
              

    Sécurisation du runner

    • Un container par éxecution, l'usage d'un runner persistant et commun à d'autres projets comme Shell met en péril la sécurité
    • L'image par défaut du runner doit être la plus simple et rester à jour depuis une source fiable
    
              concurrent = 4
    
              [[runners]]
              name = "myRunner"
              url = "https://gitlab.com/ci"
              token = "......"
              executor = "docker"
              [runners.docker]
                tls_verify = true
                image = "docker:stable"
                ...
            

    Le projet de déploiement

    Organisation

    
                .
                ├── README.md
                ├── scripts
                │   ├── deploy.sh
                │   ├── diff_commits.sh
                │   ├── opcache_reset.php
                │   └── opcache_reset.sh
                ├── ssh
                │   ├── aaa-config
                │   ├── bbb-config
                │   ├── ccc-config
                │   ├── ddd-config
                │   ├── eee-config
                │   ├── fff-config
                │   ├── ows-config
                │   └── ggg-config
                └── templates
                    ├── deploy
                    │   ├── aaa-deploy.yml
                    │   ├── bbb-deploy.yml
                    │   ├── ccc-deploy.yml
                    │   ├── ddd-deploy.yml
                    │   ├── includes
                    │   │   └── deploy.yml
                    │   ├── eee-deploy.yml
                    │   ├── ows-deploy.yml
                    │   └── fff-deploy.yml
                    ├── deploy.yml
                    └── validate.yml
              
    • Un dossier pour les config ssh selon les environnements cibles (bastions à traverser)
    • Un dossier pour altérer le chargemenr des configs ssh et autres particularités de déploiements
    • Un dossier pour les scripts, laisson le shell aux fichiers .sh plutôt que du shell embarqué dans les yaml