Skip to content

Commit 186bd1c

Browse files
authored
Merge pull request #26 from matt-mazzucato/support_ssl_for_rmq
Support ssl for RabbitMQ connections
2 parents 94824ec + 8e7994f commit 186bd1c

File tree

4 files changed

+151
-0
lines changed

4 files changed

+151
-0
lines changed

lib/astarte_rpc/config.ex

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,32 @@ defmodule Astarte.RPC.Config do
6868
type: :integer,
6969
default: 0
7070

71+
@envdoc "Enable SSL. If not specified, SSL is disabled."
72+
app_env :amqp_connection_ssl_enabled, :astarte_rpc, :amqp_connection_ssl_enabled,
73+
os_env: "RPC_AMQP_CONNECTION_SSL_ENABLED",
74+
type: :boolean,
75+
default: false
76+
77+
@envdoc "Disable Server Name Indication. Defaults to false."
78+
app_env :amqp_connection_ssl_disable_sni,
79+
:astarte_rpc,
80+
:amqp_connection_ssl_disable_sni,
81+
os_env: "RPC_AMQP_CONNECTION_SSL_DISABLE_SNI",
82+
type: :boolean,
83+
default: false
84+
85+
@envdoc "Specify the hostname to be used in TLS Server Name Indication extension. If not specified, the amqp host will be used. This value is used only if Server Name Indication is enabled."
86+
app_env :amqp_connection_ssl_custom_sni,
87+
:astarte_appengine_api,
88+
:amqp_connection_ssl_custom_sni,
89+
os_env: "RPC_AMQP_CONNECTION_SSL_CUSTOM_SNI",
90+
type: :binary
91+
92+
@envdoc "Specifies the certificates of the root Certificate Authorities to be trusted. When not specified, the bundled cURL certificate bundle will be used."
93+
app_env :amqp_connection_ssl_ca_file, :astarte_rpc, :amqp_connection_ssl_ca_file,
94+
os_env: "RPC_AMQP_CONNECTION_SSL_CA_FILE",
95+
type: :binary
96+
7197
@doc "The AMQP queue arguments."
7298
@type argument :: {:"x-max-length", integer()} | {:"x-overflow", String.t()}
7399
@spec amqp_queue_arguments!() :: [argument]
@@ -84,12 +110,19 @@ defmodule Astarte.RPC.Config do
84110
@doc """
85111
Returns the amqp_connection options or an empty list if they're not set.
86112
"""
113+
@type ssl_option ::
114+
{:cacertfile, String.t()}
115+
| {:verify, :verify_peer}
116+
| {:server_name_indication, :disable | charlist()}
117+
@type ssl_options :: :none | [ssl_option]
118+
87119
@type options ::
88120
{:username, String.t()}
89121
| {:password, String.t()}
90122
| {:virtual_host, String.t()}
91123
| {:host, String.t()}
92124
| {:port, integer()}
125+
| {:ssl_options, ssl_options}
93126

94127
@spec amqp_options!() :: [options]
95128
def amqp_options! do
@@ -106,5 +139,32 @@ defmodule Astarte.RPC.Config do
106139
host: host,
107140
port: port
108141
]
142+
|> populate_ssl_options()
143+
end
144+
145+
defp populate_ssl_options(options) do
146+
if amqp_connection_ssl_enabled!() do
147+
ssl_options = build_ssl_options()
148+
Keyword.put(options, :ssl_options, ssl_options)
149+
else
150+
options
151+
end
152+
end
153+
154+
defp build_ssl_options() do
155+
[
156+
cacertfile: amqp_connection_ssl_ca_file!() || CAStore.file_path(),
157+
verify: :verify_peer
158+
]
159+
|> populate_sni()
160+
end
161+
162+
defp populate_sni(ssl_options) do
163+
if amqp_connection_ssl_disable_sni!() do
164+
Keyword.put(ssl_options, :server_name_indication, :disable)
165+
else
166+
server_name = amqp_connection_ssl_custom_sni!() || amqp_connection_host!()
167+
Keyword.put(ssl_options, :server_name_indication, to_charlist(server_name))
168+
end
109169
end
110170
end

mix.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ defmodule Astarte.RPC.Mixfile do
7373
{:exprotobuf, "~> 1.2"},
7474
{:skogsra, "~> 2.2"},
7575
{:excoveralls, "~> 0.12", only: :test},
76+
{:castore, "~> 0.1.0"},
7677
{:dialyzex, github: "Comcast/dialyzex", only: [:dev, :ci]}
7778
]
7879
end

mix.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
%{
22
"amqp": {:hex, :amqp, "1.4.1", "b4ccde3cd5c097fb9ee44bc061af4468db6d3d2297a0515dea6156a49b0e116e", [:mix], [{:amqp_client, "~> 3.8.0", [hex: :amqp_client, repo: "hexpm", optional: false]}], "hexpm", "2f03042e4c295570c7cddfe5eeda49f68350e52c5fed5ca61b51e9a33a8cc00b"},
33
"amqp_client": {:hex, :amqp_client, "3.8.3", "11d4825d56e77bafb246631a5c68b5c245567f6cc6331c4ddaa3f55fa39c9e8a", [:make, :rebar3], [{:rabbit_common, "3.8.3", [hex: :rabbit_common, repo: "hexpm", optional: false]}], "hexpm", "987124a9cf2373d16e0ce8143d8b167548a532826ce80e53ea7b8d959d6a0e5e"},
4+
"castore": {:hex, :castore, "0.1.5", "591c763a637af2cc468a72f006878584bc6c306f8d111ef8ba1d4c10e0684010", [:mix], [], "hexpm", "6db356b2bc6cc22561e051ff545c20ad064af57647e436650aa24d7d06cd941a"},
45
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"},
56
"credentials_obfuscation": {:hex, :credentials_obfuscation, "1.1.0", "513793cc20c18afc9e03e584b436192a751a8344890e03a8741c65c8d6866fab", [:rebar3], [], "hexpm", "2d1bc574d129ff76309a03874c245193c6375bc766734e008888e636b250d5df"},
67
"dialyzex": {:git, "https://github.com/Comcast/dialyzex.git", "cdc7cf71fe6df0ce4cf59e3f497579697a05c989", []},
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#
2+
# This file is part of Astarte.
3+
#
4+
# Copyright 2020 Ispirata Srl
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
19+
defmodule Astarte.RPC.AMQPOptionsTest do
20+
alias Astarte.RPC.Config
21+
use ExUnit.Case
22+
23+
describe "amqp_options when ssl is enabled" do
24+
test "no ca_cert is set" do
25+
Config.put_amqp_connection_ssl_enabled(true)
26+
27+
ssl_options =
28+
Config.amqp_options!()
29+
|> Keyword.fetch!(:ssl_options)
30+
31+
assert cacertfile: CAStore.file_path() in ssl_options
32+
33+
Config.reload_amqp_connection_ssl_enabled()
34+
end
35+
36+
test "server name indication is disabled" do
37+
Config.put_amqp_connection_ssl_enabled(true)
38+
Config.put_amqp_connection_ssl_disable_sni(true)
39+
40+
ssl_options =
41+
Config.amqp_options!()
42+
|> Keyword.fetch!(:ssl_options)
43+
44+
assert server_name_indication: :disable in ssl_options
45+
46+
Config.reload_amqp_connection_ssl_enabled()
47+
Config.reload_amqp_connection_ssl_disable_sni()
48+
end
49+
50+
test "server name indication is enabled" do
51+
Config.put_amqp_connection_ssl_enabled(true)
52+
Config.put_amqp_connection_ssl_disable_sni(true)
53+
54+
ssl_options =
55+
Config.amqp_options!()
56+
|> Keyword.fetch!(:ssl_options)
57+
58+
assert server_name_indication: Config.amqp_connection_host!() in ssl_options
59+
60+
Config.reload_amqp_connection_ssl_enabled()
61+
Config.reload_amqp_connection_ssl_disable_sni()
62+
end
63+
64+
test "ca_cert is set" do
65+
ca_cert_path = "/the/path/to/ca_cert.pem"
66+
Config.put_amqp_connection_ssl_enabled(true)
67+
Config.put_amqp_connection_ssl_ca_file(ca_cert_path)
68+
69+
ssl_options =
70+
Config.amqp_options!()
71+
|> Keyword.fetch!(:ssl_options)
72+
73+
assert cacertfile: ca_cert_path in ssl_options
74+
75+
Config.reload_amqp_connection_ssl_enabled()
76+
Config.reload_amqp_connection_ssl_ca_file()
77+
end
78+
end
79+
80+
test "ca_cert is ignored when ssl is disabled" do
81+
Config.put_amqp_connection_ssl_enabled(false)
82+
83+
options = Config.amqp_options!()
84+
85+
assert Keyword.get(options, :ssl_options) == nil
86+
87+
Config.reload_amqp_connection_ssl_enabled()
88+
end
89+
end

0 commit comments

Comments
 (0)