exwiw 1.1.7 → 1.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +2 -0
- data/lib/exwiw/adapter/mysql_adapter.rb +19 -0
- data/lib/exwiw/adapter/mysql_client.rb +33 -2
- data/lib/exwiw/cli.rb +33 -1
- data/lib/exwiw/version.rb +1 -1
- data/lib/exwiw.rb +3 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ef76bef2bd961b6bf50afcdd7f28e87e5c78ab587385519bbbfc3d66e3438c6d
|
|
4
|
+
data.tar.gz: 99c7d8cb5250fd3cfb7951a586d575dd76b3dbfa20f6c9aa5125643ce7bd12cb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 271ef2c7496ab0d6d118a0e27195d0b411f8642914cc0a0eca68abc3d47df02b8876261c40933790f99a637f6e2eb1f63e8fc2ce2f618315151c3622370c7a1a
|
|
7
|
+
data.tar.gz: 356b3c7e579c980a38ab649e2c986bbb33310f4906593ff504f14ed2839f3d3249e60337fe355d8b32ddf2e72e6439b07bfa5da5845d194cceaa2a0d933a21b8
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [1.1.8] - 2026-10-29
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`--ssl-mode` and `--ssl-ca` connect to MySQL over TLS.** exwiw passed no TLS options to the driver or to `mysqldump`, so with a client library that does not use TLS by default (MariaDB Connector/C, for one) every command failed against a server with `require_secure_transport=ON` — `Connections using insecure transport are prohibited`. The two options take the MySQL client's values (`disabled`, `preferred`, `required`, `verify_ca`, `verify_identity`) and a CA file, and apply to the `mysql2` / `trilogy` connection and to `mysqldump`; MariaDB's `mysqldump`, which has no `--ssl-mode`, gets the equivalent `--ssl` / `--ssl-verify-server-cert` flags, and where MariaDB's client library or `mysqldump` lacks a mode, `verify_ca` also checks the host name and `preferred` leaves the client default. Without the options nothing changes. mysql adapter only.
|
|
10
|
+
|
|
5
11
|
## [1.1.7] - 2026-10-01
|
|
6
12
|
|
|
7
13
|
### Added
|
data/README.md
CHANGED
|
@@ -21,6 +21,8 @@ You usually want `require: false` on the Gemfile entry. Without bundler, run `ge
|
|
|
21
21
|
|
|
22
22
|
For MySQL, exwiw uses the `mysql2` gem if it is available and `trilogy` otherwise; pass `--adapter=mysql` either way. Set `EXWIW_MYSQL_DRIVER=trilogy` (or `mysql2`) to choose one. `trilogy` helps when `mysql2` is linked against a client library that cannot load the server's auth plugin, such as a MySQL 9.x client connecting to a server that uses `mysql_native_password`.
|
|
23
23
|
|
|
24
|
+
To connect to MySQL over TLS, pass `--ssl-mode` (`disabled`, `preferred`, `required`, `verify_ca` or `verify_identity`) and `--ssl-ca=<CA file>`. The server certificate is verified only with `verify_ca` or `verify_identity`; `--ssl-ca` is rejected without `--ssl-mode`. Both apply to the driver connection and to `mysqldump`. Without them, exwiw uses the client library's default, which may connect without TLS and then fail against a server with `require_secure_transport=ON`. MariaDB's client library and `mysqldump` do not have every mode, so with them `verify_ca` also checks the host name and `preferred` leaves the client default (`mysqldump` gets `--ssl` / `--ssl-verify-server-cert` in place of `--ssl-mode`).
|
|
25
|
+
|
|
24
26
|
## Usage
|
|
25
27
|
|
|
26
28
|
exwiw has three subcommands:
|
|
@@ -103,6 +103,7 @@ module Exwiw
|
|
|
103
103
|
# MariaDB's mysqldump does not recognise the MySQL-specific
|
|
104
104
|
# --set-gtid-purged flag and exits with code 7.
|
|
105
105
|
gtid_flags = mariadb_mysqldump?(mysqldump_bin) ? [] : ['--set-gtid-purged=OFF']
|
|
106
|
+
ssl_flags = mysqldump_ssl_flags(mariadb: mariadb_mysqldump?(mysqldump_bin))
|
|
106
107
|
|
|
107
108
|
cmd = [
|
|
108
109
|
mysqldump_bin,
|
|
@@ -115,6 +116,7 @@ module Exwiw
|
|
|
115
116
|
'--skip-set-charset',
|
|
116
117
|
'--no-tablespaces', # skip tablespace query that requires PROCESS privilege (unavailable on managed MySQL like RDS)
|
|
117
118
|
*gtid_flags,
|
|
119
|
+
*ssl_flags,
|
|
118
120
|
'--compact',
|
|
119
121
|
@connection_config.database_name,
|
|
120
122
|
]
|
|
@@ -437,6 +439,23 @@ module Exwiw
|
|
|
437
439
|
end
|
|
438
440
|
end
|
|
439
441
|
|
|
442
|
+
private def mysqldump_ssl_flags(mariadb:)
|
|
443
|
+
mode = @connection_config.ssl_mode
|
|
444
|
+
ca_flags = @connection_config.ssl_ca ? ["--ssl-ca=#{@connection_config.ssl_ca}"] : []
|
|
445
|
+
return ca_flags + (mode ? ["--ssl-mode=#{mode.upcase}"] : []) unless mariadb
|
|
446
|
+
|
|
447
|
+
# MariaDB's client has no --ssl-mode, and from 11.4 verifies the server
|
|
448
|
+
# certificate by default, so `required` must opt out to stay unverified.
|
|
449
|
+
mode_flags =
|
|
450
|
+
case mode
|
|
451
|
+
when nil, "preferred" then []
|
|
452
|
+
when "disabled" then ["--skip-ssl"]
|
|
453
|
+
when "required" then ["--ssl", "--skip-ssl-verify-server-cert"]
|
|
454
|
+
else ["--ssl", "--ssl-verify-server-cert"]
|
|
455
|
+
end
|
|
456
|
+
ca_flags + mode_flags
|
|
457
|
+
end
|
|
458
|
+
|
|
440
459
|
private def connection
|
|
441
460
|
@connection ||= MysqlClient.new(@connection_config)
|
|
442
461
|
end
|
|
@@ -20,6 +20,16 @@ module Exwiw
|
|
|
20
20
|
|
|
21
21
|
DRIVERS = [:mysql2, :trilogy].freeze
|
|
22
22
|
|
|
23
|
+
SSL_MODES = %w[disabled preferred required verify_ca verify_identity].freeze
|
|
24
|
+
|
|
25
|
+
TRILOGY_SSL_MODE_CONSTANTS = {
|
|
26
|
+
"disabled" => :SSL_DISABLED,
|
|
27
|
+
"preferred" => :SSL_PREFERRED_NOVERIFY,
|
|
28
|
+
"required" => :SSL_REQUIRED_NOVERIFY,
|
|
29
|
+
"verify_ca" => :SSL_VERIFY_CA,
|
|
30
|
+
"verify_identity" => :SSL_VERIFY_IDENTITY,
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
23
33
|
# Pick the available driver, preferring mysql2 (exwiw's historical default).
|
|
24
34
|
# require returns false when already loaded, so this is safe to call repeatedly.
|
|
25
35
|
#
|
|
@@ -188,10 +198,31 @@ module Exwiw
|
|
|
188
198
|
}.compact
|
|
189
199
|
|
|
190
200
|
case @driver
|
|
191
|
-
when :mysql2 then Mysql2::Client.new(options)
|
|
192
|
-
when :trilogy then Trilogy.new(options)
|
|
201
|
+
when :mysql2 then Mysql2::Client.new(options.merge(mysql2_ssl_options))
|
|
202
|
+
when :trilogy then Trilogy.new(options.merge(trilogy_ssl_options))
|
|
193
203
|
end
|
|
194
204
|
end
|
|
205
|
+
|
|
206
|
+
private def mysql2_ssl_options
|
|
207
|
+
{ ssl_mode: mysql2_ssl_mode, sslca: @connection_config.ssl_ca }.compact
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# mysql2 linked against MariaDB Connector/C defines unsupported modes as 0 and
|
|
211
|
+
# ignores them with a warning, so fall back to the nearest mode it supports.
|
|
212
|
+
private def mysql2_ssl_mode
|
|
213
|
+
mode = @connection_config.ssl_mode
|
|
214
|
+
return mode if mode.nil? || !Mysql2::Client.const_get("SSL_MODE_#{mode.upcase}").zero?
|
|
215
|
+
|
|
216
|
+
"verify_identity" if mode == "verify_ca"
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
private def trilogy_ssl_options
|
|
220
|
+
mode = @connection_config.ssl_mode
|
|
221
|
+
{
|
|
222
|
+
ssl_mode: mode && Trilogy.const_get(TRILOGY_SSL_MODE_CONSTANTS.fetch(mode)),
|
|
223
|
+
ssl_ca: @connection_config.ssl_ca,
|
|
224
|
+
}.compact
|
|
225
|
+
end
|
|
195
226
|
end
|
|
196
227
|
end
|
|
197
228
|
end
|
data/lib/exwiw/cli.rb
CHANGED
|
@@ -65,7 +65,7 @@ module Exwiw
|
|
|
65
65
|
# Database connection settings are environment-specific (and sometimes
|
|
66
66
|
# secret-adjacent), so they must be passed via CLI/env, never the committed
|
|
67
67
|
# config file. `adapter` is the one connection-ish key allowed in config.
|
|
68
|
-
REJECTED_CONNECTION_KEYS = %w[host port user database uri password].freeze
|
|
68
|
+
REJECTED_CONNECTION_KEYS = %w[host port user database uri password ssl_mode ssl_ca].freeze
|
|
69
69
|
|
|
70
70
|
# Keys that only make sense for `export`. They are skipped when merging config
|
|
71
71
|
# for `explain` so a shared config file does not trip validate_explain_only!,
|
|
@@ -103,6 +103,8 @@ module Exwiw
|
|
|
103
103
|
@database_user = nil
|
|
104
104
|
@database_password = ENV["DATABASE_PASSWORD"]
|
|
105
105
|
@connection_uri = nil
|
|
106
|
+
@ssl_mode = nil
|
|
107
|
+
@ssl_ca = nil
|
|
106
108
|
@output_dir = nil
|
|
107
109
|
@schema_dir = nil
|
|
108
110
|
@from_db = false
|
|
@@ -148,6 +150,8 @@ module Exwiw
|
|
|
148
150
|
database_name: @database_name,
|
|
149
151
|
uri: @connection_uri,
|
|
150
152
|
mongodb_query_timeout_ms: @mongodb_query_timeout_ms,
|
|
153
|
+
ssl_mode: @ssl_mode,
|
|
154
|
+
ssl_ca: @ssl_ca,
|
|
151
155
|
)
|
|
152
156
|
|
|
153
157
|
dump_target = DumpTarget.new(
|
|
@@ -324,6 +328,7 @@ module Exwiw
|
|
|
324
328
|
resolve_scope_column!
|
|
325
329
|
resolve_ids_field!
|
|
326
330
|
resolve_uri_option!
|
|
331
|
+
resolve_ssl_options!
|
|
327
332
|
resolve_parallel_workers!
|
|
328
333
|
resolve_mongodb_query_timeout_ms!
|
|
329
334
|
|
|
@@ -654,6 +659,31 @@ module Exwiw
|
|
|
654
659
|
end
|
|
655
660
|
end
|
|
656
661
|
|
|
662
|
+
private def resolve_ssl_options!
|
|
663
|
+
return if @ssl_mode.nil? && @ssl_ca.nil?
|
|
664
|
+
|
|
665
|
+
if @database_adapter != "mysql"
|
|
666
|
+
$stderr.puts "--ssl-mode / --ssl-ca are only supported by the mysql adapter"
|
|
667
|
+
exit 1
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
if @ssl_ca&.empty?
|
|
671
|
+
$stderr.puts "--ssl-ca must be a path to a CA certificate file"
|
|
672
|
+
exit 1
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
if @ssl_mode.nil?
|
|
676
|
+
$stderr.puts "--ssl-ca requires --ssl-mode (verify_ca or verify_identity to verify the server)"
|
|
677
|
+
exit 1
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
@ssl_mode = @ssl_mode.downcase
|
|
681
|
+
unless Adapter::MysqlClient::SSL_MODES.include?(@ssl_mode)
|
|
682
|
+
$stderr.puts "Invalid --ssl-mode. Available options are: #{Adapter::MysqlClient::SSL_MODES.join(', ')}"
|
|
683
|
+
exit 1
|
|
684
|
+
end
|
|
685
|
+
end
|
|
686
|
+
|
|
657
687
|
# `--parallel-workers` opts into the MongoDB fork-parallel dump schedule
|
|
658
688
|
# (docs/mongodb-dump-parallelism-2x-notes.md). It is mongodb-only (the SQL
|
|
659
689
|
# adapters shell out to their own dumpers) and must be a positive integer;
|
|
@@ -919,6 +949,8 @@ module Exwiw
|
|
|
919
949
|
end
|
|
920
950
|
opts.on("-a", "--adapter=ADAPTER", "Database adapter: mysql, sqlite, postgresql, mongodb (aliases: mysql2, sqlite3)") { |v| @database_adapter = v }
|
|
921
951
|
opts.on("--uri=URI", "Full MongoDB connection URI (mongodb:// or mongodb+srv://). mongodb adapter only; takes precedence over --host/--port/--user. TLS, replicaSet, authSource and credentials are read from the URI.") { |v| @connection_uri = v }
|
|
952
|
+
opts.on("--ssl-mode=MODE", "TLS mode for the connection and mysqldump: #{Adapter::MysqlClient::SSL_MODES.join(', ')}. mysql adapter only; defaults to the client library's behavior.") { |v| @ssl_mode = v }
|
|
953
|
+
opts.on("--ssl-ca=PATH", "CA certificate file used to verify the server. mysql adapter only.") { |v| @ssl_ca = v }
|
|
922
954
|
opts.on("--database=DATABASE", "Target database name") { |v| @database_name = v }
|
|
923
955
|
opts.on("--target-table=[TABLE]", "Target table for extraction. If omitted with --ids, run in scope-column mode (SQL adapters; the schema must declare `scope_column:`). If omitted without --ids, dump all tables.") { |v| @target_table_name = v }
|
|
924
956
|
opts.on("--target-collection=[COLLECTION]", "Alias of --target-table for the mongodb adapter.") { |v| @target_collection_name = v }
|
data/lib/exwiw/version.rb
CHANGED
data/lib/exwiw.rb
CHANGED
|
@@ -116,5 +116,7 @@ module Exwiw
|
|
|
116
116
|
# source: the server aborts the operation past the deadline. nil leaves it
|
|
117
117
|
# unset (no timeout). A per-collection `query_timeout_ms` overrides it.
|
|
118
118
|
# mongodb adapter only.
|
|
119
|
-
|
|
119
|
+
#
|
|
120
|
+
# `ssl_mode` / `ssl_ca` apply to both the driver connection and mysqldump. mysql adapter only.
|
|
121
|
+
ConnectionConfig = Struct.new(:adapter, :host, :port, :user, :password, :database_name, :uri, :mongodb_query_timeout_ms, :ssl_mode, :ssl_ca, keyword_init: true)
|
|
120
122
|
end
|