Skip to content

Commit dad09e6

Browse files
Merge pull request #11989 from rabbitmq/mk-encrypted-values-in-rabbitmq-conf
Make it possible to specify encrypted values in rabbitmq conf
2 parents 267d7b8 + 8b90d4a commit dad09e6

File tree

19 files changed

+584
-28
lines changed

19 files changed

+584
-28
lines changed

deps/rabbit/priv/schema/rabbit.schema

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,12 @@ end}.
229229
[{datatype, {enum, [true, false]}}]}.
230230

231231
{mapping, "definitions.tls.password", "rabbit.definitions.ssl_options.password",
232-
[{datatype, string}]}.
232+
[{datatype, [tagged_binary, binary]}]}.
233+
234+
{translation, "rabbit.definitions.ssl_options.password",
235+
fun(Conf) ->
236+
rabbit_cuttlefish:optionally_tagged_string("definitions.tls.password", Conf)
237+
end}.
233238

234239
{mapping, "definitions.tls.secure_renegotiate", "rabbit.definitions.ssl_options.secure_renegotiate",
235240
[{datatype, {enum, [true, false]}}]}.
@@ -395,7 +400,12 @@ end}.
395400
[{datatype, {enum, [true, false]}}]}.
396401

397402
{mapping, "ssl_options.password", "rabbit.ssl_options.password",
398-
[{datatype, string}]}.
403+
[{datatype, [tagged_binary, binary]}]}.
404+
405+
{translation, "rabbit.ssl_options.password",
406+
fun(Conf) ->
407+
rabbit_cuttlefish:optionally_tagged_binary("ssl_options.password", Conf)
408+
end}.
399409

400410
{mapping, "ssl_options.psk_identity", "rabbit.ssl_options.psk_identity",
401411
[{datatype, string}]}.
@@ -656,12 +666,12 @@ fun(Conf) ->
656666
end}.
657667

658668
{mapping, "default_pass", "rabbit.default_pass", [
659-
{datatype, string}
669+
{datatype, [tagged_binary, binary]}
660670
]}.
661671

662672
{translation, "rabbit.default_pass",
663673
fun(Conf) ->
664-
list_to_binary(cuttlefish:conf_get("default_pass", Conf))
674+
rabbit_cuttlefish:optionally_tagged_binary("default_pass", Conf)
665675
end}.
666676

667677
{mapping, "default_permissions.configure", "rabbit.default_permissions", [
@@ -696,7 +706,7 @@ end}.
696706
]}.
697707

698708
{mapping, "default_users.$name.password", "rabbit.default_users", [
699-
{datatype, string}
709+
{datatype, [tagged_binary, binary]}
700710
]}.
701711

702712
{mapping, "default_users.$name.configure", "rabbit.default_users", [

deps/rabbit/src/rabbit_cuttlefish.erl

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99

1010
-export([
1111
aggregate_props/2,
12-
aggregate_props/3
12+
aggregate_props/3,
13+
14+
optionally_tagged_binary/2,
15+
optionally_tagged_string/2
1316
]).
1417

1518
-type keyed_props() :: [{binary(), [{binary(), any()}]}].
@@ -41,3 +44,25 @@ aggregate_props(Conf, Prefix, KeyFun) ->
4144
FlatList
4245
)
4346
).
47+
48+
optionally_tagged_binary(Key, Conf) ->
49+
case cuttlefish:conf_get(Key, Conf) of
50+
undefined -> cuttlefish:unset();
51+
{encrypted, Bin} when is_binary(Bin) -> {encrypted, Bin};
52+
{_, Bin} when is_binary(Bin) -> {encrypted, Bin};
53+
{encrypted, Str} when is_list(Str) -> {encrypted, list_to_binary(Str)};
54+
{_, Str} when is_list(Str) -> {encrypted, list_to_binary(Str)};
55+
Bin when is_binary(Bin) -> Bin;
56+
Str when is_list(Str) -> list_to_binary(Str)
57+
end.
58+
59+
optionally_tagged_string(Key, Conf) ->
60+
case cuttlefish:conf_get(Key, Conf) of
61+
undefined -> cuttlefish:unset();
62+
{encrypted, Str} when is_list(Str) -> {encrypted, Str};
63+
{_, Str} when is_list(Str) -> {encrypted, Str};
64+
{encrypted, Bin} when is_binary(Bin) -> {encrypted, binary_to_list(Bin)};
65+
{_, Bin} when is_binary(Bin) -> {encrypted, binary_to_list(Bin)};
66+
Str when is_list(Str) -> Str;
67+
Bin when is_binary(Bin) -> binary_to_list(Bin)
68+
end.

deps/rabbit/test/config_schema_SUITE_data/rabbit.snippets

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ ssl_options.fail_if_no_peer_cert = true",
182182
[{rabbit, [{default_users, [
183183
{<<"a">>, [{<<"vhost_pattern">>, "banana"},
184184
{<<"tags">>, [administrator, operator]},
185-
{<<"password">>, "SECRET"},
185+
{<<"password">>, <<"SECRET">>},
186186
{<<"read">>, ".*"}]}]}]}],
187187
[]},
188188

@@ -510,7 +510,7 @@ tcp_listen_options.exit_on_close = false",
510510
[{cacertfile,"test/config_schema_SUITE_data/certs/cacert.pem"},
511511
{certfile,"test/config_schema_SUITE_data/certs/cert.pem"},
512512
{keyfile,"test/config_schema_SUITE_data/certs/key.pem"},
513-
{password,"t0p$3kRe7"}]}]}],
513+
{password,<<"t0p$3kRe7">>}]}]}],
514514
[]},
515515
{ssl_options_tls_ver_old,
516516
"listeners.ssl.1 = 5671

deps/rabbitmq_auth_backend_http/priv/schema/rabbitmq_auth_backend_http.schema

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ end}.
116116
[{datatype, {enum, [true, false]}}]}.
117117

118118
{mapping, "auth_http.ssl_options.password", "rabbitmq_auth_backend_http.ssl_options.password",
119-
[{datatype, string}]}.
119+
[{datatype, [tagged_binary, binary]}]}.
120120

121121
{mapping, "auth_http.ssl_options.psk_identity", "rabbitmq_auth_backend_http.ssl_options.psk_identity",
122122
[{datatype, string}]}.

deps/rabbitmq_auth_backend_http/test/config_schema_SUITE_data/rabbitmq_auth_backend_http.snippets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
[{cacertfile,"test/config_schema_SUITE_data/certs/invalid_cacert.pem"},
7979
{certfile,"test/config_schema_SUITE_data/certs/invalid_cert.pem"},
8080
{keyfile,"test/config_schema_SUITE_data/certs/invalid_key.pem"},
81-
{password,"t0p$3kRe7"}]}]}],
81+
{password,<<"t0p$3kRe7">>}]}]}],
8282
[]},
8383
{ssl_options_tls_versions,
8484
"auth_http.ssl_options.cacertfile = test/config_schema_SUITE_data/certs/invalid_cacert.pem

deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/decode_command.ex

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.DecodeCommand do
8686
{:ok, result}
8787
catch
8888
_, _ ->
89+
IO.inspect(__STACKTRACE__)
8990
{:error,
9091
"Failed to decrypt the value. Things to check: is the passphrase correct? Are the cipher and hash algorithms the same as those used for encryption?"}
9192
end
@@ -109,6 +110,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.DecodeCommand do
109110
{:ok, result}
110111
catch
111112
_, _ ->
113+
IO.inspect(__STACKTRACE__)
112114
{:error,
113115
"Failed to decrypt the value. Things to check: is the passphrase correct? Are the cipher and hash algorithms the same as those used for encryption?"}
114116
end
@@ -117,15 +119,15 @@ defmodule RabbitMQ.CLI.Ctl.Commands.DecodeCommand do
117119
def formatter(), do: RabbitMQ.CLI.Formatters.Erlang
118120

119121
def banner(_, _) do
120-
"Decrypting value..."
122+
"Decrypting an advanced.config (Erlang term) value..."
121123
end
122124

123125
def usage,
124126
do: "decode value passphrase [--cipher <cipher>] [--hash <hash>] [--iterations <iterations>]"
125127

126128
def usage_additional() do
127129
[
128-
["<value>", "config value to decode"],
130+
["<value>", "advanced.config (Erlang term) value to decode"],
129131
["<passphrase>", "passphrase to use with the config value encryption key"],
130132
["--cipher <cipher>", "cipher suite to use"],
131133
["--hash <hash>", "hashing function to use"],
@@ -141,7 +143,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.DecodeCommand do
141143

142144
def help_section(), do: :configuration
143145

144-
def description(), do: "Decrypts an encrypted configuration value"
146+
def description(), do: "Decrypts an encrypted advanced.config value"
145147

146148
#
147149
# Implementation
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
## This Source Code Form is subject to the terms of the Mozilla Public
2+
## License, v. 2.0. If a copy of the MPL was not distributed with this
3+
## file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
##
5+
## Copyright (c) 2007-2023 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
6+
7+
alias RabbitMQ.CLI.Core.Helpers
8+
9+
defmodule RabbitMQ.CLI.Ctl.Commands.DecryptConfValueCommand do
10+
alias RabbitMQ.CLI.Core.{DocGuide, Input}
11+
12+
@behaviour RabbitMQ.CLI.CommandBehaviour
13+
use RabbitMQ.CLI.DefaultOutput
14+
15+
def switches() do
16+
[
17+
cipher: :string,
18+
hash: :string,
19+
iterations: :integer
20+
]
21+
end
22+
23+
@atomized_keys [:cipher, :hash]
24+
@prefix "encrypted:"
25+
26+
def distribution(_), do: :none
27+
28+
def merge_defaults(args, opts) do
29+
with_defaults =
30+
Map.merge(
31+
%{
32+
cipher: :rabbit_pbe.default_cipher(),
33+
hash: :rabbit_pbe.default_hash(),
34+
iterations: :rabbit_pbe.default_iterations()
35+
},
36+
opts
37+
)
38+
39+
{args, Helpers.atomize_values(with_defaults, @atomized_keys)}
40+
end
41+
42+
def validate(args, _) when length(args) < 1 do
43+
{:validation_failure, {:not_enough_args, "Please provide a value to decode and a passphrase"}}
44+
end
45+
46+
def validate(args, _) when length(args) > 2 do
47+
{:validation_failure, :too_many_args}
48+
end
49+
50+
def validate(_args, opts) do
51+
case {supports_cipher(opts.cipher), supports_hash(opts.hash), opts.iterations > 0} do
52+
{false, _, _} ->
53+
{:validation_failure, {:bad_argument, "The requested cipher is not supported"}}
54+
55+
{_, false, _} ->
56+
{:validation_failure, {:bad_argument, "The requested hash is not supported"}}
57+
58+
{_, _, false} ->
59+
{:validation_failure,
60+
{:bad_argument,
61+
"The requested number of iterations is incorrect (must be a positive integer)"}}
62+
63+
{true, true, true} ->
64+
:ok
65+
end
66+
end
67+
68+
def run([value], %{cipher: cipher, hash: hash, iterations: iterations} = opts) do
69+
case Input.consume_single_line_string_with_prompt("Passphrase: ", opts) do
70+
:eof ->
71+
{:error, :not_enough_args}
72+
73+
passphrase ->
74+
try do
75+
term_value = Helpers.evaluate_input_as_term(value)
76+
77+
term_to_decrypt =
78+
case term_value do
79+
prefixed_val when is_bitstring(prefixed_val) or is_list(prefixed_val) ->
80+
tag_input_value_with_encrypted(prefixed_val)
81+
82+
{:encrypted, _} = encrypted ->
83+
encrypted
84+
85+
_ ->
86+
{:encrypted, term_value}
87+
end
88+
89+
result = :rabbit_pbe.decrypt_term(cipher, hash, iterations, passphrase, term_to_decrypt)
90+
{:ok, result}
91+
catch
92+
_, _ ->
93+
IO.inspect(__STACKTRACE__)
94+
{:error,
95+
"Failed to decrypt the value. Things to check: is the passphrase correct? Are the cipher and hash algorithms the same as those used for encryption?"}
96+
end
97+
end
98+
end
99+
100+
def run([value, passphrase], %{cipher: cipher, hash: hash, iterations: iterations}) do
101+
try do
102+
term_value = Helpers.evaluate_input_as_term(value)
103+
104+
term_to_decrypt =
105+
case term_value do
106+
prefixed_val when is_bitstring(prefixed_val) or is_list(prefixed_val) ->
107+
tag_input_value_with_encrypted(prefixed_val)
108+
109+
{:encrypted, _} = encrypted ->
110+
encrypted
111+
112+
_ ->
113+
{:encrypted, term_value}
114+
end
115+
116+
result = :rabbit_pbe.decrypt_term(cipher, hash, iterations, passphrase, term_to_decrypt)
117+
{:ok, result}
118+
catch
119+
_, _ ->
120+
IO.inspect(__STACKTRACE__)
121+
{:error,
122+
"Failed to decrypt the value. Things to check: is the passphrase correct? Are the cipher and hash algorithms the same as those used for encryption?"}
123+
end
124+
end
125+
126+
def formatter(), do: RabbitMQ.CLI.Formatters.Erlang
127+
128+
def banner(_, _) do
129+
"Decrypting a rabbitmq.conf string value..."
130+
end
131+
132+
def usage,
133+
do: "decrypt_conf_value value passphrase [--cipher <cipher>] [--hash <hash>] [--iterations <iterations>]"
134+
135+
def usage_additional() do
136+
[
137+
["<value>", "a double-quoted rabbitmq.conf string value to decode"],
138+
["<passphrase>", "passphrase to use with the config value encryption key"],
139+
["--cipher <cipher>", "cipher suite to use"],
140+
["--hash <hash>", "hashing function to use"],
141+
["--iterations <iterations>", "number of iteration to apply"]
142+
]
143+
end
144+
145+
def usage_doc_guides() do
146+
[
147+
DocGuide.configuration()
148+
]
149+
end
150+
151+
def help_section(), do: :configuration
152+
153+
def description(), do: "Decrypts an encrypted configuration value"
154+
155+
#
156+
# Implementation
157+
#
158+
159+
defp supports_cipher(cipher), do: Enum.member?(:rabbit_pbe.supported_ciphers(), cipher)
160+
161+
defp supports_hash(hash), do: Enum.member?(:rabbit_pbe.supported_hashes(), hash)
162+
163+
defp tag_input_value_with_encrypted(value) when is_bitstring(value) or is_list(value) do
164+
bin_val = :rabbit_data_coercion.to_binary(value)
165+
untagged_val = String.replace_prefix(bin_val, @prefix, "")
166+
167+
{:encrypted, untagged_val}
168+
end
169+
defp tag_input_value_with_encrypted(value) do
170+
{:encrypted, value}
171+
end
172+
end

deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/encode_command.ex

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EncodeCommand do
7777
{:ok, result}
7878
catch
7979
_, _ ->
80+
IO.inspect(__STACKTRACE__)
8081
{:error, "Error during cipher operation"}
8182
end
8283
end
@@ -99,6 +100,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EncodeCommand do
99100
{:ok, result}
100101
catch
101102
_, _ ->
103+
IO.inspect(__STACKTRACE__)
102104
{:error, "Error during cipher operation"}
103105
end
104106
end
@@ -115,22 +117,23 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EncodeCommand do
115117
{:ok, result}
116118
catch
117119
_, _ ->
120+
IO.inspect(__STACKTRACE__)
118121
{:error, "Error during cipher operation"}
119122
end
120123
end
121124

122125
def formatter(), do: RabbitMQ.CLI.Formatters.Erlang
123126

124127
def banner(_, _) do
125-
"Encrypting value ..."
128+
"Encrypting value to be used in advanced.config..."
126129
end
127130

128131
def usage,
129132
do: "encode value passphrase [--cipher <cipher>] [--hash <hash>] [--iterations <iterations>]"
130133

131134
def usage_additional() do
132135
[
133-
["<value>", "config value to encode"],
136+
["<value>", "value to encode, to be used in advanced.config"],
134137
["<passphrase>", "passphrase to use with the config value encryption key"],
135138
["--cipher <cipher>", "cipher suite to use"],
136139
["--hash <hash>", "hashing function to use"],
@@ -146,7 +149,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EncodeCommand do
146149

147150
def help_section(), do: :configuration
148151

149-
def description(), do: "Encrypts a sensitive configuration value"
152+
def description(), do: "Encrypts a sensitive configuration value to be used in the advanced.config file"
150153

151154
#
152155
# Implementation

0 commit comments

Comments
 (0)