Skip to content

Commit

Permalink
play: add TLS options
Browse files Browse the repository at this point in the history
@TarantoolBot document
Title: play: add TLS options

This patch adds support of the ssl parameters to the `tt play` command.

Closes #1067
  • Loading branch information
patapenka-alexey committed Jan 3, 2025
1 parent 035ea0a commit f32a383
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
* `-e (--executable)`: specify Tarantool executable path.
* `-p (--pid)`: specify PID of the dumped process.
* `-t (--time)`: specify time of dump (seconds since the Epoch).
- `tt play`: support TLS options.

### Changed

Expand Down
30 changes: 29 additions & 1 deletion cli/checkpoint/lua/play.lua
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,30 @@ local function play(positional_arguments, keyword_arguments, opts)
log.error('Internal error: empty URI is provided')
os.exit(1)
end
local remote = netbox.new(uri, opts)

-- https://www.tarantool.io/en/doc/latest/platform/configuration/configuration_code/#configuration
local remote = nil
if opts.transport ~= nil then
remote = netbox.connect(
{
uri = uri,
params = {
transport = opts.transport,
ssl_cert_file = opts.ssl_cert_file,
ssl_key_file = opts.ssl_key_file,
ssl_ca_file = opts.ssl_ca_file,
ssl_ciphers = opts.ssl_ciphers
}
},
{
user = opts.user,
password = opts.password
}
)
else
remote = netbox.new(uri, opts)
end

if not remote:wait_connected() then
log.error('Fatal error: no connection to the host "%s"', uri)
os.exit(1)
Expand Down Expand Up @@ -149,6 +172,11 @@ local function main()
local opts = {
user = os.getenv('TT_CLI_PLAY_USERNAME'),
password = os.getenv('TT_CLI_PLAY_PASSWORD'),
transport = os.getenv('TT_CLI_PLAY_TRANSPORT'), -- should be 'ssl,plain' or not set
ssl_cert_file = os.getenv('TT_CLI_PLAY_SSL_CERT_FILE'),
ssl_key_file = os.getenv('TT_CLI_PLAY_SSL_KEY_FILE'),
ssl_ca_file = os.getenv('TT_CLI_PLAY_SSL_CA_FILE'),
ssl_ciphers = os.getenv('TT_CLI_PLAY_SSL_CIPHERS'),
}
play(positional_arguments, keyword_arguments, opts)
end
Expand Down
35 changes: 35 additions & 0 deletions cli/cmd/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ var (
playUsername string
// playPassword contains password flag.
playPassword string
// playSslKeyFile is a path to a private SSL key file.
playSslKeyFile string
// playSslCertFile is a path to an SSL certificate file.
playSslCertFile string
// playSslCaFile is a path to a trusted certificate authorities (CA) file.
playSslCaFile string
// playSslCiphers is a colon-separated (:) list of SSL cipher suites the
// connection can use.
playSslCiphers string
)

// NewPlayCmd creates a new play command.
Expand All @@ -56,6 +65,14 @@ func NewPlayCmd() *cobra.Command {

playCmd.Flags().StringVarP(&playUsername, "username", "u", "", "username")
playCmd.Flags().StringVarP(&playPassword, "password", "p", "", "password")
playCmd.Flags().StringVar(&playSslKeyFile, "sslkeyfile", "",
`path to a private SSL key file`)
playCmd.Flags().StringVar(&playSslCertFile, "sslcertfile", "",
`path to an SSL certificate file`)
playCmd.Flags().StringVar(&playSslCaFile, "sslcafile", "",
`path to a trusted certificate authorities (CA) file`)
playCmd.Flags().StringVar(&playSslCiphers, "sslciphers", "",
`colon-separated (:) list of SSL cipher suites the connection`)
playCmd.Flags().Uint64Var(&playFlags.To, "to", playFlags.To,
"Show operations ending with the given lsn")
playCmd.Flags().StringVar(&playFlags.Timestamp, "timestamp", playFlags.Timestamp,
Expand Down Expand Up @@ -143,6 +160,24 @@ func internalPlayModule(cmdCtx *cmdcontext.CmdCtx, args []string) error {
if playPassword != "" {
os.Setenv("TT_CLI_PLAY_PASSWORD", playPassword)
}

if playSslCertFile != "" {
os.Setenv("TT_CLI_PLAY_SSL_CERT_FILE", playSslCertFile)
}
if playSslKeyFile != "" {
os.Setenv("TT_CLI_PLAY_SSL_KEY_FILE", playSslKeyFile)
}
if playSslCaFile != "" {
os.Setenv("TT_CLI_PLAY_SSL_CA_FILE", playSslCaFile)
}
if playSslCiphers != "" {
os.Setenv("TT_CLI_PLAY_SSL_CIPHERS", playSslCiphers)
}
if playSslCertFile != "" || playSslKeyFile != "" ||
playSslCaFile != "" || playSslCiphers != "" {
os.Setenv("TT_CLI_PLAY_TRANSPORT", "ssl")
}

os.Setenv("TT_CLI_PLAY_SHOW_SYS", strconv.FormatBool(playFlags.ShowSystem))

// List of spaces is passed to lua play script via environment variable in json format.
Expand Down
52 changes: 49 additions & 3 deletions test/integration/play/test_play.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
try_execute_on_instance)

from utils import (BINARY_PORT_NAME, TarantoolTestInstance, control_socket,
create_tt_config, initial_snap, initial_xlog, lib_path,
run_command_and_get_output, run_path,
skip_if_cluster_app_unsupported, wait_file)
create_tt_config, get_tarantool_version, initial_snap,
initial_xlog, lib_path, run_command_and_get_output,
run_path, skip_if_cluster_app_unsupported,
skip_if_tarantool_ce, wait_file)

tarantool_major_version, tarantool_minor_version = get_tarantool_version()

# The name of instance config file within this integration tests.
# This file should be in /test/integration/play/test_file/.
Expand Down Expand Up @@ -301,3 +304,46 @@ def test_play_to_cluster_app(tt_cmd):
# Stop the Instance.
stop_app(tt_cmd, tmpdir, app_name)
shutil.rmtree(tmpdir)


@pytest.mark.skipif(tarantool_major_version == 1,
reason="skip TLS test for Tarantool 1.0")
def test_play_to_ssl_app(tt_cmd, tmpdir_with_cfg):
skip_if_tarantool_ce()

tmpdir = tmpdir_with_cfg
# The test application file.
test_app_path = os.path.join(os.path.dirname(__file__), "test_ssl_app")
# The test file.
empty_file = "empty.lua"
empty_file_path = os.path.join(os.path.dirname(__file__), "test_file", empty_file)
# File to play.
test_xlog_path = os.path.join(os.path.dirname(__file__), "test_file", "test.snap")

# Copy test data into temporary directory.
shutil.copytree(test_app_path, os.path.join(tmpdir, "test_ssl_app"))
shutil.copy(empty_file_path, os.path.join(tmpdir, "test_ssl_app", empty_file))

# Start an instance.
start_app(tt_cmd, tmpdir, "test_ssl_app")
try:
# 'ready' file should be created by application.
file = wait_file(os.path.join(tmpdir, "test_ssl_app"), "ready", [])
assert file != ""

server = "localhost:3013"
# Connect without SSL options.
ret, output = try_execute_on_instance(tt_cmd, tmpdir, server, empty_file)
assert not ret
assert re.search(r" ⨯ unable to establish connection", output)

cmd = [tt_cmd, "play", "localhost:3013", test_xlog_path,
"--sslkeyfile=test_ssl_app/localhost.key",
"--sslcertfile=test_ssl_app/localhost.crt",
"--sslcafile=test_ssl_app/ca.crt"]
rc, _ = run_command_and_get_output(cmd, cwd=tmpdir)
assert rc == 0

finally:
# Stop the Instance.
stop_app(tt_cmd, tmpdir, "test_ssl_app")
20 changes: 20 additions & 0 deletions test/integration/play/test_ssl_app/ca.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDLzCCAhegAwIBAgIUGCC5S+WF/lwRB18qO0cYhpaorugwDQYJKoZIhvcNAQEL
BQAwJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD0V4YW1wbGUtUm9vdC1DQTAeFw0y
MzAyMjAwOTMxMDBaFw00NTA3MjYwOTMxMDBaMCcxCzAJBgNVBAYTAlVTMRgwFgYD
VQQDDA9FeGFtcGxlLVJvb3QtQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDzqL1jXNzPUr5DwnByEjbXkuIlY+olEZ9EBs1ZRdiLaHeLhcpeBonCp9U8
itbhg9EHSCAb5aH5niTkG8q4lTXKqD7zxsRMzaDwMIYMDsINE+lFbHu2LNxmUhhZ
97Y1tjD/g71ry2W63eRrAzw6USk5ELMJvXLfa7mzG6Vf2cfRqRxADK6EDGZBSgHF
9O93Jofm4TipYNBeArxf4nsWEF0uxUCqUPQAbsJ2Nd0Fc6lo0hMq6cPRct4OYqPl
dlaGA3XPStEveRANE2W+MkMm3uoSlCa5Ye1EUbXVU6q3MPG8z39e29GxZ+5R9LPw
8AsoeQ7ty+TwdOKrN8AFU6/If46NAgMBAAGjUzBRMB0GA1UdDgQWBBTyx+obVLxg
6IMoDMBGwBlK2AoqxDAfBgNVHSMEGDAWgBTyx+obVLxg6IMoDMBGwBlK2AoqxDAP
BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQDVdjxWyKwox/KoO+c3
mw+txeJxAzfBl89KTemG6rNe4LXX+oK7xh9a2WosqEwUypDSsK9fzg/2QTZCuKHS
TZVNSspQdks/dwun2+yHzl/kJ4Ic8CvIsuaUErh1VLui5vmzrgEtgRargCzNIra4
Bcrx3dcEyQ90ZMPX7ysGYeVFP3it/hVug1XKE0hGRzZLBmZP3DtrLBXCZppkgLc7
julZOCSI2L+mCj/pTgWAcItFwq9V2ZOvXON5M4cDeAA5krOYyfgymBjwKrnUKDnk
91kcIuOM04msRzSme/I8RHRfc/p1JFv3Ve92dz9wrpPmrCJTLQQYQkyFa7YBPxsZ
QfXv
-----END CERTIFICATE-----
25 changes: 25 additions & 0 deletions test/integration/play/test_ssl_app/generate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -xeuo pipefail
# An example how-to re-generate testing certificates (because usually
# TLS certificates have expiration dates and some day they will expire).
#
# The instruction is valid for:
#
# $ openssl version
# OpenSSL 3.0.7 1 Nov 2022 (Library: OpenSSL 3.0.7 1 Nov 2022)

cat <<EOF > domains.ext
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
IP.1 = 127.0.0.1
EOF

openssl req -x509 -nodes -new -sha256 -days 8192 -newkey rsa:2048 -keyout ca.key -out ca.pem -subj "/C=US/CN=Example-Root-CA"
openssl x509 -outform pem -in ca.pem -out ca.crt

openssl req -new -nodes -newkey rsa:2048 -keyout localhost.key -out localhost.csr -subj "/C=US/ST=YourState/L=YourCity/O=Example-Certificates/CN=localhost"
openssl x509 -req -sha256 -days 8192 -in localhost.csr -CA ca.pem -CAkey ca.key -CAcreateserial -extfile domains.ext -out localhost.crt
23 changes: 23 additions & 0 deletions test/integration/play/test_ssl_app/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
local fiber = require('fiber')
local fio = require('fio')

box.cfg{ listen = {
uri = 'localhost:3013',
params = {
transport = 'ssl',
ssl_key_file = 'localhost.key',
ssl_cert_file = 'localhost.crt',
ssl_ca_file = 'ca.crt'
}
}}

box.schema.user.create('test', { password = 'password' , if_not_exists = true })
box.schema.user.grant('guest','read,write,execute,create,drop','universe')
box.schema.user.grant('test','read,write,execute,create,drop','universe')

fh = fio.open('ready', {'O_WRONLY', 'O_CREAT'}, tonumber('644',8))
fh:close()

while true do
fiber.sleep(5)
end
22 changes: 22 additions & 0 deletions test/integration/play/test_ssl_app/localhost.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDkjCCAnqgAwIBAgIUC9PNBa3RkQa0KOJXOp+WMG98wTowDQYJKoZIhvcNAQEL
BQAwJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD0V4YW1wbGUtUm9vdC1DQTAeFw0y
MzAyMjAwOTMxMDBaFw00NTA3MjYwOTMxMDBaMGcxCzAJBgNVBAYTAlVTMRIwEAYD
VQQIDAlZb3VyU3RhdGUxETAPBgNVBAcMCFlvdXJDaXR5MR0wGwYDVQQKDBRFeGFt
cGxlLUNlcnRpZmljYXRlczESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxmgkVi6OEPT6qXd9/IomN6TdXa5VX5PcPsG5
iFbTVobDsKAi2EpnhZG8w1XnwRyJdWhdxRM3sUUK8xRuSKfbpBfGuWtrwzW1YzTG
K8zH+4KqpWJZ2OLAN+VEjXoO89iw3Ubi0xCLCc+xD++3scOkEgsX7V5RqAtPwsLX
Nzr+yMRTLefPGvpszDzBuq1/NGlKUobnLb6Liqh5yS8E2o1AwrKbY5mrU6YD5Jg+
VVEdE33U9jAttq6kr7c40joE4SRwmoBdM+PUPC+tqMeYpent0JzBijaspgNJMjF9
ybWo15jChExpGUnM0uluQF9tegDRYlsXPHCyZiltOt9rgcOSJQIDAQABo3YwdDAf
BgNVHSMEGDAWgBTyx+obVLxg6IMoDMBGwBlK2AoqxDAJBgNVHRMEAjAAMAsGA1Ud
DwQEAwIE8DAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwHQYDVR0OBBYEFN+G
FCIuGca+p1evNT5cMfbQTeDJMA0GCSqGSIb3DQEBCwUAA4IBAQA2cPuIIoCo6P2f
5Khc1ywP9fXsUeZsikrpidFNTZkx3KuuNqrQvemfGbkxMR/TzAmKWa53dvGvVnW2
8g6+k4PIFspWTAIJbIY2EuMG4E4bvIRUirqxHxMSuhIlQ7b1ppyzVe/H7JwnLrFm
AMpv30P2EUraKs0BNqGWK+FkL8CpCrYhI0VJ4LmBEIgbn2hyLZC9RShiCn4bmYuv
X1TNe78U1nlFmIogtYJop0AcUun1+S6wyItLc8T4QJ/BiqaAK5cRhr17WRv5e07p
l2EjMFwkJNn5R8eO8ZsaO2jCd45n88jnLEpp4aAd7pLI87DZeVQCQOcYggHvz5Hz
Fh6oBthC
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions test/integration/play/test_ssl_app/localhost.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDGaCRWLo4Q9Pqp
d338iiY3pN1drlVfk9w+wbmIVtNWhsOwoCLYSmeFkbzDVefBHIl1aF3FEzexRQrz
FG5Ip9ukF8a5a2vDNbVjNMYrzMf7gqqlYlnY4sA35USNeg7z2LDdRuLTEIsJz7EP
77exw6QSCxftXlGoC0/Cwtc3Ov7IxFMt588a+mzMPMG6rX80aUpShuctvouKqHnJ
LwTajUDCsptjmatTpgPkmD5VUR0TfdT2MC22rqSvtzjSOgThJHCagF0z49Q8L62o
x5il6e3QnMGKNqymA0kyMX3JtajXmMKETGkZSczS6W5AX216ANFiWxc8cLJmKW06
32uBw5IlAgMBAAECggEAJtXjpurd6/vHxLwa8P+pk2K14cxp8ZdjmPUad9Fm9JzU
WRI/P87wjHiGVkXOY0JOtaiEEjs8v3ogNoxdOeOBXpE42LpqEX/FzXFbCN/AlT5y
YITryUQ5E7fQv1CQ9LIJjIZ/h4jJblY09kWZ0zXUO6PoPxIjMZ1lM14n+iuWC33p
1XyU1iR9H0YDbYgx5oikfB8EXpaKITxu9V5gQ/nHwOm57d9o7Y5lc1wt7IxN7yDa
0CfTN/l9l7l6hnchqX2uG+zDnvPCtZFQneOH4+UBwBXikGcF59bkysn6fW9CwWkm
8B0qiZ68+gpbiAQRhb48JtVobqfOE5oNO0J45IbXUQKBgQDvlZTvYOby6cHVyg57
C0SNJP/47ivO0yX6jvknWFM1F+XHTk/vTZWYjduYqHqnOdEcLcjg28EfdKIcprjX
SqnBUl7bsx/wywie5sJ9CncV7faeIw8j2f6edaOA77cty9HpwOc2WJ8/H8qTEDnm
HqbpXHvpRtd6D2nJT5Gn21MLSwKBgQDUAEm6PAFe1CD7/nA/+a0uHZJqbLJA/fHF
CeOxPQ6gP5CpyKEB7nMGAV78grmsdlD2tQh66x5DMOIi9MqI+bce2HpH3+3sWx7K
wzbiJPhHTL6DEWPqhqJPORtVOt386JNaNvr6Qq0izEdgShkLl2J9WtrNxL2bGD4l
m/aXhtWCTwKBgGJ9Dj2dizMejxVQu8UvK54OML/nQNEEEd+/eIMJFyODUG0vL0MD
lNSitDw8PjeSV/kKhUKSdAB3VNEMZH30bnZPYzlTmHTHMiMIX7lBXRUBvtjhNq8Z
RUdkurMdWCMWX5OFPkckBUrQydjM2dBUl27lGvcZrSi7P1SHRixHyAqjAoGATbat
UC+e8Pwh+z4SN+F2smj0uz6NOXXdorU1WktfiS7EAPkizGp0j8cA4t+o4KeellFW
gnid51OMEfRaKkwf7Ja+fIqB1Rqx9vIItG2I9doUHEfLsLUZ2qC8fEnQBl3bZj6x
UfwPK6pmn82J0M31tK4Rd0yflLMWVQMPKgyrR9ECgYAylXMnOH3XwtbzvO6b3/oX
38zshS41HaakTvKqQDzNqyyWmx/kUTba9n2Bf1uHTCx1AklPs6/9hgbefKpWL1bb
H0RcJ/jx+vND6KfONQOGSj+g0zpX81c/Nv3meNmVX6yc3lxDvE9Rl6LNdoplPTbg
niex+WQcCjnAau5lC0haBw==
-----END PRIVATE KEY-----

0 comments on commit f32a383

Please sign in to comment.