webpack serve avec certificats

Vous venez de démarrer votre nouveau projet web avec le dernier framework Javascript frontend à la mode. Pour vos développement en local, c’est webpack qui fait office de serveur Web.

Personnellement, j’aime bien développer dans des conditions au plus proche de la futur production. Et je trouve que de ne pas développer avec un certificat SSL est un manque.

Voici donc comment je m’y suis pris pour mes projets.

Tout d’abord, il nous faut un outil en charge de la génération d’un certificat local et de l’installation de l’authorité de certification dans les navigateurs. Pour cela, j’ai opté pour mkcert.

Le fonctionnement est simple, il suffit de lancer une commande mkcert localhost 127.0.0.1 pour générer un certificat autosigné fonctionant pour localhost ou 127.0.0.1. Ensuite, il n’y a plus qu’à lancer mkcert -install et votre navigateur reconnaitra ce certificat.

Une dernière commande fort utile pour la suite: mkcert -CAROOT retournera le chemin dans lequel se trouve l’authorité de certification.

Pour intégrer, ce nouveau certificat dans mes développements, j’ai ensuite déclaré tous les chemins dans des variables d’environnement à l’ouverture de mon shell:

export MKCERT_HOME="/home/mlvtito/Softwares/mkcert"
export MKCERT_CERT="${MKCERT_HOME}/localhost+1.pem"
export MKCERT_CERT_KEY="${MKCERT_HOME}/localhost+1-key.pem"
export MKCERT_CAROOT="$(mkcert -CAROOT)"
export MKCERT_CA_CERT="${MKCERT_CAROOT}/rootCA.pem"
export MKCERT_CA_CERT_KEY="${MKCERT_CAROOT}/rootCA-key.pem"

Enfin, il ne reste plus qu’à adapter le fichier webpack.config.js conformément à la documentation pour activer le SSL à la présence des variables d’environnement.

module.exports = function (env, { analyze }) {
    const production = env.production || process.env.NODE_ENV === 'production';
    let devServer = {
        https: false,
        historyApiFallback: true,
        open: !process.env.CI,
        port: 9000,
        lazy: false,
        proxy: {
            "/api": {
                target: "http://localhost:8080/solv/"
            }
        }
    };

    if (process.env.MKCERT_CA_CERT && process.env.MKCERT_CERT && process.env.MKCERT_CERT_KEY) {
        devServer.https = true;
        devServer.key = fs.readFileSync(process.env.MKCERT_CERT_KEY);
        devServer.cert = fs.readFileSync(process.env.MKCERT_CERT);
        devServer.ca = fs.readFileSync(process.env.MKCERT_CA_CERT);
    }

    return {
        mode: production ? 'production' : 'development',
        devtool: production ? 'source-map' : 'inline-source-map',
        entry: './src/main.ts',
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: 'entry-bundle.js'
        },
        resolve: {
            extensions: ['.ts', '.js'],
            modules: [path.resolve(__dirname, 'src'), 'node_modules']
        },
        devServer: devServer,
        module: {
            rules: [
                {test: /\.(png|gif|jpg|cur)$/i, loader: 'url-loader', options: {limit: 8192}},
                {test: /\.woff2(\?v=[0-9]\.[0-9]\.[0-9])?$/i, loader: 'url-loader', options: {limit: 10000, mimetype: 'application/font-woff2'}},
                {test: /\.woff(\?v=[0-9]\.[0-9]\.[0-9])?$/i, loader: 'url-loader', options: {limit: 10000, mimetype: 'application/font-woff'}},
                {test: /\.(ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/i, loader: 'file-loader'},
                {test: /\.css$/i, use: ['style-loader', cssLoader, postcssLoader]},
                {test: /\.ts$/i, use: ['ts-loader', '@aurelia/webpack-loader'], exclude: /node_modules/},
                {test: /\.html$/i, use: '@aurelia/webpack-loader', exclude: /node_modules/}
            ]
        },
        plugins: [
            new HtmlWebpackPlugin({template: 'index.ejs'}),
            analyze && new BundleAnalyzerPlugin()
        ].filter(p => p)
};
};