Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added tls support #32

Merged
merged 7 commits into from
Mar 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,18 @@ Usage: gears-cli run [OPTIONS] FILEPATH [EXTRA_ARGS]...
Run gears function

Options:
--host TEXT Redis host to connect to
--port INTEGER Redis port to connect to
--user TEXT Redis acl user
--password TEXT Redis password
--requirements TEXT Path to requirements.txt file
--help Show this message and exit.
--host TEXT Redis host to connect to
--port INTEGER Redis port to connect to
--user TEXT Redis acl user
--password TEXT Redis password
--ssl BOOLEAN Use ssl
--ssl-password TEXT Passphrase for ssl key
--ssl-keyfile TEXT Path to ssl key file
--ssl-certfile TEXT Path to ssl certificate file
--ssl-ca-certs TEXT Path to ssl ca certificate file
--ssl-verify-ca BOOLEAN Whether or not to us CA to verify certs
--requirements TEXT Path to requirements.txt file
--help Show this message and exit.

> gears-cli export-requirements --help
Usage: gears-cli export-requirements [OPTIONS]
Expand All @@ -57,6 +63,12 @@ Options:
--port INTEGER Redis port to connect to
--user TEXT Redis acl user
--password TEXT Redis password
--ssl BOOLEAN Use ssl
--ssl-password TEXT Passphrase for ssl key
--ssl-keyfile TEXT Path to ssl key file
--ssl-certfile TEXT Path to ssl certificate file
--ssl-ca-certs TEXT Path to ssl ca certificate file
--ssl-verify-ca BOOLEAN Whether or not to us CA to verify certs
--save-directory TEXT Directory for exported files
--output-prefix TEXT Prefix for the requirement zip file
--registration-id TEXT Regisrations ids to extract their requirements
Expand All @@ -74,6 +86,12 @@ Options:
--port INTEGER Redis port to connect to
--user TEXT Redis acl user
--password TEXT Redis password
--ssl BOOLEAN Use ssl
--ssl-password TEXT Passphrase for ssl key
--ssl-keyfile TEXT Path to ssl key file
--ssl-certfile TEXT Path to ssl certificate file
--ssl-ca-certs TEXT Path to ssl ca certificate file
--ssl-verify-ca BOOLEAN Whether or not to us CA to verify certs
--requirements-path TEXT Path of requirements directory containing
requirements zip files, could also be a zip file
contains more requirements zip files
Expand All @@ -91,6 +109,12 @@ Options:
--port INTEGER Redis port to connect to
--user TEXT Redis acl user
--password TEXT Redis password
--ssl BOOLEAN Use ssl
--ssl-password TEXT Passphrase for ssl key
--ssl-keyfile TEXT Path to ssl key file
--ssl-certfile TEXT Path to ssl certificate file
--ssl-ca-certs TEXT Path to ssl ca certificate file
--ssl-verify-ca BOOLEAN Whether or not to us CA to verify certs
--requirements-file TEXT Path to requirements.txt file
--help Show this message and exit.
```
82 changes: 72 additions & 10 deletions gears_cli/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,30 @@ def Green(data):
def gears_cli():
pass

def create_connection(host, port, password, username=None, decode_responses=True):
def create_connection(host,
port,
password,
username=None,
decode_responses=True,
ssl = False,
ssl_password=None,
ssl_keyfile=None,
ssl_certfile=None,
ssl_verify_ca=True,
ssl_ca_certs=None):
global args
try:
r = redis.Redis(host=host, port=port, password=password, username=username, decode_responses=decode_responses)
r = redis.Redis(host=host,
port=port,
password=password,
username=username,
decode_responses=decode_responses,
ssl=ssl,
ssl_password=ssl_password,
ssl_keyfile=ssl_keyfile,
ssl_certfile=ssl_certfile,
ssl_cert_reqs='required' if ssl_verify_ca else None,
ssl_ca_certs=ssl_ca_certs)
r.ping()
except Exception as e:
print(Colors.Bred('Cannot connect to Redis. Aborting (%s)' % str(e)))
Expand All @@ -73,10 +93,20 @@ def print_res(res, res_id):
@click.option('--port', default=6379, type=int, help='Redis port to connect to')
@click.option('--user', default=None, help='Redis acl user')
@click.option('--password', default=None, help='Redis password')
@click.option('--ssl', default=False, type=bool, help='Use ssl')
@click.option('--ssl-password', help='Passphrase for ssl key')
@click.option('--ssl-keyfile', type=click.Path(), help='Path to ssl key file')
@click.option('--ssl-certfile', type=click.Path(),help='Path to ssl certificate file')
@click.option('--ssl-ca-certs', type=click.Path(), help='Path to ssl ca certificate file')
@click.option('--ssl-verify-ca', default=True, type=bool, help='Whether or not to us CA to verify certs')
@click.option('--requirements-file', default=None, help='Path to requirements.txt file')
@click.argument('requirements', nargs=-1, type=click.UNPROCESSED)
def install_requirements(host, port, user, password, requirements_file, requirements):
r = create_connection(host=host, port=port, username=user, password=password)
def install_requirements(host, port, user, password,
ssl, ssl_password, ssl_keyfile, ssl_certfile, ssl_ca_certs, ssl_verify_ca,
requirements_file, requirements):
r = create_connection(host=host, port=port, username=user, password=password,
ssl=ssl, ssl_password=ssl_password, ssl_keyfile=ssl_keyfile, ssl_verify_ca=ssl_verify_ca,
ssl_certfile=ssl_certfile, ssl_ca_certs=ssl_ca_certs)

requirements = list(requirements)

Expand All @@ -97,11 +127,21 @@ def install_requirements(host, port, user, password, requirements_file, requirem
@click.option('--port', default=6379, type=int, help='Redis port to connect to')
@click.option('--user', default=None, help='Redis acl user')
@click.option('--password', default=None, help='Redis password')
@click.option('--ssl', default=False, type=bool, help='Use ssl')
@click.option('--ssl-password', help='Passphrase for ssl key')
@click.option('--ssl-keyfile', type=click.Path(), help='Path to ssl key file')
@click.option('--ssl-certfile', type=click.Path(), help='Path to ssl certificate file')
@click.option('--ssl-ca-certs', type=click.Path(), help='Path to ssl ca certificate file')
@click.option('--ssl-verify-ca', default=True, type=bool, help='Whether or not to us CA to verify certs')
@click.option('--requirements', default=None, help='Path to requirements.txt file')
@click.argument('filepath')
@click.argument('extra_args', nargs=-1, type=click.UNPROCESSED)
def run(host, port, user, password, requirements, filepath, extra_args):
r = create_connection(host=host, port=port, username=user, password=password)
def run(host, port, user, password, requirements,
ssl, ssl_password, ssl_keyfile, ssl_certfile, ssl_ca_certs, ssl_verify_ca,
filepath, extra_args):
r = create_connection(host=host, port=port, username=user, password=password,
ssl=ssl, ssl_password=ssl_password, ssl_keyfile=ssl_keyfile, ssl_verify_ca=ssl_verify_ca,
ssl_certfile=ssl_certfile, ssl_ca_certs=ssl_ca_certs)

extra_args = list(extra_args)
if requirements is not None:
Expand Down Expand Up @@ -187,13 +227,24 @@ def export_single_req(r, req_name, save_directory, output_prefix):
@click.option('--port', default=6379, type=int, help='Redis port to connect to')
@click.option('--user', default=None, help='Redis acl user')
@click.option('--password', default=None, help='Redis password')
@click.option('--ssl', default=False, type=bool, help='Use ssl')
@click.option('--ssl-password', help='Passphrase for ssl key')
@click.option('--ssl-keyfile', type=click.Path(), help='Path to ssl key file')
@click.option('--ssl-certfile', type=click.Path(), help='Path to ssl certificate file')
@click.option('--ssl-ca-certs', type=click.Path(), help='Path to ssl ca certificate file')
@click.option('--ssl-verify-ca', default=True, type=bool, help='Whether or not to us CA to verify certs')
@click.option('--save-directory', default='./', help='Directory for exported files')
@click.option('--output-prefix', default=None, help='Prefix for the requirement zip file')
@click.option('--registration-id', multiple=True, default=[], help='Regisrations ids to extract their requirements')
@click.option('--requirement', multiple=True, default=[], help='Requirement to export')
@click.option('--all', is_flag=True, default=False, help='Export all requirements')
def export_requirements(host, port, user, password, save_directory, output_prefix, registration_id, all, requirement):
r = create_connection(host=host, port=port, username=user, password=password, decode_responses=False)
def export_requirements(host, port, user, password,
ssl, ssl_password, ssl_keyfile, ssl_certfile, ssl_ca_certs, ssl_verify_ca,
save_directory, output_prefix, registration_id, all, requirement):
r = create_connection(host=host, port=port, username=user, password=password,
ssl=ssl, ssl_password=ssl_password, ssl_keyfile=ssl_keyfile,
ssl_certfile=ssl_certfile, ssl_ca_certs=ssl_ca_certs, ssl_verify_ca=ssl_verify_ca,
decode_responses=False)

if all:
all_reqs = r.execute_command('RG.PYDUMPREQS')
Expand Down Expand Up @@ -245,11 +296,19 @@ def import_single_req(r, req_io, bulk_size_in_bytes):
@click.option('--port', default=6379, type=int, help='Redis port to connect to')
@click.option('--user', default=None, help='Redis acl user')
@click.option('--password', default=None, help='Redis password')
@click.option('--ssl', default=False, type=bool, help='Use ssl')
@click.option('--ssl-password', help='Passphrase for ssl key')
@click.option('--ssl-keyfile', type=click.Path(), help='Path to ssl key file')
@click.option('--ssl-certfile', type=click.Path(), help='Path to ssl certificate file')
@click.option('--ssl-ca-certs', type=click.Path(), help='Path to ssl ca certificate file')
@click.option('--ssl-verify-ca', default=True, type=bool, help='Whether or not to us CA to verify certs')
@click.option('--requirements-path', default='./', help='Path of requirements directory containing requirements zip files, could also be a zip file contains more requirements zip files')
@click.option('--all', is_flag=True, default=False, help='Import all requirements in zip file')
@click.option('--bulk-size', default=10, type=int, help='Max bulk size to send to redis in MB')
@click.argument('requirements', nargs=-1, type=click.UNPROCESSED)
def import_requirements(host, port, user, password, requirements_path, all, bulk_size, requirements):
def import_requirements(host, port, user, password,
ssl, ssl_password, ssl_keyfile, ssl_certfile, ssl_ca_certs, ssl_verify_ca,
requirements_path, all, bulk_size, requirements):
def install_req(req):
try:
req_data = zf.read(req)
Expand All @@ -260,7 +319,10 @@ def install_req(req):
import_single_req(r, io_buffer, bulk_size_in_bytes)
print(Colors.Green('Requirement %s imported successfully' % req))

r = create_connection(host=host, port=port, username=user, password=password, decode_responses=False)
r = create_connection(host=host, port=port, username=user, password=password,
ssl=ssl, ssl_password=ssl_password, ssl_keyfile=ssl_keyfile,
ssl_certfile=ssl_certfile, ssl_ca_certs=ssl_ca_certs, ssl_verify_ca=ssl_verify_ca,
decode_responses=False)

bulk_size_in_bytes = bulk_size * 1024 * 1024

Expand Down
31 changes: 31 additions & 0 deletions generate_tests_cert.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash
set -x

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
ROOT=$(cd "$HERE" && pwd)

mkdir -p $ROOT/tests/tls
cd $ROOT/tests/tls

openssl genrsa -out ca.key 4096
openssl req \
-x509 -new -nodes -sha256 \
-key ca.key \
-days 3650 \
-subj '/O=Redis Test/CN=Certificate Authority' \
-out ca.crt
openssl genrsa -out redis.key -aes256 -passout pass:foobar 2048
openssl req \
-new -sha256 \
-key redis.key \
-passin pass:foobar \
-subj '/O=Redis Test/CN=Server' | \
openssl x509 \
-req -sha256 \
-CA ca.crt \
-CAkey ca.key \
-CAserial ca.txt \
-CAcreateserial \
-days 365 \
-out redis.crt
openssl dhparam -out redis.dh 2048
10 changes: 6 additions & 4 deletions pytests/test_basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,25 @@

Defaults.decode_responses = True

SSL_ARGS = ['--ssl', 'True', '--ssl-keyfile', './tests/tls/redis.key', '--ssl-certfile', './tests/tls/redis.crt', '--ssl-ca-certs', './tests/tls/ca.crt', '--ssl-password', 'foobar']

def set_acl_user(env):
env.expect('ACL', 'SETUSER', 'foo', 'on', '>pass', '+@all').ok()

def run_internal_test(env, extra_args=[]):
runner = CliRunner()
result = runner.invoke(run, extra_args + ['./pytests/example.py'])
result = runner.invoke(run, extra_args + SSL_ARGS + ['./pytests/example.py'])
env.assertEqual(result.exit_code, 0)
env.assertContains('"1"', result.output)

def requirements_internal_test(env, extra_args=[], after_restart_callback=None):
runner = CliRunner()
result = runner.invoke(install_requirements, extra_args + ['redis'])
result = runner.invoke(install_requirements, extra_args + SSL_ARGS + ['redis'])
env.assertEqual(result.exit_code, 0)
env.assertContains('Done', result.output)

# export requirement redis
result = runner.invoke(export_requirements, extra_args + ['--requirement', 'redis'])
result = runner.invoke(export_requirements, extra_args + SSL_ARGS + ['--requirement', 'redis'])
env.assertEqual(result.exit_code, 0)
env.assertContains('Saving exported requirement into ', result.output)
exported_file_path = result.output[result.output.find('/') : result.output.find('zip') + 3]
Expand All @@ -32,7 +34,7 @@ def requirements_internal_test(env, extra_args=[], after_restart_callback=None):
after_restart_callback(env)

# import redis requirement
result = runner.invoke(import_requirements, extra_args + ['--requirements-path', os.path.dirname(exported_file_path), os.path.basename(exported_file_path)])
result = runner.invoke(import_requirements, extra_args + SSL_ARGS + ['--requirements-path', os.path.dirname(exported_file_path), os.path.basename(exported_file_path)])
env.assertEqual(result.exit_code, 0)
env.assertContains('imported successfully', result.output)

Expand Down
3 changes: 2 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ commands_pre =
{envdir}/bin/python -m pip install poetry
{envdir}/bin/python -m pip install RLTest~=0.5.0
{envdir}/bin/python -m poetry install
commands = {envdir}/bin/python -m RLTest --module /var/opt/redislabs/lib/modules/redisgears.so --module-args "Plugin /var/opt/redislabs/modules/rg/plugin/gears_python.so" -t pytests/
bash ./generate_tests_cert.sh
commands = {envdir}/bin/python -m RLTest --module /var/opt/redislabs/lib/modules/redisgears.so --module-args "Plugin /var/opt/redislabs/modules/rg/plugin/gears_python.so" -t pytests/ --tls --tls-cert-file ./tests/tls/redis.crt --tls-key-file ./tests/tls/redis.key --tls-ca-cert-file ./tests/tls/ca.crt --tls-passphrase foobar