Skip to content

Commit c2fdd73

Browse files
Secret encoding: refine CLI tools
'ctl encode' is unfortunately name and targets advanced.config commands. This introduce a command that targets 'rabbitmq.conf' values and has a more specific name. Eventually 'ctl encode' will be aliased and deprecated, although we still do not have an aliasing mechanism and it won't be in scope for 4.0.
1 parent 1c7e590 commit c2fdd73

File tree

3 files changed

+183
-2
lines changed

3 files changed

+183
-2
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EncodeCommand do
122122
def formatter(), do: RabbitMQ.CLI.Formatters.Erlang
123123

124124
def banner(_, _) do
125-
"Encrypting value ..."
125+
"Encrypting value to be used in advanced.config..."
126126
end
127127

128128
def usage,
@@ -146,7 +146,7 @@ defmodule RabbitMQ.CLI.Ctl.Commands.EncodeCommand do
146146

147147
def help_section(), do: :configuration
148148

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

151151
#
152152
# Implementation
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
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+
defmodule RabbitMQ.CLI.Ctl.Commands.EncryptConfValueCommand do
8+
alias RabbitMQ.CLI.Core.{DocGuide, Helpers, Input}
9+
10+
@behaviour RabbitMQ.CLI.CommandBehaviour
11+
use RabbitMQ.CLI.DefaultOutput
12+
13+
def switches() do
14+
[
15+
cipher: :string,
16+
hash: :string,
17+
iterations: :integer
18+
]
19+
end
20+
21+
@atomized_keys [:cipher, :hash]
22+
23+
def distribution(_), do: :none
24+
25+
def merge_defaults(args, opts) do
26+
with_defaults =
27+
Map.merge(
28+
%{
29+
cipher: :rabbit_pbe.default_cipher(),
30+
hash: :rabbit_pbe.default_hash(),
31+
iterations: :rabbit_pbe.default_iterations()
32+
},
33+
opts
34+
)
35+
36+
{args, Helpers.atomize_values(with_defaults, @atomized_keys)}
37+
end
38+
39+
def validate(args, _) when length(args) > 2 do
40+
{:validation_failure, :too_many_args}
41+
end
42+
43+
def validate(_args, opts) do
44+
case {supports_cipher(opts.cipher), supports_hash(opts.hash), opts.iterations > 0} do
45+
{false, _, _} ->
46+
{:validation_failure, {:bad_argument, "The requested cipher is not supported."}}
47+
48+
{_, false, _} ->
49+
{:validation_failure, {:bad_argument, "The requested hash is not supported"}}
50+
51+
{_, _, false} ->
52+
{:validation_failure, {:bad_argument, "The requested number of iterations is incorrect"}}
53+
54+
{true, true, true} ->
55+
:ok
56+
end
57+
end
58+
59+
def run([], %{cipher: cipher, hash: hash, iterations: iterations} = opts) do
60+
case Input.consume_single_line_string_with_prompt("Value to encode: ", opts) do
61+
:eof ->
62+
{:error, :not_enough_args}
63+
64+
value ->
65+
case Input.consume_single_line_string_with_prompt("Passphrase: ", opts) do
66+
:eof ->
67+
{:error, :not_enough_args}
68+
69+
passphrase ->
70+
try do
71+
term_value = Helpers.evaluate_input_as_term(value)
72+
73+
{:encrypted, result} =
74+
:rabbit_pbe.encrypt_term(cipher, hash, iterations, passphrase, term_value)
75+
76+
{:ok, result}
77+
catch
78+
_, _ ->
79+
{:error, "Error during cipher operation"}
80+
end
81+
end
82+
end
83+
end
84+
85+
def run([value], %{cipher: cipher, hash: hash, iterations: iterations} = opts) do
86+
case Input.consume_single_line_string_with_prompt("Passphrase: ", opts) do
87+
:eof ->
88+
{:error, :not_enough_args}
89+
90+
passphrase ->
91+
try do
92+
term_value = Helpers.evaluate_input_as_term(value)
93+
94+
{:encrypted, result} =
95+
:rabbit_pbe.encrypt_term(cipher, hash, iterations, passphrase, term_value)
96+
97+
{:ok, result}
98+
catch
99+
_, _ ->
100+
{:error, "Error during cipher operation"}
101+
end
102+
end
103+
end
104+
105+
def run([value, passphrase], %{cipher: cipher, hash: hash, iterations: iterations}) do
106+
try do
107+
term_value = Helpers.evaluate_input_as_term(value)
108+
109+
{:encrypted, result} =
110+
:rabbit_pbe.encrypt_term(cipher, hash, iterations, passphrase, term_value)
111+
112+
{:ok, result}
113+
catch
114+
_, _ ->
115+
{:error, "Error during cipher operation"}
116+
end
117+
end
118+
119+
def formatter(), do: RabbitMQ.CLI.Formatters.EncryptedConfValue
120+
121+
def banner(_, _) do
122+
"Encrypting value to be used in rabbitmq.conf..."
123+
end
124+
125+
def usage,
126+
do: "encrypt_conf_value value passphrase [--cipher <cipher>] [--hash <hash>] [--iterations <iterations>]"
127+
128+
def usage_additional() do
129+
[
130+
["<value>", "config value to encode"],
131+
["<passphrase>", "passphrase to use with the config value encryption key"],
132+
["--cipher <cipher>", "cipher suite to use"],
133+
["--hash <hash>", "hashing function to use"],
134+
["--iterations <iterations>", "number of iteration to apply"]
135+
]
136+
end
137+
138+
def usage_doc_guides() do
139+
[
140+
DocGuide.configuration()
141+
]
142+
end
143+
144+
def help_section(), do: :configuration
145+
146+
def description(), do: "Encrypts a sensitive configuration value to be used in the advanced.config file"
147+
148+
#
149+
# Implementation
150+
#
151+
152+
defp supports_cipher(cipher), do: Enum.member?(:rabbit_pbe.supported_ciphers(), cipher)
153+
154+
defp supports_hash(hash), do: Enum.member?(:rabbit_pbe.supported_hashes(), hash)
155+
end
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
## Prints values from a command as strings(if possible)
8+
defmodule RabbitMQ.CLI.Formatters.EncryptedConfValue do
9+
alias RabbitMQ.CLI.Core.Helpers
10+
alias RabbitMQ.CLI.Formatters.FormatterHelpers
11+
12+
@behaviour RabbitMQ.CLI.FormatterBehaviour
13+
14+
def format_output(output, _) do
15+
Helpers.string_or_inspect("encrypted:#{output}")
16+
end
17+
18+
def format_stream(stream, options) do
19+
Stream.map(
20+
stream,
21+
FormatterHelpers.without_errors_1(fn el ->
22+
format_output(el, options)
23+
end)
24+
)
25+
end
26+
end

0 commit comments

Comments
 (0)