Skip to content

Commit

Permalink
Add Redis ACL support
Browse files Browse the repository at this point in the history
  • Loading branch information
davidpil2002 committed Feb 5, 2024
1 parent d89854f commit a8856f9
Show file tree
Hide file tree
Showing 18 changed files with 169 additions and 17 deletions.
12 changes: 11 additions & 1 deletion dockers/docker-database/Dockerfile.j2
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,27 @@ RUN pip3 install click
{{ install_debian_packages(docker_database_debs.split(' ')) }}
{%- endif %}

ENV REDIS_SHADOW_TLS=/etc/ssl/certs_redis/certs/tls/
# Clean up
RUN apt-get clean -y && \
apt-get autoclean -y && \
apt-get autoremove -y && \
rm -rf /debs ~/.cache && \
sed -ri 's/^(save .*$)/# \1/g; \
s/^daemonize yes$/daemonize no/; \
s/^logfile .*$/logfile ""/; \
s|^logfile .*$|logfile /etc/redis/redis-server.log|; \
s/^# syslog-enabled no$/syslog-enabled no/; \
s/^# unixsocket/unixsocket/; \
s/redis-server.sock/redis.sock/g; \
s/^client-output-buffer-limit pubsub [0-9]+mb [0-9]+mb [0-9]+/client-output-buffer-limit pubsub 0 0 0/; \
s/^port 6379/# port 6379/; \
s/^# port 0/port 0/; \
s/^# tls-port 6379/tls-port 6379/; \
/tls-auth-clients no/s/^# //; \
s|# tls-cert-file .*|tls-cert-file '"$REDIS_SHADOW_TLS"'/redis.crt|; \
s|# tls-key-file .*|tls-key-file '"$REDIS_SHADOW_TLS"'/redis.key|; \
s|# tls-ca-cert-file .*|tls-ca-cert-file '"$REDIS_SHADOW_TLS"'/ca.crt|; \
/aclfile \/etc\/redis\/users.acl/s/^# //; \
s/^notify-keyspace-events ""$/notify-keyspace-events AKE/; \
s/^databases [0-9]+$/databases 100/ \
' /etc/redis/redis.conf
Expand All @@ -48,5 +57,6 @@ COPY ["files/supervisor-proc-exit-listener", "/usr/bin"]
COPY ["files/sysctl-net.conf", "/etc/sysctl.d/"]
COPY ["files/update_chassisdb_config", "/usr/local/bin/"]
COPY ["flush_unused_database", "/usr/local/bin/"]
COPY ["users.acl.template", "/etc/redis/"]

ENTRYPOINT ["/usr/local/bin/docker-database-init.sh"]
10 changes: 10 additions & 0 deletions dockers/docker-database/docker-database-init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,14 @@ ln -sf /usr/share/zoneinfo/$TZ /etc/localtime

chown -R redis:redis $REDIS_DIR

# Redis PW update in users.acl
acl_template=$(< /etc/redis/users.acl.template)

USER_COUNTER_PASSWORD=$(cat /etc/shadow_redis_dir/shadow_redis_admin)
acl_new_admin_user="${acl_template//\$\{USER_COUNTER_PASSWORD\}/$USER_COUNTER_PASSWORD}"

MONITOR_PASSWORD=$(cat /etc/shadow_redis_dir/shadow_redis_monitor)
acl_new_admin_monitor_users="${acl_new_admin_user//\$\{MONITOR_PASSWORD\}/$MONITOR_PASSWORD}"
echo "$acl_new_admin_monitor_users" > /etc/redis/users.acl

exec /usr/local/bin/supervisord
3 changes: 1 addition & 2 deletions dockers/docker-database/supervisord.conf.j2
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@ dependent_startup=true
{%- else -%}
{%- set LOOPBACK_IP = '' -%}
{%- endif -%}
command=/bin/bash -c "{ [[ -s /var/lib/{{ redis_inst }}/dump.rdb ]] || rm -f /var/lib/{{ redis_inst }}/dump.rdb; } && mkdir -p /var/lib/{{ redis_inst }} && exec /usr/bin/redis-server /etc/redis/redis.conf --bind {{ LOOPBACK_IP }} {{ redis_items['hostname'] }} --port {{ redis_items['port'] }} --unixsocket {{ redis_items['unix_socket_path'] }} --pidfile /var/run/redis/{{ redis_inst }}.pid --dir /var/lib/{{ redis_inst }}"
command=/bin/bash -c "{ [[ -s /var/lib/{{ redis_inst }}/dump.rdb ]] || rm -f /var/lib/{{ redis_inst }}/dump.rdb; } && mkdir -p /var/lib/{{ redis_inst }} && exec /usr/bin/redis-server /etc/redis/redis.conf --bind {{ LOOPBACK_IP }} {{ redis_items['hostname'] }} --unixsocket {{ redis_items['unix_socket_path'] }} --pidfile /var/run/redis/{{ redis_inst }}.pid --dir /var/lib/{{ redis_inst }}"
priority=2
user=redis
autostart=true
autorestart=false
stdout_logfile=syslog
Expand Down
3 changes: 3 additions & 0 deletions dockers/docker-database/users.acl.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
user admin on +@all -DEBUG ~* >${USER_COUNTER_PASSWORD}
user monitor on +hgetall +keys +select +get +mget +hget -DEBUG ~* >${MONITOR_PASSWORD}
user default off
2 changes: 1 addition & 1 deletion dockers/docker-orchagent/buffermgrd.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env bash

BUFFER_CALCULATION_MODE=$(redis-cli -n 4 hget "DEVICE_METADATA|localhost" buffer_model)
BUFFER_CALCULATION_MODE=$(sonic-db-cli CONFIG_DB hget "DEVICE_METADATA|localhost" buffer_model)

if [ "$BUFFER_CALCULATION_MODE" == "dynamic" ]; then
BUFFERMGRD_ARGS="-a /etc/sonic/asic_table.json"
Expand Down
2 changes: 2 additions & 0 deletions files/build_templates/docker_image_ctl.j2
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,7 @@ start() {
# TODO: Mellanox will remove the --tmpfs exception after SDK socket path changed in new SDK version
{%- endif %}
docker create {{docker_image_run_opt}} \
-v /etc/shadow_redis_dir:/etc/shadow_redis_dir:ro \
{%- if docker_container_name != "dhcp_server" %}
--net=$NET \
{%- endif %}
Expand Down Expand Up @@ -627,6 +628,7 @@ start() {
{%- endif %}
{%- if docker_container_name == "database" %}
$DB_OPT \
-v /etc/ssl/certs_redis:/etc/ssl/certs_redis:ro \
{%- else %}
-v /var/run/redis$DEV:/var/run/redis:rw \
-v /var/run/redis-chassis:/var/run/redis-chassis:ro \
Expand Down
13 changes: 13 additions & 0 deletions files/build_templates/sonic_debian_extension.j2
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y in
# Install j2cli for handling jinja template
sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install j2cli

# Create an empty Redis dir for the generation of Redis ACL passwords
sudo mkdir $FILESYSTEM_ROOT/etc/shadow_redis_dir

# Create an empty Redis dir for the public cacert of Redis TLS
sudo mkdir $FILESYSTEM_ROOT/etc/shadow_redis_dir/certs_redis

# Install Python client for Redis
sudo https_proxy=$https_proxy LANG=C chroot $FILESYSTEM_ROOT pip3 install "redis==3.5.3"

Expand Down Expand Up @@ -932,6 +938,13 @@ sudo LANG=C cp $SCRIPTS_DIR/mgmt-framework.sh $FILESYSTEM_ROOT/usr/local/bin/mgm
sudo LANG=C cp $SCRIPTS_DIR/asic_status.sh $FILESYSTEM_ROOT/usr/local/bin/asic_status.sh
sudo LANG=C cp $SCRIPTS_DIR/asic_status.py $FILESYSTEM_ROOT/usr/local/bin/asic_status.py

# Copy Redis Certificate generator script
FSROOT_ETC_SSL_CERTS_REDIS=$FILESYSTEM_ROOT/etc/ssl/certs_redis
sudo LANG=C mkdir $FSROOT_ETC_SSL_CERTS_REDIS
sudo LANG=C cp $SCRIPTS_DIR/gen-redis-certs.sh $FSROOT_ETC_SSL_CERTS_REDIS/gen-redis-certs.sh
sudo chroot $FILESYSTEM_ROOT chown admin:admin /etc/ssl/certs_redis/gen-redis-certs.sh
sudo chmod 770 $FSROOT_ETC_SSL_CERTS_REDIS/gen-redis-certs.sh

# Copy sonic-netns-exec script
sudo LANG=C cp $SCRIPTS_DIR/sonic-netns-exec $FILESYSTEM_ROOT/usr/bin/sonic-netns-exec

Expand Down
4 changes: 2 additions & 2 deletions files/image_config/pcie-check/pcie-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function check_and_rescan_pcie_devices()
fi

if [ "$(eval $PCIE_CHK_CMD)" = "$EXPECTED" ]; then
redis-cli -n 6 HSET $PCIE_STATUS_TABLE "status" "PASSED"
sonic-db-cli STATE_DB HSET $PCIE_STATUS_TABLE "status" "PASSED"
debug "PCIe check passed"
exit
else
Expand All @@ -54,7 +54,7 @@ function check_and_rescan_pcie_devices()

done
debug "PCIe check failed"
redis-cli -n 6 HSET $PCIE_STATUS_TABLE "status" "FAILED"
sonic-db-cli STATE_DB HSET $PCIE_STATUS_TABLE "status" "FAILED"
}

check_and_rescan_pcie_devices
31 changes: 31 additions & 0 deletions files/image_config/platform/rc.local
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,37 @@ fi

program_console_speed

# Generate password for Redis DB
echo "Redis PW generation"
set +x
REDIS_SHADOW_PATH=/etc/shadow_redis_dir
openssl_random_cmd=$(openssl rand -base64 32)
ADMIN_PASSWORD=''
ADMIN_PASSWORD=$(echo "$openssl_random_cmd" | tr -d '\n')
REDIS_SHADOW_ADMIN_PATH=$REDIS_SHADOW_PATH/shadow_redis_admin
touch "$REDIS_SHADOW_ADMIN_PATH"
chown admin:admin "$REDIS_SHADOW_ADMIN_PATH"
chmod 640 "$REDIS_SHADOW_ADMIN_PATH"
echo "$ADMIN_PASSWORD" > "$REDIS_SHADOW_ADMIN_PATH"
MONITOR_PASSWORD=''
MONITOR_PASSWORD=$(echo "$openssl_random_cmd" | tr -d '\n')
echo "$MONITOR_PASSWORD" > $REDIS_SHADOW_PATH/shadow_redis_monitor

# TLS support
ETC_SSL=/etc/ssl/certs_redis
REDIS_CERTS=$ETC_SSL/certs
REDIS_CERTS_TLS=$REDIS_CERTS/tls

# Check if the directory exists and remove it if it does
[ -d "$REDIS_CERTS" ] && rm -rf "$REDIS_CERTS"

(cd $ETC_SSL && ./gen-redis-certs.sh)

# Copy CA cert to host share location for Redis client usage
cp $REDIS_CERTS_TLS/ca.crt $REDIS_SHADOW_PATH/certs_redis

set -x

if [ -f $FIRST_BOOT_FILE ]; then

echo "First boot detected. Performing first boot tasks..."
Expand Down
52 changes: 52 additions & 0 deletions files/scripts/gen-redis-certs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/bin/bash

# from redis official repo https://github.com/redis/redis/
# Generate some test certificates which are used by the regression test suite:
#
# certs/tls/ca.{crt,key} Self signed CA certificate.
# certs/tls/redis.{crt,key} A certificate with no key usage/policy restrictions.

generate_cert() {
local name=$1
local cn="$2"
local opts="$3"

local keyfile=certs/tls/${name}.key
local certfile=certs/tls/${name}.crt

[ -f $keyfile ] || openssl genrsa -out $keyfile 2048
openssl req \
-new -sha256 \
-subj "/O=Redis Test/CN=$cn" \
-key $keyfile | \
openssl x509 \
-req -sha256 \
-CA certs/tls/ca.crt \
-CAkey certs/tls/ca.key \
-CAserial certs/tls/ca.txt \
-CAcreateserial \
-days 365 \
$opts \
-out $certfile
}

mkdir -p certs/tls
[ -f certs/tls/ca.key ] || openssl genrsa -out certs/tls/ca.key 4096
openssl req \
-x509 -new -nodes -sha256 \
-key certs/tls/ca.key \
-days 3650 \
-subj '/O=Redis Test/CN=Certificate Authority' \
-out certs/tls/ca.crt

cat > certs/tls/openssl.cnf <<_END_
[ server_cert ]
keyUsage = digitalSignature, keyEncipherment
nsCertType = server
[ client_cert ]
keyUsage = digitalSignature, keyEncipherment
nsCertType = client
_END_

generate_cert redis "Generic-cert"
8 changes: 6 additions & 2 deletions platform/mellanox/mlnx-platform-api/sonic_platform/module.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES.
# Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES.
# Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -35,7 +35,11 @@ class Module(ModuleBase):
STATE_DB = 6
STATE_MODULAR_CHASSIS_SLOT_TABLE = 'MODULAR_CHASSIS_SLOT|{}'
FIELD_SEQ_NO = 'seq_no'
redis_client = redis.Redis(db = STATE_DB)
USERNAME = 'admin'
PASSWORD = utils.read_str_from_file('/etc/shadow_redis_dir/shadow_redis_admin')
REDIS_SHADOW_TLS_CA="/etc/shadow_redis_dir/certs_redis/ca.crt"
redis_client = redis.Redis(port=6379, db=STATE_DB, username=USERNAME, password=PASSWORD, ssl=True, ssl_cert_reqs=None, ssl_ca_certs=REDIS_SHADOW_TLS_CA)


def __init__(self, slot_id):
super(Module, self).__init__()
Expand Down
2 changes: 1 addition & 1 deletion platform/vs/docker-sonic-vs/buffermgrd.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env bash

BUFFER_CALCULATION_MODE=$(redis-cli -n 4 hget "DEVICE_METADATA|localhost" buffer_model)
BUFFER_CALCULATION_MODE=$(sonic-db-cli CONFIG_DB hget "DEVICE_METADATA|localhost" buffer_model)
export ASIC_VENDOR=vs

if [ "$BUFFER_CALCULATION_MODE" == "dynamic" ]; then
Expand Down
4 changes: 2 additions & 2 deletions platform/vs/tests/bgp/test_default_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def test_DefaultRoute(dvs, testlog):

time.sleep(10)

(exit_code, output) = dvs.runcmd(["redis-cli", "hgetall", "ROUTE_TABLE:0.0.0.0/0"])
(exit_code, output) = dvs.runcmd(["sonic-db-cli", "APPL_DB", "hgetall", "ROUTE_TABLE:0.0.0.0/0"])
print(exit_code, output)

# make sure 10.10.10.1 is the correct next hop for default route
Expand All @@ -34,7 +34,7 @@ def test_DefaultRoute(dvs, testlog):
# insert default route for table default
dvs.runcmd("ip route add default via 172.17.0.1 table default")

(exit_code, output) = dvs.runcmd(["redis-cli", "hgetall", "ROUTE_TABLE:0.0.0.0/0"])
(exit_code, output) = dvs.runcmd(["sonic-db-cli", "APPL_DB", "hgetall", "ROUTE_TABLE:0.0.0.0/0"])
print(exit_code, output)

time.sleep(10)
Expand Down
9 changes: 9 additions & 0 deletions src/sonic-config-engine/tests/test_cfggen.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@

from unittest import TestCase

import sys
if sys.version_info.major == 3:
from unittest import mock
else:
import mock

TOR_ROUTER = 'ToRRouter'
BACKEND_TOR_ROUTER = 'BackEndToRRouter'
LEAF_ROUTER = 'LeafRouter'
BACKEND_LEAF_ROUTER = 'BackEndLeafRouter'

@mock.patch('swsssdk.util.read_from_file', mock.MagicMock(return_value='mock_password'))
class TestCfgGen(TestCase):

def setUp(self):
Expand Down Expand Up @@ -219,6 +226,8 @@ def test_template_json_batch_mode(self):
def test_minigraph_acl(self):
argument = ['-m', self.sample_graph_t0, '-p', self.port_config, '-v', 'ACL_TABLE']
output = self.run_script(argument, True, True)
# ignore safe error in unittest infra, since this file exists only in a runtime device
output = output.strip().replace("ERROR:root:Failed to read from /etc/shadow_redis_dir/shadow_redis_admin, errno is [Errno 2] No such file or directory: '/etc/shadow_redis_dir/shadow_redis_admin'", '')
self.assertEqual(
utils.to_dict(output.strip().replace("Warning: Ignoring Control Plane ACL NTP_ACL without type\n", '')),
utils.to_dict(
Expand Down
1 change: 1 addition & 0 deletions src/sonic-config-engine/tests/test_cfggen_platformJson.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# Global Variable
PLATFORM_OUTPUT_FILE = "platform_output.json"

@mock.patch('swsssdk.util.read_from_file', mock.MagicMock(return_value='mock_password'))
class TestCfgGenPlatformJson(TestCase):

def setUp(self):
Expand Down
6 changes: 6 additions & 0 deletions src/sonic-config-engine/tests/test_j2files.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@
import tests.common_utils as utils
from sonic_py_common.general import getstatusoutput_noshell, getstatusoutput_noshell_pipe

import sys
if sys.version_info.major == 3:
from unittest import mock
else:
import mock

@mock.patch('swsssdk.util.read_from_file', mock.MagicMock(return_value='mock_password'))
class TestJ2Files(TestCase):
def setUp(self):
self.test_dir = os.path.dirname(os.path.realpath(__file__))
Expand Down
7 changes: 7 additions & 0 deletions src/sonic-config-engine/tests/test_minigraph_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,17 @@

from unittest import TestCase

import sys
if sys.version_info.major == 3:
from unittest import mock
else:
import mock

TOR_ROUTER = 'ToRRouter'
BACKEND_TOR_ROUTER = 'BackEndToRRouter'
BMC_MGMT_TOR_ROUTER = 'BmcMgmtToRRouter'

@mock.patch('swsssdk.util.read_from_file', mock.MagicMock(return_value='mock_password'))
class TestCfgGenCaseInsensitive(TestCase):

def setUp(self):
Expand Down
17 changes: 11 additions & 6 deletions src/sonic-config-engine/tests/test_multinpu_cfggen.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@
from unittest import TestCase
from sonic_py_common.general import getstatusoutput_noshell

import sys
if sys.version_info.major == 3:
from unittest import mock
else:
import mock


SKU = 'multi-npu-01'
ASIC_SKU = 'multi-npu-asic'
NUM_ASIC = 4
HOSTNAME = 'multi_npu_platform_01'
DEVICE_TYPE = 'LeafRouter'

@mock.patch('swsssdk.util.read_from_file', mock.MagicMock(return_value='mock_password'))
class TestMultiNpuCfgGen(TestCase):

def setUp(self):
Expand Down Expand Up @@ -45,6 +52,10 @@ def run_script(self, argument, check_stderr=True, output_file=None, validateYang

if utils.PY3x:
output = output.decode()

# ignore safe error in unittest infra, since this file exists only in a runtime device
output = output.replace("ERROR:root:Failed to read from /etc/shadow_redis_dir/shadow_redis_admin, errno is [Errno 2] No such file or directory: '/etc/shadow_redis_dir/shadow_redis_admin'", '')

if output_file:
with open(output_file, 'w') as f:
f.write(output)
Expand Down Expand Up @@ -538,12 +549,6 @@ def test_buffers_chassis_packet_lc_template(self):
}
)

def test_bgpd_frr_frontendasic(self):
self.assertTrue(*self.run_frr_asic_case('bgpd/bgpd.conf.j2', 'bgpd_frr_frontend_asic.conf', "asic0", self.port_config[0]))

def test_bgpd_frr_backendasic(self):
self.assertTrue(*self.run_frr_asic_case('bgpd/bgpd.conf.j2', 'bgpd_frr_backend_asic.conf', "asic3", self.port_config[3]))

def test_no_asic_in_graph(self):
argument = ["-m", self.sample_graph, "-p", self.sample_no_asic_port_config, "-n", "asic4", "--var-json", "PORTCHANNEL"]
output = json.loads(self.run_script(argument, check_stderr=False, validateYang=False))
Expand Down

0 comments on commit a8856f9

Please sign in to comment.