Skip to content

Commit

Permalink
Adding SASL authentication + add documentation
Browse files Browse the repository at this point in the history
closes gh-4

+ cmd_set isn't invoked in text protocol

closes gh-24
  • Loading branch information
bigbes committed Aug 25, 2016
1 parent b5cf524 commit c49af59
Show file tree
Hide file tree
Showing 25 changed files with 1,214 additions and 527 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
# Find Tarantool and Lua dependecies
set(TARANTOOL_FIND_REQUIRED ON)
find_package(Tarantool)
find_package(CyrusSASL)
# include(cmake/FindTarantool.cmake)
include_directories(${TARANTOOL_INCLUDE_DIRS})

Expand Down
57 changes: 53 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ Memcached protocol 'wrapper' for tarantool.

### Prerequisites

* Tarantol 1.6.7+ with header files (tarantool && tarantool-dev packages).
* Tarantol 1.6.8+ with header files (tarantool && tarantool-dev packages).
* Cyrus SASL library (with header files)
* Python >= 2.7, <3 with next packages (for testing only):
- PyYAML
- msgpack-python
Expand All @@ -31,8 +32,9 @@ make
make install
```

Or use LuaRocks (in this case you'll need `libsmall`, `libsmall-dev` and `tarantool-dev`
packages available from our binary repository at http://tarantool.org/dist/master):
Or use LuaRocks (in this case you'll need `libsmall`, `libsmall-dev`, `tarantool-dev`
packages available from our binary repository at http://tarantool.org/dist/master, and
system package `libsasl2-dev`):

``` bash
luarocks install https://raw.githubusercontent.com/tarantool/memcached/master/memcached-scm-1.rockspec --local
Expand Down Expand Up @@ -104,6 +106,53 @@ END
- ~~`disk` - store everything on hdd/ssd (using `sophia` engine)~~ (not yet supported)
* *space_name* - custom name for a memcached space, default is `__mc_<instance name>`
* *if_not_exists* - do not throw error if an instance already exists.
* *sasl* - enable or disable SASL support (disabled by default)

## SASL support

Usual rules for memcached are aplicable for this plugin:

1. Create user (NOTE: it'll use custom folder):

``` bash
echo testpass | saslpasswd2 -p -c testuser -f /tmp/test-tarantool-memcached.sasldb
```

2. Place configuration file `/etc/sasl2/tarantool-memcached.conf`. For example:

```
mech_list: plain cram-md5
log_level: 7
sasldb_path: /tmp/test-tarantool-memcached.sasldb
```

NOTE: This will disable 'ANONYMOUS' (and other, that aren't listed)
authentication plugins.

NOTE: This will set logging level to the highest possible

NOTE: This will set custom path for database path

3. Run tarantool with memcached plugin with SASL enabled

```
local memcached = require('memcached')
local instance = memcached.create('my_instance', '0.0.0.0:11211', {
sasl = true
})
```

4. Use your favorite binary(!!) memcached client, that supports(!!) SASL:

Example using Python's ['python-binary-memcached' library](https://github.com/jaysonsantos/python-binary-memcached)
```
import bmemcached
client = bmemcached.Client(('127.0.0.1:11211', ), 'testuser', 'testpasswd')
client.set('key', 'value')
print client.get('key')
```

For custom configuration file path, please, use `SASL_CONF_PATH` environment variable.

## What's supported, what's not and other features

Expand All @@ -125,7 +174,7 @@ END
- `append`/`prepend`/`incr`/`decr`
- `verbosity` - partially, logging is not very good.
- `stat` - `reset` is supported and all stats too.
- for now **SASL** authentication is not supported
- **SASL** authentication is supported
- **range** operations are not supported as well.
* Expiration is supported
* Flush is supported
Expand Down
42 changes: 42 additions & 0 deletions cmake/FindCyrusSASL.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# - Find Cyrus SASL (sasl.h, libsasl2.so)
#
# This module defines
# CYRUS_SASL_INCLUDE_DIR, directory containing headers
# CYRUS_SASL_SHARED_LIB, path to Cyrus SASL's shared library
# CYRUS_SASL_FOUND, whether Cyrus SASL and its plugins have been found
#
# N.B: we do _not_ include sasl in thirdparty, for a fairly subtle reason. The
# TLDR version is that newer versions of cyrus-sasl (>=2.1.26) have a bug fix
# for https://bugzilla.cyrusimap.org/show_bug.cgi?id=3590, but that bug fix
# relied on a change both on the plugin side and on the library side. If you
# then try to run the new version of sasl (e.g from our thirdparty tree) with
# an older version of a plugin (eg from RHEL6 install), you'll get a SASL_NOMECH
# error due to this bug.
#
# In practice, Cyrus-SASL is so commonly used and generally non-ABI-breaking that
# we should be OK to depend on the host installation.


find_path(CYRUS_SASL_INCLUDE_DIR sasl/sasl.h)
find_library(CYRUS_SASL_SHARED_LIB sasl2)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CYRUS_SASL REQUIRED_VARS
CYRUS_SASL_SHARED_LIB CYRUS_SASL_INCLUDE_DIR)
3 changes: 2 additions & 1 deletion debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ Maintainer: Eugene Blikh <bigbes@tarantool.org>
Build-Depends: debhelper (>= 9), cdbs,
cmake (>= 2.8),
tarantool-dev (>= 1.6.8.0),
libsmall-dev, libmsgpuck-dev
libsmall-dev, libmsgpuck-dev,
libsasl2-dev
Standards-Version: 3.9.6
Homepage: https://github.com/tarantool/memcached
Vcs-Git: git://github.com/tarantool/memcached.git
Expand Down
5 changes: 5 additions & 0 deletions memcached/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E touch_nocreate
# ${CMAKE_SOURCE_DIR}/memcached/internal/proto_txt_parser.c
# PROPERTIES HEADER_FILE_ONLY true)

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated")

add_library(internalso SHARED
"internal/constants.c"
"internal/utils.c"
Expand All @@ -27,6 +30,7 @@ add_library(internalso SHARED
"internal/memcached_layer.c"
"internal/expiration.c"
"internal/memcached.c"
"internal/mc_sasl.c"
)

set_property(DIRECTORY PROPERTY CLEAN_NO_CUSTOM true)
Expand All @@ -38,6 +42,7 @@ if(NOT SMALL_FOUND)
endif()

target_link_libraries(internalso small)
target_link_libraries(internalso sasl2)

set_target_properties(internalso
PROPERTIES
Expand Down
67 changes: 43 additions & 24 deletions memcached/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,21 @@ enum memcached_options {
MEMCACHED_OPT_FLUSH_ENABLED = 0x04,
MEMCACHED_OPT_VERBOSITY = 0x05,
MEMCACHED_OPT_PROTOCOL = 0x06,
MEMCACHED_OPT_SASL = 0x07,
MEMCACHED_OPT_MAX
};

struct memcached_stat *memcached_get_stat (struct memcached_service *);
struct memcached_stat *
memcached_get_stat (struct memcached_service *);

struct memcached_service *memcached_create(const char *, uint32_t);
int memcached_start (struct memcached_service *);
void memcached_stop (struct memcached_service *);
void memcached_free (struct memcached_service *);
struct memcached_service *
memcached_create(const char *, uint32_t);

void
memcached_handler(struct memcached_service *p, int fd);
int memcached_start (struct memcached_service *);
void memcached_stop (struct memcached_service *);
void memcached_free (struct memcached_service *);

void memcached_handler(struct memcached_service *p, int fd);
]]

function startswith(str, start)
Expand Down Expand Up @@ -156,6 +159,12 @@ local typetable = {
end,
[[protocol type ('negotiation'/'binary'/'ascii')]]
},
sasl = {
'boolean',
function() return false end,
function(x) return x end,
[[enable SASL authorization]]
},
storage = {
'string',
function() return 'memory' end,
Expand Down Expand Up @@ -239,10 +248,11 @@ local conf_table = {
expire_items_per_iter = ffi.C.MEMCACHED_OPT_EXPIRE_COUNT,
expire_full_scan_time = ffi.C.MEMCACHED_OPT_EXPIRE_TIME,
verbosity = ffi.C.MEMCACHED_OPT_VERBOSITY,
protocol = ffi.C.MEMCACHED_OPT_PROTOCOL
protocol = ffi.C.MEMCACHED_OPT_PROTOCOL,
sasl = ffi.C.MEMCACHED_OPT_SASL
}

local memcached_mt = {
local memcached_methods = {
cfg = function (self, opts)
if type(opts) ~= 'table' then
error(err_bad_args)
Expand Down Expand Up @@ -270,10 +280,8 @@ local memcached_mt = {
end
ffi.C.memcached_start(self.service)
local parsed = uri.parse(self.uri)
self.listener = socket.tcp_server(
parsed.host,
parsed.service, {
handler = memcached_handler
self.listener = socket.tcp_server(parsed.host, parsed.service, {
handler = memcached_handler
})
if self.listener == nil then
self.status = ERRORED
Expand All @@ -291,6 +299,7 @@ local memcached_mt = {
end
local rc = ffi.C.memcached_stop(self.service)
self.status = STOPPED
return self
end,
info = function (self)
stats = ffi.C.memcached_get_stat(self.service)
Expand All @@ -299,6 +308,10 @@ local memcached_mt = {
retval[v] = stats[0][v]
end
return retval
end,
grant = function (self, username)
box.schema.user.grant(username, 'read,write', 'space', self.space_name)
return self
end
}

Expand All @@ -322,12 +335,12 @@ local function memcached_init(name, uri, opts)
instance.space = box.schema.create_space(instance.space_name, {
engine = storage,
format = {
{ name = 'key', type = 'str' },
{ name = 'expire', type = 'num' },
{ name = 'key', type = 'str' },
{ name = 'expire', type = 'num' },
{ name = 'creation', type = 'num' },
{ name = 'value', type = 'str' },
{ name = 'cas', type = 'num' },
{ name = 'flags', type = 'num' },
{ name = 'value', type = 'str' },
{ name = 'cas', type = 'num' },
{ name = 'flags', type = 'num' },
}
})
instance.space:create_index('primary', {
Expand All @@ -342,14 +355,20 @@ local function memcached_init(name, uri, opts)
error(fmt(err_enomem, "memcached service"))
end
instance.service = ffi.gc(service, ffi.C.memcached_free)
memcached_services[instance.name] = setmetatable(instance,
{ __index = memcached_mt }
)
memcached_services[instance.name] = setmetatable(instance, {
__index = memcached_methods
})
return instance:cfg(opts):start()
end

local function memcached_get(name)
return memcached_services[name]
end

return {
create = memcached_init;
get = function (name) return memcached_services[name] end;
debug = memcached_services;
create = memcached_init,
get = memcached_get,
server = setmetatable({}, {
__index = memcached_services
})
}
6 changes: 6 additions & 0 deletions memcached/internal/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,12 @@ struct memcached_response_record {
/* Not presented in vanilla memcached */ \
/* 0x86 */ _(MEMCACHED_RES_EAGAIN, 2, "Temporary unavailable")

enum memcached_authentication_state {
MEMCACHED_AUTH_NOT = 0x00,
MEMCACHED_AUTH_STEP = 0x01,
MEMCACHED_AUTH_OK = 0x02
};

ENUM0(memcached_response, RESPONSE_CODES);

ENUM0_TXT(memcached_txt_cmd, TEXT_COMMANDS);
Expand Down
18 changes: 15 additions & 3 deletions memcached/internal/expiration.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ memcached_expire_process(struct memcached_service *p, box_iterator_t **iterp)
return -1;
} else if (tpl == NULL) {
box_iterator_free(iter);
box_txn_commit();
if (box_txn_commit() == -1) {
return -1;
}
*iterp = NULL;
return 0;
} else if (is_expired_tuple(p, tpl)) {
Expand All @@ -47,7 +49,9 @@ memcached_expire_process(struct memcached_service *p, box_iterator_t **iterp)
p->stat.evictions++;
}
}
box_txn_commit();
if (box_txn_commit() == -1) {
return -1;
}
return 0;
}

Expand All @@ -71,6 +75,13 @@ memcached_expire_loop(va_list ap)
goto finish;
}
rv = memcached_expire_process(p, &iter);
if (rv == -1) {
const box_error_t *err = box_error_last();
say_error("Unexpected error %u: %s",
box_error_code(err),
box_error_message(err));
goto finish;
}

/* This part is where we rest after all deletes */
double delay = ((double )p->expire_count * p->expire_time) /
Expand All @@ -84,7 +95,8 @@ memcached_expire_loop(va_list ap)

goto restart;
finish:
if (iter) box_iterator_free(iter);
if (iter)
box_iterator_free(iter);
return 0;
}

Expand Down
Loading

0 comments on commit c49af59

Please sign in to comment.