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.
|
||||
# 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*]
|
||||
# (Optional) Whether or not remove default MySQL accounts.
|
||||
# Defaults to true
|
||||
@ -112,6 +117,7 @@ class tripleo::profile::base::database::mysql (
|
||||
$manage_resources = true,
|
||||
$mysql_server_options = {},
|
||||
$mysql_max_connections = hiera('mysql_max_connections', undef),
|
||||
$mysql_auth_ed25519 = hiera('mysql_auth_ed25519', false),
|
||||
$remove_default_accounts = true,
|
||||
$step = Integer(hiera('step')),
|
||||
) {
|
||||
@ -174,6 +180,7 @@ class tripleo::profile::base::database::mysql (
|
||||
'ssl-cert' => $tls_certfile,
|
||||
'ssl-cipher' => $tls_cipher_list,
|
||||
'ssl-ca' => undef,
|
||||
'plugin_load_add' => 'auth_ed25519',
|
||||
}
|
||||
}
|
||||
$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')),
|
||||
}
|
||||
}
|
||||
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) {
|
||||
include aodh::db::mysql
|
||||
tripleo::profile::base::database::mysql::include_and_check_auth{'aodh::db::mysql':}
|
||||
}
|
||||
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) {
|
||||
include cinder::db::mysql
|
||||
tripleo::profile::base::database::mysql::include_and_check_auth{'cinder::db::mysql':}
|
||||
}
|
||||
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) {
|
||||
include designate::db::mysql
|
||||
tripleo::profile::base::database::mysql::include_and_check_auth{'designate::db::mysql':}
|
||||
}
|
||||
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) {
|
||||
include gnocchi::db::mysql
|
||||
tripleo::profile::base::database::mysql::include_and_check_auth{'gnocchi::db::mysql':}
|
||||
}
|
||||
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) {
|
||||
include ironic::db::mysql
|
||||
tripleo::profile::base::database::mysql::include_and_check_auth{'ironic::db::mysql':}
|
||||
}
|
||||
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) {
|
||||
include keystone::db::mysql
|
||||
tripleo::profile::base::database::mysql::include_and_check_auth{'keystone::db::mysql':}
|
||||
}
|
||||
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) {
|
||||
include mistral::db::mysql
|
||||
tripleo::profile::base::database::mysql::include_and_check_auth{'mistral::db::mysql':}
|
||||
}
|
||||
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) {
|
||||
include nova::db::mysql_api
|
||||
tripleo::profile::base::database::mysql::include_and_check_auth{'nova::db::mysql_api':}
|
||||
}
|
||||
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) {
|
||||
include octavia::db::mysql
|
||||
tripleo::profile::base::database::mysql::include_and_check_auth{'octavia::db::mysql':}
|
||||
}
|
||||
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) {
|
||||
include trove::db::mysql
|
||||
tripleo::profile::base::database::mysql::include_and_check_auth{'trove::db::mysql':}
|
||||
}
|
||||
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) {
|
||||
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' {
|
||||
# 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) {
|
||||
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.
|
||||
# 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*]
|
||||
# (Optional) The number of times pcs commands should be retried.
|
||||
# Defaults to hiera('pcs_tries', 20)
|
||||
@ -149,6 +154,7 @@ class tripleo::profile::pacemaker::database::mysql_bundle (
|
||||
$sst_tls_options = undef,
|
||||
$ipv6 = str2bool(hiera('mysql_ipv6', false)),
|
||||
$mysql_server_options = hiera('tripleo::profile::base::database::mysql::mysql_server_options', {}),
|
||||
$mysql_auth_ed25519 = hiera('mysql_auth_ed25519', false),
|
||||
$container_backend = 'docker',
|
||||
$log_driver = undef,
|
||||
$tls_priorities = hiera('tripleo::pacemaker::tls_priorities', undef),
|
||||
@ -498,6 +504,23 @@ MYSQL_HOST=localhost\n",
|
||||
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
|
||||
# Galera is up and ready before those get created
|
||||
File['/root/.my.cnf'] -> Mysql_database<||>
|
||||
|
Loading…
Reference in New Issue
Block a user