forked from redis/redis
-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
TLS: Connections refactoring and TLS support.
* Introduce a connection abstraction layer for all socket operations and integrate it across the code base. * Provide an optional TLS connections implementation based on OpenSSL. * Pull a newer version of hiredis with TLS support. * Tests, redis-cli updates for TLS support.
- Loading branch information
Showing
85 changed files
with
4,622 additions
and
832 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
TLS Support -- Work In Progress | ||
=============================== | ||
|
||
This is a brief note to capture current thoughts/ideas and track pending action | ||
items. | ||
|
||
Getting Started | ||
--------------- | ||
|
||
### Building | ||
|
||
To build with TLS support you'll need OpenSSL development libraries (e.g. | ||
libssl-dev on Debian/Ubuntu). | ||
|
||
Run `make BUILD_TLS=yes`. | ||
|
||
### Tests | ||
|
||
To run Redis test suite with TLS, you'll need TLS support for TCL (i.e. | ||
`tcl-tls` package on Debian/Ubuntu). | ||
|
||
1. Run `./utils/gen-test-certs.sh` to generate a root CA and a server | ||
certificate. | ||
|
||
2. Run `./runtest --tls` or `./runtest-cluster --tls` to run Redis and Redis | ||
Cluster tests in TLS mode. | ||
|
||
### Running manually | ||
|
||
To manually run a Redis server with TLS mode (assuming `gen-test-certs.sh` was | ||
invoked so sample certificates/keys are available): | ||
|
||
./src/redis-server --tls-port 6379 --port 0 \ | ||
--tls-cert-file ./tests/tls/redis.crt \ | ||
--tls-key-file ./tests/tls/redis.key \ | ||
--tls-ca-cert-file ./tests/tls/ca.crt | ||
|
||
To connect to this Redis server with `redis-cli`: | ||
|
||
./src/redis-cli --tls \ | ||
--cert ./tests/tls/redis.crt \ | ||
--key ./tests/tls/redis.key \ | ||
--cacert ./tests/tls/ca.crt | ||
|
||
This will disable TCP and enable TLS on port 6379. It's also possible to have | ||
both TCP and TLS available, but you'll need to assign different ports. | ||
|
||
To make a Replica connect to the master using TLS, use `--tls-replication yes`, | ||
and to make Redis Cluster use TLS across nodes use `--tls-cluster yes`. | ||
|
||
**NOTE: This is still very much work in progress and some configuration is still | ||
missing or may change.** | ||
|
||
Connections | ||
----------- | ||
|
||
Connection abstraction API is mostly done and seems to hold well for hiding | ||
implementation details between TLS and TCP. | ||
|
||
1. Still need to implement the equivalent of AE_BARRIER. Because TLS | ||
socket-level read/write events don't correspond to logical operations, this | ||
should probably be done at the Read/Write handler level. | ||
|
||
2. Multi-threading I/O is not supported. The main issue to address is the need | ||
to manipulate AE based on OpenSSL return codes. We can either propagate this | ||
out of the thread, or explore ways of further optimizing MT I/O by having | ||
event loops that live inside the thread and borrow connections in/out. | ||
|
||
3. Finish cleaning up the implementation. Make sure all error cases are handled | ||
and reflected into connection state, connection state validated before | ||
certain operations, etc. | ||
- Clean (non-errno) interface to report would-block. | ||
- Consistent error reporting. | ||
|
||
4. Sync IO for TLS is currently implemented in a hackish way, i.e. making the | ||
socket blocking and configuring socket-level timeout. This means the timeout | ||
value may not be so accurate, and there would be a lot of syscall overhead. | ||
However I believe that getting rid of syncio completely in favor of pure | ||
async work is probably a better move than trying to fix that. For replication | ||
it would probably not be so hard. For cluster keys migration it might be more | ||
difficult, but there are probably other good reasons to improve that part | ||
anyway. | ||
|
||
5. A mechanism to re-trigger read callbacks for connections with unread buffers | ||
(the case of reading partial TLS frames): | ||
|
||
a) Before sleep should iterate connections looking for those with a read handler, | ||
SSL_pending() != 0 and no read event. | ||
b) If found, trigger read handler for these conns. | ||
c) After iteration if this state persists, epoll should be called in a way | ||
that won't block so the process continues and this behave the same as a | ||
level trigerred epoll. | ||
|
||
Replication | ||
----------- | ||
|
||
Diskless master replication is broken, until child/parent connection proxying is | ||
implemented. | ||
|
||
|
||
TLS Features | ||
------------ | ||
|
||
1. Add metrics to INFO. | ||
2. Add certificate authentication configuration (i.e. option to skip client | ||
auth, master auth, etc.). | ||
3. Add TLS cipher configuration options. | ||
4. [Optional] Add session caching support. Check if/how it's handled by clients | ||
to assess how useful/important it is. | ||
|
||
|
||
redis-benchmark | ||
--------------- | ||
|
||
The current implementation is a mix of using hiredis for parsing and basic | ||
networking (establishing connections), but directly manipulating sockets for | ||
most actions. | ||
|
||
This will need to be cleaned up for proper TLS support. The best approach is | ||
probably to migrate to hiredis async mode. | ||
|
||
|
||
Others | ||
------ | ||
|
||
Consider the implications of allowing TLS to be configured on a separate port, | ||
making Redis listening on multiple ports. | ||
|
||
This impacts many things, like | ||
1. Startup banner port notification | ||
2. Proctitle | ||
3. How slaves announce themselves | ||
4. Cluster bus port calculation |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,3 +5,4 @@ | |
/*.dylib | ||
/*.a | ||
/*.pc | ||
*.dSYM |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
CMAKE_MINIMUM_REQUIRED(VERSION 3.4.0) | ||
INCLUDE(GNUInstallDirs) | ||
PROJECT(hiredis) | ||
|
||
OPTION(ENABLE_SSL "Build hiredis_ssl for SSL support" OFF) | ||
|
||
MACRO(getVersionBit name) | ||
SET(VERSION_REGEX "^#define ${name} (.+)$") | ||
FILE(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/hiredis.h" | ||
VERSION_BIT REGEX ${VERSION_REGEX}) | ||
STRING(REGEX REPLACE ${VERSION_REGEX} "\\1" ${name} "${VERSION_BIT}") | ||
ENDMACRO(getVersionBit) | ||
|
||
getVersionBit(HIREDIS_MAJOR) | ||
getVersionBit(HIREDIS_MINOR) | ||
getVersionBit(HIREDIS_PATCH) | ||
getVersionBit(HIREDIS_SONAME) | ||
SET(VERSION "${HIREDIS_MAJOR}.${HIREDIS_MINOR}.${HIREDIS_PATCH}") | ||
MESSAGE("Detected version: ${VERSION}") | ||
|
||
PROJECT(hiredis VERSION "${VERSION}") | ||
|
||
SET(ENABLE_EXAMPLES OFF CACHE BOOL "Enable building hiredis examples") | ||
|
||
ADD_LIBRARY(hiredis SHARED | ||
async.c | ||
dict.c | ||
hiredis.c | ||
net.c | ||
read.c | ||
sds.c | ||
sockcompat.c) | ||
|
||
SET_TARGET_PROPERTIES(hiredis | ||
PROPERTIES | ||
VERSION "${HIREDIS_SONAME}") | ||
IF(WIN32 OR MINGW) | ||
TARGET_LINK_LIBRARIES(hiredis PRIVATE ws2_32) | ||
ENDIF() | ||
TARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC .) | ||
|
||
CONFIGURE_FILE(hiredis.pc.in hiredis.pc @ONLY) | ||
|
||
INSTALL(TARGETS hiredis | ||
DESTINATION "${CMAKE_INSTALL_LIBDIR}") | ||
|
||
INSTALL(FILES hiredis.h read.h sds.h async.h | ||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis) | ||
|
||
INSTALL(DIRECTORY adapters | ||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis) | ||
|
||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis.pc | ||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) | ||
|
||
IF(ENABLE_SSL) | ||
IF (NOT OPENSSL_ROOT_DIR) | ||
IF (APPLE) | ||
SET(OPENSSL_ROOT_DIR "/usr/local/opt/openssl") | ||
ENDIF() | ||
ENDIF() | ||
FIND_PACKAGE(OpenSSL REQUIRED) | ||
ADD_LIBRARY(hiredis_ssl SHARED | ||
ssl.c) | ||
TARGET_INCLUDE_DIRECTORIES(hiredis_ssl PRIVATE "${OPENSSL_INCLUDE_DIR}") | ||
TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE ${OPENSSL_LIBRARIES}) | ||
CONFIGURE_FILE(hiredis_ssl.pc.in hiredis_ssl.pc @ONLY) | ||
|
||
INSTALL(TARGETS hiredis_ssl | ||
DESTINATION "${CMAKE_INSTALL_LIBDIR}") | ||
|
||
INSTALL(FILES hiredis_ssl.h | ||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis) | ||
|
||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl.pc | ||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) | ||
ENDIF() | ||
|
||
IF(NOT (WIN32 OR MINGW)) | ||
ENABLE_TESTING() | ||
ADD_EXECUTABLE(hiredis-test test.c) | ||
TARGET_LINK_LIBRARIES(hiredis-test hiredis) | ||
ADD_TEST(NAME hiredis-test | ||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test.sh) | ||
ENDIF() | ||
|
||
# Add examples | ||
IF(ENABLE_EXAMPLES) | ||
ADD_SUBDIRECTORY(examples) | ||
ENDIF(ENABLE_EXAMPLES) |
Oops, something went wrong.