Symfony:如何在 Doctrine DBAL 配置(YAML)中设置 SSL 参数?

Symfony : how to set SSL parameters in Doctrine DBAL configuration (YAML)?

我想将我的 SSL 证书和密钥文件添加到 Doctrine DBAL 配置中,但我不知道如何实现。

在PHP中,我只需要写这样的东西:

$databaseHandler = new \PDO(
    'mysql:host=my_host;dbname=my_db',
    'username',
    'password',
    array(
        \PDO::MYSQL_ATTR_SSL_KEY   => '.../client-key.pem',
        \PDO::MYSQL_ATTR_SSL_CERT  => '.../client-cert.pem',
        \PDO::MYSQL_ATTR_SSL_CA    => '.../ca-cert.pem'
    )
);

我知道有一个 Custom Driver Option driverOptions, and I saw this answer 但我不确定如何将其转换为 YAML。

我觉得我应该写一些接近 :

的东西
doctrine:
    dbal:
        driver:   "%database_driver%"
        host:     "%database_host%"
        port:     "%database_port%"
        dbname:   "%database_name%"
        user:     "%database_user%"
        password: "%database_password%"
        charset:  UTF8
        driverOptions:
            PDO::MYSQL_ATTR_SSL_CA: '.../client-key.pem'
            PDO::MYSQL_ATTR_SSL_CERT: '.../client-cert.pem'
            PDO::MYSQL_ATTR_SSL_CA: '.../ca-cert.pem'

但双冒号不会真正取悦 YAML...

而不是 PDO 常量,您应该在 options:

中使用它们的值
doctrine:
    dbal:
        connections:
            default:
                driver:   %database_driver%
                host:     %database_host%
                port:     %database_port%
                dbname:   %database_name%
                password: %database_password%
                charset:  UTF8
                options:
                    1010 : %private_key% 
                    1011 : %public_cert% 
                    1012 : %ca_cert%

通过 yaml(可能 xml)的 Symfony 配置不允许动态设置键,这意味着您不能使用常量。为了解决这个问题,您可以创建一个额外的 PHP 配置文件,它只处理从常量中创建一个键。

Gist 中的解决方案在这里:https://gist.github.com/samsch/d5243de3924a8ad10df2

它利用的两个主要特点是 PHP 配置文件可以使用任何字符串值作为键,包括变量、常量;并且您可以使用参数作为其他参数的值(这是我最近尝试之前才知道的。)

因此,您在 config.yml 中添加 PHP 配置文件:

imports:
    - { resource: parameters.yml }
    - { resource: pdo-constants.php }

pdo-constants.php是这样的:

<?php
$container->setParameter("pdo_options", [
    PDO::MYSQL_ATTR_SSL_CA => "%pdo_ca_file%",
]);

同时添加您需要的任何其他常量。

然后在 parameters.yml 中,您只需要常量的值:

parameters:
#...
    pdo_ca_file: /pathtocerts/certs/mysql-ca.pem

现在,我猜想与另一个使用 PDO 常量的数据库系统一起工作会很相似,但我只使用了这个 MySQL。

我发现了一个比其他方法简单得多的方法。 在 app/config/config.yml 中进行以下设置:

# Doctrine Configuration
doctrine:
    dbal:
        driver:   pdo_mysql
        host:     "%database_host%"
        port:     "%database_port%"
        dbname:   "%database_name%"
        user:     "%database_user%"
        password: "%database_password%"
        charset:  UTF8
        # Options for SSL connection
        options:
            MYSQL_ATTR_SSL_CA : %ca_cert%
            MYSQL_ATTR_SSL_KEY : %private_key%
            MYSQL_ATTR_SSL_CERT : %public_cert%

然后在您的 app/config/parameters.yml 文件中:

parameters:
    ...
    # SSL Info
    private_key: /etc/my.cnf.d/certs/client-key.pem
    public_cert: /etc/my.cnf.d/certs/client-cert.pem
    ca_cert: /etc/my.cnf.d/certs/ca-cert.pem

我在 Symfony3 上对此进行了测试,效果很好。上面的路径可能会有所不同,特别是证书可能会有所不同,具体取决于您的发行版和设置方式。

自从 Symfony 3.2 这变得容易多了:

doctrine:
    dbal:
        <other configs>
        options:
            !php/const:PDO::MYSQL_ATTR_SSL_CA: %ca_cert%
            !php/const:PDO::MYSQL_ATTR_SSL_KEY: %private_key%
            !php/const:PDO::MYSQL_ATTR_SSL_CERT: %public_cert%

An alternative approach would be installing composer/ca-bundle to help find the path to the CA root bundle and use a custom compiler pass 自动向 Doctrine 注册该路径:

<?php

namespace App\DepenedencyInjection\Compiler;

use Composer\CaBundle\CaBundle;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
 * Registers a CA root bundle with the PDO MySQL driver used by Doctrine DBAL
 *
 * This allows Doctrine to connect to MySQL instances that force SSL encryption, such as Azure's.
 */
final class RegisterCABundleWithPDOMysqlDriverPass implements CompilerPassInterface
{
    /**
     * @inheritDoc
     */
    public function process(ContainerBuilder $container)
    {
        $caPathOrFile = CaBundle::getSystemCaRootBundlePath();

        foreach ($container->getParameter('doctrine.connections') ?? [] as $connectionName) {
            $definition = $container->getDefinition($connectionName);
            $options = $definition->getArgument(0) ?? [];
            $options['driverOptions'][\PDO::MYSQL_ATTR_SSL_CA] = $caPathOrFile;
            $definition->setArgument(0, $options);
        }
    }
}

现在 2020 年,Symfony > 4.2,解决方案是:

# Doctrine Configuration
doctrine:
    dbal:
        driver:   pdo_mysql
        host:     "%database_host%"
        port:     "%database_port%"
        dbname:   "%database_name%"
        user:     "%database_user%"
        password: "%database_password%"
        charset:  UTF8
        # Options for SSL connection
        options:
            !php/const PDO::MYSQL_ATTR_SSL_CA : %ca_cert%
            !php/const PDO::MYSQL_ATTR_SSL_KEY : %private_key%
            !php/const PDO::MYSQL_ATTR_SSL_CERT : %public_cert%

来源:symfony.com/doc/4.2/service_container/parameters.html

使用 Symfony 5.1.8 测试

我在 docker 上使用 PHP 5.6.40,并且一直在调试为什么我的本地无法通过 ssl 连接。出于某种奇怪的原因,它需要 const 1009 而不是 1012。在这里删除这个答案以防将来有人偶然发现它。

doctrine:
    dbal:
        connections:
            default:
                options:
                    1001: true
                    1009: %ssl_cert_path%