Search code examples
phpdockerssldockerfilessl-certificate

Why php does not recognise the user-installed CA?


For a dockerised solution I created my own CA certificates:

  • ca.crt
  • ca.key

And upon dockerfile I place them into /usr/local/share/ca-certificates/

FROM php:7.1-fpm

COPY ./ssl/ca/ca.crt /usr/local/share/ca-certificates/php-www.ca
RUN chmod 644 /usr/local/share/ca-certificates/foo.crt && update-ca-certificates

But once I run the script:


$contents = file_get_contents("https://custom_ca_url.local");
echo $contents;

I get the error:

PHP Warning:  file_get_contents(): SSL operation failed with code 1. OpenSSL Error messages:
error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed in php shell code on line 1
PHP Stack trace:
PHP   1. {main}() php shell code:0
PHP   2. file_get_contents(*uninitialized*) php shell code:1
PHP Warning:  file_get_contents(): Failed to enable crypto in php shell code on line 1
PHP Stack trace:
PHP   1. {main}() php shell code:0
PHP   2. file_get_contents(*uninitialized*) php shell code:1
PHP Warning:  file_get_contents(https://polihome.local): failed to open stream: operation failed in php shell code on line 1
PHP Stack trace:
PHP   1. {main}() php shell code:0
PHP   2. file_get_contents(*uninitialized*) php shell code:1

What I want to achieve is the php to use the server's ca certificates including the custom one in order to verify any certificate signed with my own CA.

Any cert has been generated as:

#!/usr/bin/env bash

#  Development Certificate Generator
#  Copyright (C) 2023  Dimitrios Desyllas
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
#

SCRIPT="$(readlink --canonicalize-existing "$0")"
SCRIPTPATH="$(dirname "$SCRIPT")"
SSL_PATH="${SCRIPTPATH}/../ssl"

CERT_PATH="${SSL_PATH}/certs"
SSL_CONF_PATH="${SSL_PATH}/conf/ssl_conf"
SIGNING_REQUEST_CONF="${SSL_PATH}/conf/v3.sign"

CA_PATH="${SSL_PATH}/ca"
CA_KEY=${CA_PATH}/ca.key 
CA_CERT=${CA_PATH}/ca.crt 

if  [[ ! -f ${CA_CERT} ]] || [[ ! -f ${CA_KEY} ]]; then

    echo "==Create CA==" 
    openssl genrsa -out ${CA_KEY} 2048
    openssl req -x509 -new -nodes \
            -key ${CA_KEY} -subj "/C=GR/L=ATTICA" \
            -days 1825 -out ${CA_CERT}

fi

CERT_BASENAME="www"

CERTIFICATE_PATH=${CERT_PATH}/${CERT_BASENAME}.crt
KEY_PATH=${CERT_PATH}/${CERT_BASENAME}.key
SIGNING_REQUEST=${CERT_PATH}/${CERT_BASENAME}.csr

echo "Cleanup old certicicates from previous execution"
rm -rf ${CERTIFICATE_PATH}
rm -rf ${KEY_PATH}
rm -rf ${SIGNING_REQUEST}

echo "CREATING CERTIFICATE"

openssl req -new -sha512 -keyout ${KEY_PATH} -nodes -out ${SIGNING_REQUEST} -config ${SSL_CONF_PATH}

echo $CERTIFICATE_PATH

echo "SIGNING CERTIFICATE using CA"
openssl x509 -req -days 9000 -startdate -sha512 -in ${SIGNING_REQUEST} \
     -CAkey ${CA_KEY} -CA ${CA_CERT} -CAcreateserial \
     -extfile ${SIGNING_REQUEST_CONF} \
     -out ${CERTIFICATE_PATH}

echo "#######################################"
echo "IMPORT these cert into your system as CA cert:"
echo -e "\033[0;96m${CA_CERT}\033[0m"
echo
echo The generated certificates are:
echo -e "CERT: \033[0;96m${CERTIFICATE_PATH}\033[0m"
echo -e "KEY: \033[0;96m${KEY_PATH}\033[0m"

And the nginx has been properly configured with the following ssl:


    server {
        listen [::]:443 ssl ipv6only=on;
        listen 443 ssl;

        ssl_certificate /etc/nginx/ssl/www.crt;
        ssl_certificate_key /etc/nginx/ssl/www.key;
        
        server_name custom_ca_url.local;

        set_root /var/www/public_html;

        # misc settings here
   }

Do you know how I can verify certs in php using the system's CA files?


Solution

  • As far as I understand you do not want to tinker the settings openssl.cafile= or openssl.capath= at php.ini.

    In order to achieve that, this answer explains https://stackoverflow.com/a/59960897/4706711

    You need in your Dockerfile to place theese settings:

    COPY ./ssl/ca/ca.crt /usr/local/share/ca-certificates/php-www-ca.crt
    RUN chmod 644 /usr/local/share/ca-certificates/php-www-ca.crt && \
        echo php-www-ca.crt >> /etc/ca-certificates.conf && \
        update-ca-certificates
    

    This is applicable at debian-based oficial docker php images.