You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by rn...@apache.org on 2023/05/26 15:04:54 UTC

[couchdb] 01/01: Add optional logging of security issues when replicating

This is an automated email from the ASF dual-hosted git repository.

rnewson pushed a commit to branch replicator_security_warnings
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit cb6c65a5016b964498902d94777a95b195288fa6
Author: Robert Newson <rn...@apache.org>
AuthorDate: Fri May 26 15:21:50 2023 +0100

    Add optional logging of security issues when replicating
---
 .../src/couch_replicator_scheduler.erl             |  2 +
 .../src/couch_replicator_utils.erl                 | 68 +++++++++++++++++++++-
 2 files changed, 69 insertions(+), 1 deletion(-)

diff --git a/src/couch_replicator/src/couch_replicator_scheduler.erl b/src/couch_replicator/src/couch_replicator_scheduler.erl
index d02256807..7c275737e 100644
--- a/src/couch_replicator/src/couch_replicator_scheduler.erl
+++ b/src/couch_replicator/src/couch_replicator_scheduler.erl
@@ -58,6 +58,7 @@
 -include("couch_replicator.hrl").
 -include_lib("couch_replicator/include/couch_replicator_api_wrap.hrl").
 -include_lib("couch/include/couch_db.hrl").
+-include_lib("ibrowse/include/ibrowse.hrl").
 
 %% definitions
 -define(MAX_BACKOFF_EXPONENT, 10).
@@ -627,6 +628,7 @@ start_job_int(#job{pid = Pid}, _State) when Pid /= undefined ->
     ok;
 start_job_int(#job{} = Job0, State) ->
     Job = maybe_optimize_job_for_rate_limiting(Job0),
+    ok = couch_replicator_utils:log_security_warnings(Job0#job.rep),
     case couch_replicator_scheduler_sup:start_child(Job#job.rep) of
         {ok, Child} ->
             Ref = monitor(process, Child),
diff --git a/src/couch_replicator/src/couch_replicator_utils.erl b/src/couch_replicator/src/couch_replicator_utils.erl
index 40d188516..4cceaafed 100644
--- a/src/couch_replicator/src/couch_replicator_utils.erl
+++ b/src/couch_replicator/src/couch_replicator_utils.erl
@@ -27,13 +27,15 @@
     get_basic_auth_creds/1,
     remove_basic_auth_creds/1,
     normalize_basic_auth/1,
-    seq_encode/1
+    seq_encode/1,
+    log_security_warnings/1
 ]).
 
 -include_lib("ibrowse/include/ibrowse.hrl").
 -include_lib("couch/include/couch_db.hrl").
 -include("couch_replicator.hrl").
 -include_lib("couch_replicator/include/couch_replicator_api_wrap.hrl").
+-include_lib("public_key/include/public_key.hrl").
 
 -import(couch_util, [
     get_value/2,
@@ -276,6 +278,70 @@ seq_encode(Seq) ->
     % object. We are being maximally compatible here.
     ?JSON_ENCODE(Seq).
 
+%% Log uses of http protocol and uses of https protocol where verify_peer
+%% would fail.
+log_security_warnings(#rep{} = Rep) ->
+    case config:get_boolean("couch_replicator", "log_security_warnings", false) of
+        false ->
+            ok;
+        true ->
+            ok = check_security(Rep, source),
+            ok = check_security(Rep, target)
+    end.
+
+check_security(#rep{} = Rep, Type) ->
+    Url =
+        case Type of
+            source -> Rep#rep.source#httpdb.url;
+            target -> Rep#rep.target#httpdb.url
+        end,
+    #url{protocol = Protocol} = ibrowse_lib:parse_url(Url),
+    case Protocol of
+        http ->
+            couch_log:warning("**security warning** replication ~s has insecure ~s at ~s", [
+                rep_principal(Rep), Type, Url
+            ]),
+            ok;
+        https ->
+            ibrowse:send_req(Url, [], head, [], [
+                {is_ssl, true}, {ssl_options, [{verify_fun, check_security_fun(Rep, Url)}]}
+            ]),
+            ok;
+        _ ->
+            ok
+    end.
+
+check_security_fun(#rep{} = Rep, Url) ->
+    Fun = fun
+        (_, {bad_cert, Reason}, UserState) ->
+            couch_log:warning("**security warning** bad cert ~s for reason ~p at ~s", [
+                rep_principal(Rep), Reason, Url
+            ]),
+            {valid, UserState};
+        (_, {extension, #'Extension'{critical = true} = Ext}, UserState) ->
+            couch_log:warning(
+                "**security warning** unsupported critical extension ~p ~s for reason ~p at ~s", [
+                    Ext#'Extension'.extnID, rep_principal(Rep), Url
+                ]
+            ),
+            {valid, UserState};
+        (_, {extension, _}, UserState) ->
+            {unknown, UserState};
+        (_, valid, UserState) ->
+            {valid, UserState};
+        (_, valid_peer, UserState) ->
+            {valid, UserState}
+    end,
+    InitialState = [],
+    {Fun, InitialState}.
+
+rep_principal(#rep{db_name = DbName} = Rep) when is_binary(DbName) ->
+    io_lib:format("in database ~s, docid ~s", [
+        mem3:dbname(DbName), Rep#rep.doc_id
+    ]);
+rep_principal(#rep{user_ctx = #user_ctx{name = Name}}) when is_binary(Name) ->
+    io_lib:format("by user ~s", [Name]).
+
 -ifdef(TEST).
 
 -include_lib("couch/include/couch_eunit.hrl").