Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Add Redis ACL support
  • Loading branch information
davidpil2002 committed Feb 5, 2024
commit a8856f9b31c7f57c9c2df729282af7226a95f496
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_

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CA cert need protect by file permission? for example only RW/Admin user can modify it?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a nice addition, but currently we generated a cert just to have TLS working.
We tried performing the TLS without a certificate but did not manage to do so.

I think that adding some write protect (for admin only) is possible in this case.
@davidpil2002 - please review it :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes - the file was copied to the filesystem with the owner admin,
the code can be seen in this PR in the filename
files/build_templates/sonic_debian_extension.j2

generate_cert redis "Generic-cert"
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