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 SSL Support #34

Merged
merged 4 commits into from
Apr 28, 2024
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
8 changes: 8 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ services:
memcache:
container_name: memcache_1
image: memcached:alpine
memcache_ssl:
container_name: memcache_ssl
image: memcached:alpine
command: memcached -p 31211 --enable-ssl -o ssl_chain_cert=/etc/localcerts/memcache.cert,ssl_key=/etc/localcerts/memcache.key -vvv
volumes:
- "./docker/localcerts:/etc/localcerts"
ports:
- "31211:31211"
memcache_sasl:
container_name: memcache_sasl
build:
Expand Down
1 change: 1 addition & 0 deletions docker/localcerts/generate
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
openssl req -nodes -new -x509 -keyout memcache.key -out memcache.cert
21 changes: 21 additions & 0 deletions docker/localcerts/memcache.cert
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUaan2Xz9NUXbEkyRRolUCziiNosUwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA0MjgxMTAyMzdaFw0yNDA1
MjgxMTAyMzdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQCYgBV5xGu39YB7NpnqXoBLHnSQeUwqunPsF6Hapce4
/vYGrTo3izr4nc/Z6S3FwZMQwbjlDZlvrvkuN7dMxJ2o1zFBaQyGYqflgvnAwtZx
HgmcRxKxS3L9lCr0AIMo9ewDpZjNC+7AlahDrI919w815lPeJI9crcXakzL5wtVS
Dz8ZfIdjZDe8zX3rL3jEzVmlMBTJsry6rXWxkTCgqF7RCreRpe356SJR0InmAT/H
jDIFLcMapkSdkfDu+ZCWba4zbYh5XJC7+6r9mQcFfqugfHgEaU9mLkLDI7qBviW2
3B0vtakPO+IgdKseoJ5PpmMx7jm2qMxJ+mebmpP5gUqlAgMBAAGjUzBRMB0GA1Ud
DgQWBBRN39qS+PA8aPHP7c2mZpfh4YJ+rTAfBgNVHSMEGDAWgBRN39qS+PA8aPHP
7c2mZpfh4YJ+rTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAd
+T2tg5ZGtSZACvNE0TCK7P6cAVPWiQ5Np0rsKWoPVuHChpYOhXI+5OY2DEJurvN1
R2jBdS6aVDpbmgRQhwDSULJCp1M5UhUD1xeWbvef4v8miZ0EXWlymzmjujyfamN6
FGpVy23VA2owg5D80zcuFLqyh9mIKve4Pn5Ci/IfMCJwhCVYXXSUpLZGQn/jJjnv
XYCozSyNeSir63gPdaLpZOh/fTCSwsNqpSrqXfQZ3t1jI/wS1lEv9xnZVOR9Hs1m
Jt1WEp8VLAqEDFrfQhE2jwZXku0AQx1Q55bg4qT4WwrmOlsuKPUfUVgfuYdAsEPW
Nnd+Hmoe5W8uwqAYTbiQ
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions docker/localcerts/memcache.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCYgBV5xGu39YB7
NpnqXoBLHnSQeUwqunPsF6Hapce4/vYGrTo3izr4nc/Z6S3FwZMQwbjlDZlvrvku
N7dMxJ2o1zFBaQyGYqflgvnAwtZxHgmcRxKxS3L9lCr0AIMo9ewDpZjNC+7AlahD
rI919w815lPeJI9crcXakzL5wtVSDz8ZfIdjZDe8zX3rL3jEzVmlMBTJsry6rXWx
kTCgqF7RCreRpe356SJR0InmAT/HjDIFLcMapkSdkfDu+ZCWba4zbYh5XJC7+6r9
mQcFfqugfHgEaU9mLkLDI7qBviW23B0vtakPO+IgdKseoJ5PpmMx7jm2qMxJ+meb
mpP5gUqlAgMBAAECggEADTnuouT6QrY0MnuF1eHkVp+JlI4woPQS4cMa8o+2RsE2
LVYQ2ohk0PM+97NVmPU1ABsNIjCPwjP2wJ9scmgLFH21c+rcfztHPIyjhKhhWOY2
ByvS3zWf/4Y+Q3NX7NGHarmOCxqh1mokcdohhyWqWA5l77pUBMG9izuBDFBtl/RA
R6SBw6NNYNPpd+5LaooGN/4opHOPvc6/2fAEqUZ0Kc5VZQvUbK+GWNekT80aGhTw
ntTJH2y7JUbztMg1wc1+vK547s1cEY36AJzxUKFvY5sXCWK5m6jfVBfrMkUPt2Ih
+O6uR+yCZUvUW/m2HC3+6ssQH/KSIEhP/9XLkA5k8QKBgQDK40E1d6nIrf+uuAK4
B0kNmeTAsX7FLOCUUTwiSEBLOxHxgSIexSRGaHJLfBTLSKU3ZulnsLn9umo6Bwda
j3b7vkroTicLULfpIyecIEItMjE8zKssrbrIQVgX2NZ2JaCajc6ft9blBLvNX1V3
une/s/SAK6uyTpZaj9A/gCBxZwKBgQDAbBAE/PmfVV3UbnTYH3TFBG5uGXR0KM0q
fPuVCvrWgGu/m1vtzyn4LerkiqXtxtPCx9HH24RkV57uxBle1OoP3CEcmS4viLfM
AWdsFe804Xs2sUSTkfdlvube6b/IOn6ln+Qwf3F+vQpm2pV18iMmP7uevpJbP+uD
wu1OoDQgEwKBgFOQcH5GMC8Qw5vB/JSxo3wvo5FeSfmKd2F/aAYyvhB+XvCaoGQO
DNfXEyRWEfZ8kFgWUhI8Lw77Zd9Kn9PD/VzIzu+JqPk3INisexcKDlexMah6IBLB
54mtqEwCKqr06oZd2/na7W7cmUGIf5pVuhbCVvUnh4tkZPcd/sb4zeQHAoGAMCVZ
xMW/d7yWY6bXmZQVVY6MSwQXfVEQNMdc86fxp2Yuj5zB3bjuGWjz0wdWCS6soZn5
xXYJpEnAPKHYbz/66Mj9h/hT9BTTWDoptIOwfJzGhaLJM9tb+ZtQjQoTsHfiZHKj
V/crYLjQN3gN2ZsibFmaClhIqohN14QYeInhQP0CgYBPetiwRJa9J6BFGQ2nnm3z
nAy7vjHtSpAQjAU2FrlYxIW/f5O9zE8DDA7TsOxp1QDHaqOjv5gUG3jN3G6cBqNK
VuKex3NndfcMgc6EiSXUViERZ5uM7t2navJDc8dTZZNBFABlwAirZgDXaEenKJrc
gDBVM1smjLvHxFcz8Ni3Lg==
-----END PRIVATE KEY-----
32 changes: 19 additions & 13 deletions lib/memcache/connection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defmodule Memcache.Connection do
alias Memcache.Protocol
alias Memcache.Receiver
alias Memcache.Utils
alias Memcache.Transport

defmodule State do
@moduledoc false
Expand All @@ -17,7 +18,8 @@ defmodule Memcache.Connection do
backoff_current: nil,
receiver: nil,
receiver_queue: nil,
server: nil
server: nil,
transport: nil
end

@doc """
Expand Down Expand Up @@ -55,6 +57,8 @@ defmodule Memcache.Connection do
supported. It is specified using the following format `{:plain,
"username", "password"}`. Defaults to `nil`.

* `:ssl_options` - (keyword) SSL options, see `https://www.erlang.org/doc/man/ssl#type-tls_client_option`.

## Examples

{:ok, pid} = Memcache.Connection.start_link()
Expand All @@ -70,8 +74,9 @@ defmodule Memcache.Connection do
backoff_initial: 500,
backoff_max: 30_000,
connect_timeout: :infinity,
hostname: 'localhost',
port: 11_211
hostname: ~c"localhost",
port: 11_211,
transport: :gen_tcp
]

defp with_defaults(opts) do
Expand Down Expand Up @@ -173,7 +178,8 @@ defmodule Memcache.Connection do
end

def init(opts) do
{:connect, :init, %State{opts: opts, server: Utils.format_host(opts)}}
transport = if Keyword.get(opts, :ssl, false), do: :ssl, else: :gen_tcp
{:connect, :init, %State{opts: opts, server: Utils.format_host(opts), transport: transport}}
end

def connect(info, %State{opts: opts, server: server} = s) do
Expand Down Expand Up @@ -314,7 +320,7 @@ defmodule Memcache.Connection do

def cleanup(%State{sock: sock, receiver: receiver, receiver_queue: receiver_queue} = s) do
if sock do
:ok = :gen_tcp.close(sock)
:ok = Transport.close(sock)
end

if receiver do
Expand All @@ -340,7 +346,7 @@ defmodule Memcache.Connection do

defp maybe_activate_sock(state) do
if Enum.empty?(state.receiver_queue) do
case :inet.setopts(state.sock, active: :once) do
case Transport.setopts(state.sock, active: :once) do
:ok -> {:noreply, state}
error -> {:disconnect, error, state}
end
Expand All @@ -351,7 +357,7 @@ defmodule Memcache.Connection do

defp maybe_deactivate_sock(state) do
if Enum.empty?(state.receiver_queue) do
case :inet.setopts(state.sock, active: false) do
case Transport.setopts(state.sock, active: false) do
:ok -> :ok
error -> {:disconnect, error, error_response(state), state}
end
Expand All @@ -363,7 +369,7 @@ defmodule Memcache.Connection do
defp send_and_receive(%State{sock: sock} = s, from, command, args, opts) do
packet = serialize(command, args)

case :gen_tcp.send(sock, packet) do
case Transport.send(sock, packet) do
:ok ->
s = enqueue_receiver(s, from)
:ok = Receiver.read(s.receiver, from, command, opts)
Expand All @@ -378,7 +384,7 @@ defmodule Memcache.Connection do
{packet, commands, i} = Enum.reduce(commands, {[], [], 1}, &accumulate_commands/2)
packet = [packet | serialize(:NOOP, [], i)]

case :gen_tcp.send(sock, packet) do
case Transport.send(sock, packet) do
:ok ->
s = enqueue_receiver(s, from)
:ok = Receiver.read_quiet(s.receiver, from, Enum.reverse([{i, :NOOP, [], []} | commands]))
Expand Down Expand Up @@ -413,16 +419,16 @@ defmodule Memcache.Connection do
end

defp connect_and_authenticate(host, port, sock_opts, timeout, state) do
case :gen_tcp.connect(host, port, sock_opts, timeout) do
case Transport.connect(state.transport, host, port, sock_opts, timeout, state.opts) do
{:ok, sock} ->
with {:ok} <- authenticate(sock, state.opts),
# Make sure the socket is usable
{:ok, _} <- execute_command(sock, :NOOP, []),
:ok <- :inet.setopts(sock, active: :once) do
:ok <- Transport.setopts(sock, active: :once) do
{:ok, sock}
else
error ->
:gen_tcp.close(sock)
Transport.close(sock)
error
end

Expand All @@ -442,7 +448,7 @@ defmodule Memcache.Connection do
defp execute_command(sock, command, args) do
packet = serialize(command, args)

case :gen_tcp.send(sock, packet) do
case Transport.send(sock, packet) do
:ok -> recv_response(sock, command)
error -> error
end
Expand Down
3 changes: 2 additions & 1 deletion lib/memcache/receiver.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ defmodule Memcache.Receiver do
@moduledoc false
use GenServer
alias Memcache.Protocol
alias Memcache.Transport

defmodule State do
@moduledoc false
Expand Down Expand Up @@ -161,7 +162,7 @@ defmodule Memcache.Receiver do
end

defp read(sock, buffer, min_required) do
case :gen_tcp.recv(sock, 0) do
case Transport.recv(sock, 0) do
{:ok, data} -> read(sock, buffer <> data, min_required)
{:error, reason} -> {:error, reason}
end
Expand Down
37 changes: 37 additions & 0 deletions lib/memcache/transport.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
defmodule Memcache.Transport do
defstruct [:transport, :sock]

def connect(transport, host, port, sock_opts, timeout, opts)
when transport in [:gen_tcp, :ssl] do
ssl_opts = Keyword.get(opts, :ssl_options, [])
sock_opts = sock_opts ++ ssl_opts

case transport.connect(host, port, sock_opts, timeout) do
{:ok, sock} ->
{:ok, %__MODULE__{transport: transport, sock: sock}}

error ->
error
end
end

def setopts(%{transport: :gen_tcp, sock: sock}, opts) do
:inet.setopts(sock, opts)
end

def setopts(%{transport: :ssl, sock: sock}, opts) do
:ssl.setopts(sock, opts)
end

def send(%{transport: transport, sock: sock}, data) do
transport.send(sock, data)
end

def recv(%{transport: transport, sock: sock}, length) do
transport.recv(sock, length)
end

def close(%{transport: transport, sock: sock}) do
transport.close(sock)
end
end
2 changes: 1 addition & 1 deletion lib/memcache/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ defmodule Memcache.Utils do

def format_error(reason) do
case :inet.format_error(reason) do
'unknown POSIX error' -> inspect(reason)
~c"unknown POSIX error" -> inspect(reason)
message -> List.to_string(message)
end
end
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ defmodule Memcache.Mixfile do

def application do
[
extra_applications: [:logger],
extra_applications: [:logger, :inets, :ssl],
mod: {Memcache.Application, []}
]
end
Expand Down
Loading
Loading