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-2021 VMware, Inc. or its affiliates.  All rights reserved.
6
7defmodule RabbitMQ.CLI.Diagnostics.Commands.CheckCertificateExpirationCommand do
8  alias RabbitMQ.CLI.Core.DocGuide
9  alias RabbitMQ.CLI.TimeUnit, as: TU
10  @behaviour RabbitMQ.CLI.CommandBehaviour
11
12  import RabbitMQ.CLI.Core.Listeners
13
14  def switches(), do: [unit: :string, within: :integer]
15
16  def merge_defaults(args, opts) do
17    {args, Map.merge(%{unit: "weeks", within: 4}, opts)}
18  end
19
20  def validate(args, _) when length(args) > 0 do
21    {:validation_failure, :too_many_args}
22  end
23  def validate(_, %{unit: unit}) do
24    case TU.known_unit?(unit) do
25      true ->
26        :ok
27
28      false ->
29        {:validation_failure, "unit '#{unit}' is not supported. Please use one of: days, weeks, months, years"}
30    end
31  end
32  def validate(_, _), do: :ok
33
34  def run([], %{node: node_name, unit: unit, within: within, timeout: timeout}) do
35    case :rabbit_misc.rpc_call(node_name, :rabbit_networking, :active_listeners, [], timeout) do
36      {:error, _} = err ->
37        err
38
39      {:error, _, _} = err ->
40        err
41
42      {:badrpc, _} = err ->
43        err
44
45      xs when is_list(xs) ->
46        listeners = listeners_on(xs, node_name)
47        seconds = TU.convert(within, unit)
48        Enum.reduce(listeners, [], fn (listener, acc) -> case listener_expiring_within(listener, seconds) do
49                                                           false -> acc
50                                                           expiring -> [expiring | acc]
51                                                         end
52        end)
53    end
54  end
55
56  def output([], %{formatter: "json"}) do
57    {:ok, %{"result" => "ok"}}
58  end
59
60  def output([], %{unit: unit, within: within}) do
61    unit_label = unit_label(within, unit)
62    {:ok, "No certificates are expiring within #{within} #{unit_label}."}
63  end
64
65  def output(listeners, %{formatter: "json"}) do
66    {:error, :check_failed, %{"result" => "error", "expired" => Enum.map(listeners, &expired_listener_map/1)}}
67  end
68
69  def output(listeners, %{}) do
70    {:error, :check_failed, Enum.map(listeners, &expired_listener_map/1)}
71  end
72
73  def unit_label(1, unit) do
74    unit |> String.slice(0..-2)
75  end
76  def unit_label(_within, unit) do
77    unit
78  end
79
80  def usage, do: "check_certificate_expiration [--within <period>] [--unit <unit>]"
81
82  def usage_additional() do
83    [
84      ["<period>", "period of time to check. Default is four (weeks)."],
85      ["<unit>", "time unit for the period, can be days, weeks, months, years. Default is weeks."],
86    ]
87  end
88
89  def usage_doc_guides() do
90    [
91      DocGuide.tls(),
92      DocGuide.networking()
93    ]
94  end
95
96  def help_section(), do: :observability_and_health_checks
97
98  def description(), do: "Checks the expiration date on the certificates for every listener configured to use TLS"
99
100  def banner(_, %{node: node_name}), do: "Checking certificate expiration on node #{node_name} ..."
101end
102