diff options
-rw-r--r-- | config/global.ini.php | 27 | ||||
-rw-r--r-- | core/Db/Adapter/Mysqli.php | 22 | ||||
-rw-r--r-- | core/Db/Adapter/Pdo/Mysql.php | 22 | ||||
-rw-r--r-- | libs/Zend/Db/Adapter/Mysqli.php | 40 | ||||
-rw-r--r-- | plugins/Diagnostics/Diagnostic/DbOverSSLCheck.php | 62 | ||||
-rw-r--r-- | plugins/Diagnostics/config/config.php | 1 | ||||
-rw-r--r-- | plugins/Installation/lang/en.json | 6 | ||||
-rw-r--r-- | tests/PHPUnit/Integration/DbSSLTest.php | 30 |
8 files changed, 207 insertions, 3 deletions
diff --git a/config/global.ini.php b/config/global.ini.php index ea707a097e..e51cd09561 100644 --- a/config/global.ini.php +++ b/config/global.ini.php @@ -21,6 +21,25 @@ port = 3306 adapter = PDO\MYSQL type = InnoDB schema = Mysql + +; Database SSL Options START +; Turn on or off SSL connection to database, possible values for enable_ssl: 1 or 0 +enable_ssl = 0 +; Direct path to server CA file, CA bundle supported (required for ssl connection) +ssl_ca = +; Direct path to client cert file (optional) +ssl_cert = +; Direct path to client key file (optional) +ssl_key = +; Direct path to CA cert files directory (optional) +ssl_ca_path = +; List of one or more ciphers for SSL encryption, in OpenSSL format (optional) +ssl_cipher = +; Whether to skip verification of self signed certificates (optional, only supported +; w/ specific PHP versions, and is mostly for testing purposes) +ssl_no_verify = +; Database SSL Options END + ; if charset is set to utf8, Matomo will ensure that it is storing its data using UTF8 charset. ; it will add a sql query SET at each page view. ; Matomo should work correctly without this setting but we recommend to have a charset set. @@ -37,6 +56,13 @@ adapter = PDO\MYSQL type = InnoDB schema = Mysql charset = utf8 +enable_ssl = 0 +ssl_ca = +ssl_cert = +ssl_key = +ssl_ca_path = +ssl_cipher = +ssl_no_verify = 1 [tests] ; needed in order to run tests. @@ -904,4 +930,3 @@ SDK_batch_size = 10 SDK_interval_value = 30 ; NOTE: do not directly edit this file! See notice at the top - diff --git a/core/Db/Adapter/Mysqli.php b/core/Db/Adapter/Mysqli.php index 396ab60343..4c8c7fed81 100644 --- a/core/Db/Adapter/Mysqli.php +++ b/core/Db/Adapter/Mysqli.php @@ -29,6 +29,28 @@ class Mysqli extends Zend_Db_Adapter_Mysqli implements AdapterInterface { // Enable LOAD DATA INFILE $config['driver_options'][MYSQLI_OPT_LOCAL_INFILE] = true; + + if ($config['enable_ssl']) { + if (!empty($config['ssl_key'])) { + $config['driver_options']['ssl_key'] = $config['ssl_key']; + } + if (!empty($config['ssl_cert'])) { + $config['driver_options']['ssl_cert'] = $config['ssl_cert']; + } + if (!empty($config['ssl_ca'])) { + $config['driver_options']['ssl_ca'] = $config['ssl_ca']; + } + if (!empty($config['ssl_ca_path'])) { + $config['driver_options']['ssl_ca_path'] = $config['ssl_ca_path']; + } + if (!empty($config['ssl_cipher'])) { + $config['driver_options']['ssl_cipher'] = $config['ssl_cipher']; + } + if (!empty($config['ssl_no_verify'])) { + $config['driver_options']['ssl_no_verify'] = $config['ssl_no_verify']; + } + } + parent::__construct($config); } diff --git a/core/Db/Adapter/Pdo/Mysql.php b/core/Db/Adapter/Pdo/Mysql.php index 360e6a57e8..b9b250081f 100644 --- a/core/Db/Adapter/Pdo/Mysql.php +++ b/core/Db/Adapter/Pdo/Mysql.php @@ -35,6 +35,28 @@ class Mysql extends Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface if (defined('PDO::MYSQL_ATTR_LOCAL_INFILE')) { $config['driver_options'][PDO::MYSQL_ATTR_LOCAL_INFILE] = true; } + if ($config['enable_ssl']) { + if (!empty($config['ssl_key'])) { + $config['driver_options'][PDO::MYSQL_ATTR_SSL_KEY] = $config['ssl_key']; + } + if (!empty($config['ssl_cert'])) { + $config['driver_options'][PDO::MYSQL_ATTR_SSL_CERT] = $config['ssl_cert']; + } + if (!empty($config['ssl_ca'])) { + $config['driver_options'][PDO::MYSQL_ATTR_SSL_CA] = $config['ssl_ca']; + } + if (!empty($config['ssl_ca_path'])) { + $config['driver_options'][PDO::MYSQL_ATTR_SSL_CAPATH] = $config['ssl_ca_path']; + } + if (!empty($config['ssl_cipher'])) { + $config['driver_options'][PDO::MYSQL_ATTR_SSL_CIPHER] = $config['ssl_cipher']; + } + if (!empty($config['ssl_no_verify']) + && defined('PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT') + ) { + $config['driver_options'][PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = false; + } + } parent::__construct($config); } diff --git a/libs/Zend/Db/Adapter/Mysqli.php b/libs/Zend/Db/Adapter/Mysqli.php index 84dd9cab0b..e94ec26b37 100644 --- a/libs/Zend/Db/Adapter/Mysqli.php +++ b/libs/Zend/Db/Adapter/Mysqli.php @@ -299,9 +299,21 @@ class Zend_Db_Adapter_Mysqli extends Zend_Db_Adapter_Abstract $this->_connection = mysqli_init(); + $enable_ssl = false; + $ssl_options = array ( + 'ssl_ca' => null, + 'ssl_ca_path' => null, + 'ssl_cert' => null, + 'ssl_cipher' => null, + 'ssl_key' => null, + ); + if(!empty($this->_config['driver_options'])) { foreach($this->_config['driver_options'] as $option=>$value) { - if(is_string($option)) { + if(array_key_exists($option, $ssl_options)) { + $ssl_options[$option] = $value; + $enable_ssl = true; + } elseif(is_string($option)) { // Suppress warnings here // Ignore it if it's not a valid constant $option = @constant(strtoupper($option)); @@ -312,6 +324,28 @@ class Zend_Db_Adapter_Mysqli extends Zend_Db_Adapter_Abstract } } + + if ($enable_ssl) { + mysqli_ssl_set( + $this->_connection, + $ssl_options['ssl_key'], + $ssl_options['ssl_cert'], + $ssl_options['ssl_ca'], + $ssl_options['ssl_ca_path'], + $ssl_options['ssl_cipher'] + ); + } + + $flags = null; + if ($enable_ssl) { + $flags = MYSQLI_CLIENT_SSL; + if (!empty($this->_config['driver_options']['ssl_no_verify']) + && defined('MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT') + ) { + $flags = MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT; + } + } + // Suppress connection warnings here. // Throw an exception instead. $_isConnected = @mysqli_real_connect( @@ -320,7 +354,9 @@ class Zend_Db_Adapter_Mysqli extends Zend_Db_Adapter_Abstract $this->_config['username'], $this->_config['password'], $this->_config['dbname'], - $port + $port, + $socket = null, + $enable_ssl ? $flags : null ); if ($_isConnected === false || mysqli_connect_errno()) { diff --git a/plugins/Diagnostics/Diagnostic/DbOverSSLCheck.php b/plugins/Diagnostics/Diagnostic/DbOverSSLCheck.php new file mode 100644 index 0000000000..07b8942da5 --- /dev/null +++ b/plugins/Diagnostics/Diagnostic/DbOverSSLCheck.php @@ -0,0 +1,62 @@ +<?php + +namespace Piwik\Plugins\Diagnostics\Diagnostic; + +use Piwik\Common; +use Piwik\Config; +use Piwik\Db; +use Piwik\Translation\Translator; + +/** + * Check if Piwik is connected with database through ssl. + */ +class DbOverSSLCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $enable_ssl = Config::getInstance()->database['enable_ssl']; + if (!$enable_ssl) { + return array(); + } + + $label = $this->translator->translate('Installation_SystemCheckDatabaseSSL'); + + $cipher = Db::fetchRow("show status like 'Ssl_cipher'"); + if(!empty($cipher['Value'])) { + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_OK, $this->translator->translate('Installation_SystemCheckDatabaseSSLCipher') . ': ' . $cipher['Value'])); + } + + //no cipher, not working + $comment = sprintf($this->translator->translate('Installation_SystemCheckDatabaseSSLNotWorking'), "enable_ssl") . "<br />"; + + // test ssl support + $ssl_support = Db::fetchRow("SHOW VARIABLES LIKE 'have_ssl'"); + if(!empty($ssl_support['Value'])) { + switch ($ssl_support['Value']) { + case 'YES': + $comment .= $this->translator->translate('Installation_SystemCheckDatabaseSSLOn'); + break; + case 'DISABLED': + $comment .= $this->translator->translate('Installation_SystemCheckDatabaseSSLDisabled'); + break; + case 'NO': + $comment .= $this->translator->translate('Installation_SystemCheckDatabaseSSLNo'); + break; + } + } + + $comment .= '<br />' . '<a target="_blank" href="?module=Proxy&action=redirect&url=http://piwik.org/faq/"> FAQ on piwik.org</a>'; + + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_WARNING, $comment)); + } +} diff --git a/plugins/Diagnostics/config/config.php b/plugins/Diagnostics/config/config.php index 566b476535..4e300166e7 100644 --- a/plugins/Diagnostics/config/config.php +++ b/plugins/Diagnostics/config/config.php @@ -24,6 +24,7 @@ return array( DI\get('Piwik\Plugins\Diagnostics\Diagnostic\NfsDiskCheck'), DI\get('Piwik\Plugins\Diagnostics\Diagnostic\CronArchivingCheck'), DI\get('Piwik\Plugins\Diagnostics\Diagnostic\LoadDataInfileCheck'), + Di\get('Piwik\Plugins\Diagnostics\Diagnostic\DbOverSSLCheck'), ), // Allows other plugins to disable diagnostics that were previously registered 'diagnostics.disabled' => array(), diff --git a/plugins/Installation/lang/en.json b/plugins/Installation/lang/en.json index 406a499200..d6812de344 100644 --- a/plugins/Installation/lang/en.json +++ b/plugins/Installation/lang/en.json @@ -72,6 +72,12 @@ "SystemCheckCreateFunctionHelp": "Matomo uses anonymous functions for callbacks.", "SystemCheckDatabaseExtensions": "MySQL extensions", "SystemCheckDatabaseHelp": "Matomo requires either the mysqli extension or both the PDO and pdo_mysql extensions.", + "SystemCheckDatabaseSSL": "Database SSL Connection", + "SystemCheckDatabaseSSLCipher": "SSL cipher being used", + "SystemCheckDatabaseSSLDisabled": "SSL support in your database server is disabled", + "SystemCheckDatabaseSSLNo": "Database server is not compiled with SSL support", + "SystemCheckDatabaseSSLNotWorking": "%s is set to '1' but SSL connection is not working", + "SystemCheckDatabaseSSLOn": "Your database supports SSL connections but SSL connection is not used. Check your database SSL settings in your Matomo config file", "SystemCheckDebugBacktraceHelp": "View::factory won't be able to create views for the calling module.", "SystemCheckError": "An error occured - must be fixed before you proceed", "SystemCheckEvalHelp": "Required by HTML QuickForm and Smarty templating system.", diff --git a/tests/PHPUnit/Integration/DbSSLTest.php b/tests/PHPUnit/Integration/DbSSLTest.php new file mode 100644 index 0000000000..30b554e884 --- /dev/null +++ b/tests/PHPUnit/Integration/DbSSLTest.php @@ -0,0 +1,30 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Tests\Integration; + +use Piwik\Db; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; +use Piwik\Config; + +/** + * @group Core + */ +class DbSSLTest extends IntegrationTestCase +{ + public function testMysqlSSLConnection() { + $dbConfig = Config::getInstance()->database; + if(isset($dbConfig['enable_ssl']) && $dbConfig['enable_ssl'] == true) { + Db::createDatabaseObject($dbConfig); + $cipher = Db::fetchRow("show status like 'Ssl_cipher'"); + $this->assertNotEmpty($cipher['Value']); + } else { + $this->markTestSkipped(true); + } + } +} |