Le contexte

J’utilise Docker-compose et Traefik pour faire du développement local sur WordPress. J’ai un plugin WordPress qui utilise wp_get_remote pour lire un fichier .json et pour ensuite utiliser son contenu. Tout fonctionne sur un serveur distant. En local, avec LAMP et un certificat auto-signé par mkcert, je sais qu’il faut ajouter un hook pour faire fonctionner curl. Je pensais donc que la même chose pouvait s’appliquer avec Docker. La démarche est un peu plus compliquée.

Logo de curl

Premières manipulations

Comme, je le disais, avec un certificat auto-signé, il faut ajouter un hook WordPress pour que la fonction wp_get_remote fonctionne correctement. J’utilise pour cela un mu-plugin :

<?php
/**
 * Plugin Name: Local Certs
 * Plugin URI: https://stackoverflow.com/q/44632619/881743
 * Description: Ensure WordPress curl requests compatibility with local SSL certs. Based on Kevin Leary answer: https://github.com/FiloSottile/mkcert/issues/165#issuecomment-616002942
 * Version: 1.0.0
 * Author: Armand Philippot
 * Author URI: https://www.armandphilippot.com/
 * License: MIT
 */

/**
 * Replace Localhost cURL Certificates
 *
 * If the URL for an HTTP request in WordPress contains a custom TLD, replace
 * the WordPress local /certificates/ca-bundle.crt with the PHP supplied bundle.
 */
function local_certs_http_request_args( $parsed_args, $url ) {
	$custom_tld = array( 'test', 'local', 'localhost' );

	// Get the URL TLD
	$test_url = wp_parse_url( $url, PHP_URL_HOST );
	$test_tld = explode( '.', $test_url );
	$test_tld = end( $test_tld );

	if ( in_array( $test_tld, $custom_tld ) ) {
		// Replace WordPress local certificates with PHP's default cacert path
		$openssl_cert_locations = openssl_get_cert_locations();
		if ( isset( $openssl_cert_locations[ 'default_cert_file' ] ) ) {
			$parsed_args[ 'sslcertificates' ] = $openssl_cert_locations[ 'default_cert_file' ];
		}
	}

	return $parsed_args;
}
add_filter( 'http_request_args', 'local_certs_http_request_args', 10, 2 );

Je me doutais que ce ne serait pas suffisant. Docker risque de se plaindre à cause du certificat. J’ai tout de même testé pour m’en assurer.

1er problème : cURL Error 6: Could not resolve host

Il semble que même si j’arrive à accéder à mon site avec un serveur virtuel, via curl le nom de domaine n’est pas reconnu. Après quelques recherches, j’ai fini par trouver une piste : les alias de réseaux.

J’ai donc modifié la partie networks du service Traefik dans le fichier docker-compose.yml :

version: '3.8'

services:
  traefik:
    image: traefik:${DWP_TRAEFIK_VERSION:-2.3.1}
    container_name: dwp_traefik
    restart: on-failure
    ports:
      - '127.0.0.1:80:80'
      - '127.0.0.1:443:443'
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./traefik/traefik.yml:/etc/traefik/traefik.yml
      - ./traefik/ssl.yml:/etc/traefik/ssl.yml
      - ./certs:/etc/traefik/certs
    networks:
      dwp-proxy:
        aliases:
          - ${DWP_DOMAIN_NAME}
          - www.${DWP_DOMAIN_NAME}
    labels:
      - 'traefik.enable=true'
      - 'traefik.http.routers.traefik.entrypoints=https'
      - 'traefik.http.routers.traefik.rule=Host(`traefik.${DWP_DOMAIN_NAME}`)'
      - 'traefik.http.routers.traefik.service=api@internal'
      - 'traefik.http.routers.traefik.tls=true'
volumes:
  db_data:
networks:
  dwp-proxy:
    name: dwp-proxy
  dwp-web:
    name: dwp-web

Ensuite, il suffit d’arrêter puis de démarrer à nouveau les conteneurs :

sudo docker-compose stop
sudo docker-compose up -d

Ça fonctionne ! Enfin presque… Le nom de domaine est bien résolu, mais une nouvelle erreur pointe son nez.

2e problème : cURL Error 77: Error setting certificate verify locations

Ce problème, je m’y attendais. Docker a besoin du fichier rootCA généré par mkcert pour résoudre la commande curl. La solution n’est pas aussi simple. Il faut rentrer dans le conteneur pour mettre à jour les certificats manuellement.

Exemple : cURL error 77

Pour commencer, j’ai du copier les fichiers rootCA générés par mkcert dans le dossier ./certs que j’utilise déjà. Là, il faut soit les renommer en .crt au lieu de .pem pour qu’ils puissent être découverts automatiquement, soit faire un lien symbolique.

ln -s rootCA.pem rootCA.crt

Ensuite, j’ai ajouté un volume au service WordPress pour monter les certificats :

version: '3.8'

services:
  wordpress:
    depends_on:
      - db
    image: wordpress:${DWP_WORDPRESS_VERSION:-latest}
    container_name: dwp_wordpress
    restart: on-failure
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_NAME: ${DWP_DB_NAME:-wordpress}
      WORDPRESS_DB_USER: ${DWP_DB_USER:-wordpress}
      WORDPRESS_DB_PASSWORD: ${DWP_DB_PASSWORD:-wordpress}
      WORDPRESS_TABLE_PREFIX: ${DWP_TABLE_PREFIX:-wp_}
      WORDPRESS_CONFIG_EXTRA: |
        define( 'WP_DISABLE_FATAL_ERROR_HANDLER', true );
        define( 'WP_DEBUG', true );
        define( 'WP_DEBUG_LOG', true );
        define( 'WP_DEBUG_DISPLAY', true );
        define( 'SCRIPT_DEBUG', true );
        define( 'SAVEQUERIES', true );
    working_dir: /var/www/html
    volumes:
      - ./wp-content:/var/www/html/wp-content
      - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
      - ./certs:/usr/local/share/ca-certificates/certs
    networks:
      dwp-proxy:
      dwp-web:
    labels:
      - 'traefik.enable=true'
      - 'traefik.http.routers.wordpress.entrypoints=http,https,browserSync'
      - 'traefik.http.routers.wordpress.rule=Host(`www.${DWP_DOMAIN_NAME}`) || Host(`${DWP_DOMAIN_NAME}`)'
      - 'traefik.http.routers.wordpress.tls=true'
volumes:
  db_data:
networks:
  dwp-proxy:
    name: dwp-proxy
  dwp-web:
    name: dwp-web

Puis, encore une fois, j’ai arrêté et redémarré les conteneurs. Maintenant il faut entrer dans le conteneur de WordPress. Pour cela, j’ai exécuté :

sudo docker-compose exec wordpress bash

Dans le conteneur, j’ai alors exécuté la commande update-ca-certificate pour mettre à jour la liste des certificats.

Seulement, ça ne suffit pas… curl cherche le fichier au mauvais endroit : CAfile: /usr/lib/ssl/cert.pem. Ce fichier n’existe pas. Toujours dans le conteneur, j’ai donc exécuté :

ln -s /etc/ssl/certs/ca-certificates.crt /usr/lib/ssl/cert.pem
exit

Maintenant vous pouvez actualiser votre page, et curl devrait fonctionner !

3e problème : le lien symbolique n’est pas persistant

Si vous arrêtez vos conteneurs Docker pour les redémarrer ensuite, vous serez à nouveau confronté au problème n°2. Le lien symbolique est apparemment détruit au redémarrage.

Exemple : lien symbolique manquant

Je n’ai pas trouvé comment le rendre persistent. Ou plutôt, la seule solution semble de créer sa propre image WordPress mais je n’ai pas testé.

Il faut donc recréer le lien symbolique manuellement à chaque redémarrage des conteneurs.

4e problème : cURL error 60: SSL certificate problem

Ce problème n’en est pas réellement un si vous avez suivi toutes les étapes ci-dessus. Il survient si vous avez appliqué les différentes modifications proposées sans ajouter le hook WordPress.

Exemple : cURL error 60

La solution est donc simple : soit utiliser votre propre hook dans votre fichier functions.php soit copier le mu-plugins dans le répertoire wp-content/mu-plugins/.

Épilogue

Même si ce n’est pas la solution idéale, je m’en contenterais pour le moment et, si vous êtes confronté au même problème, j’espère que ça vous aidera.

Aucun commentaire sur “Utiliser curl avec WordPress et Docker” pour le moment.

S'abonner aux commentaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *