Support for mariadb's ed25519 authentication
Add the ability to configure all mysql users to require authenticating to the server via mariadb's ed25519 auth plugin [1], rather than the default native authentication [2]. [1] https://mariadb.com/kb/en/authentication-plugin-ed25519/ [2] https://mariadb.com/kb/en/authentication-plugin-mysql_native_password/ Change-Id: I430ea8e1fa15fb263d1d4ef8c39615021d907f8a Partial-Bug: #1866093
This commit is contained in:
parent
7ee97845dd
commit
00a06edc5c
42
files/mysql_ed25519_password.py
Executable file
42
files/mysql_ed25519_password.py
Executable file
@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import hashlib
|
||||||
|
import base64
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from nacl.bindings.crypto_scalarmult import \
|
||||||
|
crypto_scalarmult_ed25519_base_noclamp
|
||||||
|
|
||||||
|
# https://github.com/MariaDB/server/blob/10.4/plugin/auth_ed25519/ref10/sign.c
|
||||||
|
# mariadb's use of ed25519:
|
||||||
|
# . password is the secret seed
|
||||||
|
# . ed25519's public key (computed from password) is what is stored in mariadb
|
||||||
|
# . the hash in mariadb is the base64 encoding of the pk minus the last '='
|
||||||
|
|
||||||
|
|
||||||
|
def _scalar_clamp(s32):
|
||||||
|
ba = bytearray(s32)
|
||||||
|
ba0 = bytes(bytearray([ba[0] & 248]))
|
||||||
|
ba31 = bytes(bytearray([(ba[31] & 127) | 64]))
|
||||||
|
return ba0 + bytes(s32[1:31]) + ba31
|
||||||
|
|
||||||
|
|
||||||
|
def mysql_ed25519_password(pwd):
|
||||||
|
# h = SHA512(password)
|
||||||
|
h = hashlib.sha512(pwd).digest()
|
||||||
|
# s = prune(first_half(h))
|
||||||
|
s = _scalar_clamp(h[:32])
|
||||||
|
# A = encoded point [s]B
|
||||||
|
A = crypto_scalarmult_ed25519_base_noclamp(s)
|
||||||
|
# encoded pk
|
||||||
|
encoded = base64.b64encode(A)[:-1]
|
||||||
|
return encoded
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) <= 1:
|
||||||
|
print("Usage: %s PASSWORD" % sys.argv[0], file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
pwd = sys.argv[1].encode()
|
||||||
|
res = mysql_ed25519_password(pwd)
|
||||||
|
print(res.decode(), end='')
|
19
lib/puppet/functions/mysql_ed25519_password.rb
Normal file
19
lib/puppet/functions/mysql_ed25519_password.rb
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Custom function to generate password hash for MariaDB's auth_ed25519
|
||||||
|
# Input is a regular mariadb user password
|
||||||
|
# Output is the hashed password as expected by auth_ed25519
|
||||||
|
Puppet::Functions.create_function(:'mysql_ed25519_password') do
|
||||||
|
dispatch :mysql_ed25519_password do
|
||||||
|
param 'String', :password
|
||||||
|
return_type 'String'
|
||||||
|
end
|
||||||
|
|
||||||
|
def mysql_ed25519_password(password)
|
||||||
|
# mysql's auth_ed25519 consists in generating a ed25519 public key
|
||||||
|
# out of the sha512(password). Unfortunately, there is no native
|
||||||
|
# ruby implementation of ed25519's unclamped scalar multiplication
|
||||||
|
# just yet, so rely on an binary to get the hash for now.
|
||||||
|
hashed = `/etc/puppet/modules/tripleo/files/mysql_ed25519_password.py #{password}`
|
||||||
|
raise Puppet::Error, 'generated hash is not 43 bytes long.' unless hashed.length == 43
|
||||||
|
return hashed
|
||||||
|
end
|
||||||
|
end
|
@ -87,6 +87,11 @@
|
|||||||
# (Optional) Maximum number of connections to MySQL.
|
# (Optional) Maximum number of connections to MySQL.
|
||||||
# Defaults to hiera('mysql_max_connections', undef)
|
# Defaults to hiera('mysql_max_connections', undef)
|
||||||
#
|
#
|
||||||
|
# [*mysql_auth_ed25519*]
|
||||||
|
# (Optional) Use MariaDB's ed25519 authentication plugin to authenticate
|
||||||
|
# a user when connecting to the server
|
||||||
|
# Defaults to hiera('mysql_auth_ed25519', false)
|
||||||
|
#
|
||||||
# [*remove_default_accounts*]
|
# [*remove_default_accounts*]
|
||||||
# (Optional) Whether or not remove default MySQL accounts.
|
# (Optional) Whether or not remove default MySQL accounts.
|
||||||
# Defaults to true
|
# Defaults to true
|
||||||
@ -112,6 +117,7 @@ class tripleo::profile::base::database::mysql (
|
|||||||
$manage_resources = true,
|
$manage_resources = true,
|
||||||
$mysql_server_options = {},
|
$mysql_server_options = {},
|
||||||
$mysql_max_connections = hiera('mysql_max_connections', undef),
|
$mysql_max_connections = hiera('mysql_max_connections', undef),
|
||||||
|
$mysql_auth_ed25519 = hiera('mysql_auth_ed25519', false),
|
||||||
$remove_default_accounts = true,
|
$remove_default_accounts = true,
|
||||||
$step = Integer(hiera('step')),
|
$step = Integer(hiera('step')),
|
||||||
) {
|
) {
|
||||||
@ -174,6 +180,7 @@ class tripleo::profile::base::database::mysql (
|
|||||||
'ssl-cert' => $tls_certfile,
|
'ssl-cert' => $tls_certfile,
|
||||||
'ssl-cipher' => $tls_cipher_list,
|
'ssl-cipher' => $tls_cipher_list,
|
||||||
'ssl-ca' => undef,
|
'ssl-ca' => undef,
|
||||||
|
'plugin_load_add' => 'auth_ed25519',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$mysql_server_options_real = deep_merge($mysql_server_default, $mysql_server_options)
|
$mysql_server_options_real = deep_merge($mysql_server_default, $mysql_server_options)
|
||||||
@ -214,78 +221,88 @@ class tripleo::profile::base::database::mysql (
|
|||||||
password_hash => mysql_password(hiera('mysql::server::root_password')),
|
password_hash => mysql_password(hiera('mysql::server::root_password')),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($mysql_auth_ed25519) {
|
||||||
|
['root@localhost', 'root@%'].each |$user| {
|
||||||
|
Mysql_user<| title == $user |> {
|
||||||
|
plugin => 'ed25519',
|
||||||
|
password_hash => mysql_ed25519_password(hiera('mysql::server::root_password'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# Note: use 'include_and_check_auth' below rather than 'include'
|
||||||
|
# to support ed25519 authentication
|
||||||
if hiera('aodh_api_enabled', false) {
|
if hiera('aodh_api_enabled', false) {
|
||||||
include aodh::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'aodh::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('ceilometer_collector_enabled', false) {
|
if hiera('ceilometer_collector_enabled', false) {
|
||||||
include ceilometer::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'ceilometer::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('cinder_api_enabled', false) {
|
if hiera('cinder_api_enabled', false) {
|
||||||
include cinder::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'cinder::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('barbican_api_enabled', false) {
|
if hiera('barbican_api_enabled', false) {
|
||||||
include barbican::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'barbican::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('designate_api_enabled', false) {
|
if hiera('designate_api_enabled', false) {
|
||||||
include designate::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'designate::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('glance_api_enabled', false) {
|
if hiera('glance_api_enabled', false) {
|
||||||
include glance::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'glance::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('gnocchi_api_enabled', false) {
|
if hiera('gnocchi_api_enabled', false) {
|
||||||
include gnocchi::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'gnocchi::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('heat_engine_enabled', false) {
|
if hiera('heat_engine_enabled', false) {
|
||||||
include heat::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'heat::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('ironic_api_enabled', false) {
|
if hiera('ironic_api_enabled', false) {
|
||||||
include ironic::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'ironic::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('ironic_inspector_enabled', false) {
|
if hiera('ironic_inspector_enabled', false) {
|
||||||
include ironic::inspector::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'ironic::inspector::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('keystone_enabled', false) {
|
if hiera('keystone_enabled', false) {
|
||||||
include keystone::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'keystone::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('manila_api_enabled', false) {
|
if hiera('manila_api_enabled', false) {
|
||||||
include manila::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'manila::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('mistral_api_enabled', false) {
|
if hiera('mistral_api_enabled', false) {
|
||||||
include mistral::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'mistral::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('neutron_api_enabled', false) {
|
if hiera('neutron_api_enabled', false) {
|
||||||
include neutron::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'neutron::db::mysql':}
|
||||||
|
}
|
||||||
|
if hiera('nova_conductor_enabled', false) {
|
||||||
|
tripleo::profile::base::database::mysql::include_and_check_auth{'nova::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('nova_conductor_enabled', false) {
|
|
||||||
include nova::db::mysql
|
|
||||||
}
|
|
||||||
if hiera('nova_api_enabled', false) {
|
if hiera('nova_api_enabled', false) {
|
||||||
include nova::db::mysql_api
|
tripleo::profile::base::database::mysql::include_and_check_auth{'nova::db::mysql_api':}
|
||||||
}
|
}
|
||||||
if hiera('placement_enabled', false) {
|
if hiera('placement_enabled', false) {
|
||||||
include placement::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'placement::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('octavia_api_enabled', false) {
|
if hiera('octavia_api_enabled', false) {
|
||||||
include octavia::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'octavia::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('sahara_api_enabled', false) {
|
if hiera('sahara_api_enabled', false) {
|
||||||
include sahara::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'sahara::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('trove_api_enabled', false) {
|
if hiera('trove_api_enabled', false) {
|
||||||
include trove::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'trove::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('panko_api_enabled', false) {
|
if hiera('panko_api_enabled', false) {
|
||||||
include panko::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'panko::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('ec2_api_enabled', false) {
|
if hiera('ec2_api_enabled', false) {
|
||||||
include ec2api::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'ec2api::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('zaqar_api_enabled', false) and hiera('zaqar::db::mysql::user', '') == 'zaqar' {
|
if hiera('zaqar_api_enabled', false) and hiera('zaqar::db::mysql::user', '') == 'zaqar' {
|
||||||
# NOTE: by default zaqar uses sqlalchemy
|
# NOTE: by default zaqar uses sqlalchemy
|
||||||
include zaqar::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'zaqar::db::mysql':}
|
||||||
}
|
}
|
||||||
if hiera('veritas_hyperscale_controller_enabled', false) {
|
if hiera('veritas_hyperscale_controller_enabled', false) {
|
||||||
include veritas_hyperscale::db::mysql
|
tripleo::profile::base::database::mysql::include_and_check_auth{'veritas_hyperscale::db::mysql':}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
# Copyright 2016 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
# == Class: include_and_check_auth
|
||||||
|
#
|
||||||
|
# Include an OpenStack MySQL profile and configures it for alternative
|
||||||
|
# client authentication like e.g. ed25519
|
||||||
|
#
|
||||||
|
# === Parameters
|
||||||
|
#
|
||||||
|
# [*module*]
|
||||||
|
# (Optional) The puppet module to include
|
||||||
|
# Defaults to $title
|
||||||
|
#
|
||||||
|
# [*mysql_auth_ed25519*]
|
||||||
|
# (Optional) Use MariaDB's ed25519 authentication plugin to authenticate
|
||||||
|
# a user when connecting to the server
|
||||||
|
# Defaults to hiera('mysql_auth_ed25519', false)
|
||||||
|
#
|
||||||
|
define tripleo::profile::base::database::mysql::include_and_check_auth(
|
||||||
|
$module = $title,
|
||||||
|
$mysql_auth_ed25519 = hiera('mysql_auth_ed25519', false),
|
||||||
|
) {
|
||||||
|
include $module
|
||||||
|
if ($mysql_auth_ed25519) {
|
||||||
|
# currently all openstack puppet modules create MySQL users
|
||||||
|
# by hashing their password for the default auth method.
|
||||||
|
# If ed25519 auth is enabled, we must hash the password
|
||||||
|
# differently; so do it with a collector until all
|
||||||
|
# openstack modules support ed25519 auth natively.
|
||||||
|
$stripped_module_name = regsubst($module,'^::','')
|
||||||
|
$password_key = "${stripped_module_name}::password"
|
||||||
|
Openstacklib::Db::Mysql<| tag == $stripped_module_name |> {
|
||||||
|
plugin => 'ed25519',
|
||||||
|
password_hash => mysql_ed25519_password(hiera($password_key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -105,6 +105,11 @@
|
|||||||
# Should be an hash.
|
# Should be an hash.
|
||||||
# Defaults to hiera('tripleo::profile::base::database::mysql::mysql_server_options', {}
|
# Defaults to hiera('tripleo::profile::base::database::mysql::mysql_server_options', {}
|
||||||
#
|
#
|
||||||
|
# [*mysql_auth_ed25519*]
|
||||||
|
# (Optional) Use MariaDB's ed25519 authentication plugin to authenticate
|
||||||
|
# a user when connecting to the server
|
||||||
|
# Defaults to hiera('mysql_auth_ed25519', false)
|
||||||
|
#
|
||||||
# [*pcs_tries*]
|
# [*pcs_tries*]
|
||||||
# (Optional) The number of times pcs commands should be retried.
|
# (Optional) The number of times pcs commands should be retried.
|
||||||
# Defaults to hiera('pcs_tries', 20)
|
# Defaults to hiera('pcs_tries', 20)
|
||||||
@ -149,6 +154,7 @@ class tripleo::profile::pacemaker::database::mysql_bundle (
|
|||||||
$sst_tls_options = undef,
|
$sst_tls_options = undef,
|
||||||
$ipv6 = str2bool(hiera('mysql_ipv6', false)),
|
$ipv6 = str2bool(hiera('mysql_ipv6', false)),
|
||||||
$mysql_server_options = hiera('tripleo::profile::base::database::mysql::mysql_server_options', {}),
|
$mysql_server_options = hiera('tripleo::profile::base::database::mysql::mysql_server_options', {}),
|
||||||
|
$mysql_auth_ed25519 = hiera('mysql_auth_ed25519', false),
|
||||||
$container_backend = 'docker',
|
$container_backend = 'docker',
|
||||||
$log_driver = undef,
|
$log_driver = undef,
|
||||||
$tls_priorities = hiera('tripleo::pacemaker::tls_priorities', undef),
|
$tls_priorities = hiera('tripleo::pacemaker::tls_priorities', undef),
|
||||||
@ -498,6 +504,23 @@ MYSQL_HOST=localhost\n",
|
|||||||
password_hash => mysql_password($mysql_root_password),
|
password_hash => mysql_password($mysql_root_password),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# declare the clustercheck user resource to configure
|
||||||
|
# ed25519 authentication on stack creation or update.
|
||||||
|
if ($mysql_auth_ed25519) {
|
||||||
|
$clustercheck_resource_config = {
|
||||||
|
plugin => 'ed25519',
|
||||||
|
password_hash => mysql_ed25519_password($clustercheck_password),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$clustercheck_resource_config = {
|
||||||
|
password_hash => mysql_password($clustercheck_password),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mysql_user { 'clustercheck@localhost':
|
||||||
|
ensure => present,
|
||||||
|
* => $clustercheck_resource_config,
|
||||||
|
}
|
||||||
|
|
||||||
# We create databases and users for services at step 2 as well. This ensures
|
# We create databases and users for services at step 2 as well. This ensures
|
||||||
# Galera is up and ready before those get created
|
# Galera is up and ready before those get created
|
||||||
File['/root/.my.cnf'] -> Mysql_database<||>
|
File['/root/.my.cnf'] -> Mysql_database<||>
|
||||||
|
Loading…
Reference in New Issue
Block a user