You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by va...@apache.org on 2023/02/27 20:34:40 UTC

[couchdb] branch main updated (4df37bc8c -> 54879f9a5)

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

vatamane pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/couchdb.git


    from 4df37bc8c Update last_check in file logger record
     new f677dd5e8 Use persistent terms for features
     new 54879f9a5 This enables configuring FIPS mode at runtime without the need for a custom build.

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 rel/overlay/etc/vm.args                  | 11 +++++++
 src/config/src/config.erl                | 42 +++++++++++++++++---------
 src/config/test/config_tests.erl         |  6 ++++
 src/couch/src/couch_hash.erl             | 30 +++++++++++++++---
 src/couch/src/couch_server.erl           |  8 -----
 src/couch/test/eunit/couch_hash_test.erl | 52 ++++++++++++++++++++++++++++++++
 6 files changed, 122 insertions(+), 27 deletions(-)
 create mode 100644 src/couch/test/eunit/couch_hash_test.erl


[couchdb] 02/02: This enables configuring FIPS mode at runtime without the need for a custom build.

Posted by va...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 54879f9a5d093b8000d64070e7de323e155f2a2a
Author: Nick Vatamaniuc <va...@gmail.com>
AuthorDate: Fri Feb 24 18:16:19 2023 -0500

    This enables configuring FIPS mode at runtime without the need for a custom build.
    
    Issue: #4442
---
 rel/overlay/etc/vm.args                  | 11 +++++++
 src/config/src/config.erl                | 12 ++++++++
 src/config/test/config_tests.erl         |  6 ++++
 src/couch/src/couch_hash.erl             | 30 +++++++++++++++---
 src/couch/src/couch_server.erl           |  8 -----
 src/couch/test/eunit/couch_hash_test.erl | 52 ++++++++++++++++++++++++++++++++
 6 files changed, 107 insertions(+), 12 deletions(-)

diff --git a/rel/overlay/etc/vm.args b/rel/overlay/etc/vm.args
index 2c011e405..174fba1c5 100644
--- a/rel/overlay/etc/vm.args
+++ b/rel/overlay/etc/vm.args
@@ -99,3 +99,14 @@
 #-proto_dist couch
 #-couch_dist no_tls '"clouseau@127.0.0.1"'
 #-ssl_dist_optfile <path/to/couch_ssl_dist.conf>
+
+# Enable FIPS mode
+#   https://www.erlang.org/doc/apps/crypto/fips.html
+#   Ensure that:
+#    - Erlang is built with --enable-fips configuration option
+#    - Crypto library (e.g. OpenSSL) supports this mode
+#
+# When the mode is successfully enabled "Welcome" message should show `fips`
+# in the features list.
+#
+#-crypto fips_mode true
diff --git a/src/config/src/config.erl b/src/config/src/config.erl
index 7cd7251e6..72dff72d8 100644
--- a/src/config/src/config.erl
+++ b/src/config/src/config.erl
@@ -229,6 +229,17 @@ is_enabled(Feature) when is_atom(Feature) ->
     Map = persistent_term:get({?MODULE, ?FEATURES}, #{}),
     maps:get(Feature, Map, false).
 
+% Some features like FIPS mode must be enabled earlier before couch, couch_epi
+% start up
+%
+enable_early_features() ->
+    % Mark FIPS if enabled
+    case crypto:info_fips() == enabled of
+        true ->
+            enable_feature(fips);
+        false ->
+            ok
+    end.
 
 listen_for_changes(CallbackModule, InitialState) ->
     config_listener_mon:subscribe(CallbackModule, InitialState).
@@ -237,6 +248,7 @@ subscribe_for_changes(Subscription) ->
     config_notifier:subscribe(Subscription).
 
 init(IniFiles) ->
+    enable_early_features(),
     ets:new(?MODULE, [named_table, set, protected, {read_concurrency, true}]),
     lists:map(
         fun(IniFile) ->
diff --git a/src/config/test/config_tests.erl b/src/config/test/config_tests.erl
index 3d3ee9c94..90d430a87 100644
--- a/src/config/test/config_tests.erl
+++ b/src/config/test/config_tests.erl
@@ -651,11 +651,14 @@ should_enable_features() ->
 
     ?assertEqual(ok, config:enable_feature(snek)),
     ?assertEqual([snek], config:features()),
+    ?assert(config:is_enabled(snek)),
 
     ?assertEqual(ok, config:enable_feature(snek)),
     ?assertEqual([snek], config:features()),
 
     ?assertEqual(ok, config:enable_feature(dogo)),
+    ?assert(config:is_enabled(dogo)),
+    ?assert(config:is_enabled(snek)),
     ?assertEqual([dogo, snek], config:features()).
 
 should_disable_features() ->
@@ -666,9 +669,11 @@ should_disable_features() ->
     ?assertEqual([snek], config:features()),
 
     ?assertEqual(ok, config:disable_feature(snek)),
+    ?assertNot(config:is_enabled(snek)),
     ?assertEqual([], config:features()),
 
     ?assertEqual(ok, config:disable_feature(snek)),
+    ?assertNot(config:is_enabled(snek)),
     ?assertEqual([], config:features()).
 
 should_keep_features_on_config_restart() ->
@@ -678,6 +683,7 @@ should_keep_features_on_config_restart() ->
     config:enable_feature(snek),
     ?assertEqual([snek], config:features()),
     with_process_restart(config),
+    ?assert(config:is_enabled(snek)),
     ?assertEqual([snek], config:features()).
 
 should_notify_on_config_reload(Subscription, {_Apps, Pid}) ->
diff --git a/src/couch/src/couch_hash.erl b/src/couch/src/couch_hash.erl
index 842b37423..a2b3da4d8 100644
--- a/src/couch/src/couch_hash.erl
+++ b/src/couch/src/couch_hash.erl
@@ -10,10 +10,20 @@
 % License for the specific language governing permissions and limitations under
 % the License.
 
+% This module is enable use of the built-in Erlang MD5 hashing function for
+% non-cryptographic usage when in FIPS mode.
+%
+% For more details see:
+%   https://www.erlang.org/doc/apps/crypto/fips.html#avoid-md5-for-hashing
+
 -module(couch_hash).
 
 -export([md5_hash/1, md5_hash_final/1, md5_hash_init/0, md5_hash_update/2]).
 
+% The ERLANG_MD5 define is set at compile time by --erlang-md5 configure flag
+% This is deprecated. Instead, FIPS mode is now detected automatically and the
+% build-in Erlang function will be used when FIPS mode is enabled.
+%
 -ifdef(ERLANG_MD5).
 
 md5_hash(Data) ->
@@ -31,15 +41,27 @@ md5_hash_update(Context, Data) ->
 -else.
 
 md5_hash(Data) ->
-    crypto:hash(md5, Data).
+    case config:is_enabled(fips) of
+        true -> erlang:md5(Data);
+        false -> crypto:hash(md5, Data)
+    end.
 
 md5_hash_final(Context) ->
-    crypto:hash_final(Context).
+    case config:is_enabled(fips) of
+        true -> erlang:md5_final(Context);
+        false -> crypto:hash_final(Context)
+    end.
 
 md5_hash_init() ->
-    crypto:hash_init(md5).
+    case config:is_enabled(fips) of
+        true -> erlang:md5_init();
+        false -> crypto:hash_init(md5)
+    end.
 
 md5_hash_update(Context, Data) ->
-    crypto:hash_update(Context, Data).
+    case config:is_enabled(fips) of
+        true -> erlang:md5_update(Context, Data);
+        false -> crypto:hash_update(Context, Data)
+    end.
 
 -endif.
diff --git a/src/couch/src/couch_server.erl b/src/couch/src/couch_server.erl
index 4af4c1ff4..7dbbe4af1 100644
--- a/src/couch/src/couch_server.erl
+++ b/src/couch/src/couch_server.erl
@@ -274,14 +274,6 @@ init([N]) ->
     % Mark being able to receive documents with an _access property as a supported feature
     config:enable_feature('access-ready'),
 
-    % Mark if fips is enabled
-    case crypto:info_fips() == enabled of
-        true ->
-            config:enable_feature('fips');
-        false ->
-            ok
-    end,
-
     % read config and register for configuration changes
 
     % just stop if one of the config settings change. couch_server_sup
diff --git a/src/couch/test/eunit/couch_hash_test.erl b/src/couch/test/eunit/couch_hash_test.erl
new file mode 100644
index 000000000..a33164c6e
--- /dev/null
+++ b/src/couch/test/eunit/couch_hash_test.erl
@@ -0,0 +1,52 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_hash_test).
+
+-include_lib("couch/include/couch_eunit.hrl").
+
+-define(XY_HASH, <<62, 68, 16, 113, 112, 165, 32, 88, 42, 222, 82, 47, 167, 60, 29, 21>>).
+
+couch_hash_test_() ->
+    {
+        foreach,
+        fun setup/0,
+        fun teardown/1,
+        [
+            ?TDEF_FE(t_fips_disabled),
+            ?TDEF_FE(t_fips_enabled)
+        ]
+    }.
+
+setup() ->
+    Ctx = test_util:start_couch([crypto]),
+    config:disable_feature(fips),
+    Ctx.
+
+teardown(Ctx) ->
+    config:disable_feature(fips),
+    test_util:stop_couch(Ctx).
+
+t_fips_disabled(_) ->
+    ?assertEqual(?XY_HASH, couch_hash:md5_hash(<<"xy">>)),
+    H = couch_hash:md5_hash_init(),
+    H1 = couch_hash:md5_hash_update(H, <<"x">>),
+    H2 = couch_hash:md5_hash_update(H1, <<"y">>),
+    ?assertEqual(?XY_HASH, couch_hash:md5_hash_final(H2)).
+
+t_fips_enabled(_) ->
+    config:enable_feature(fips),
+    ?assertEqual(?XY_HASH, couch_hash:md5_hash(<<"xy">>)),
+    H = couch_hash:md5_hash_init(),
+    H1 = couch_hash:md5_hash_update(H, <<"x">>),
+    H2 = couch_hash:md5_hash_update(H1, <<"y">>),
+    ?assertEqual(?XY_HASH, couch_hash:md5_hash_final(H2)).


[couchdb] 01/02: Use persistent terms for features

Posted by va...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit f677dd5e8f5276c5dff8d48b4df5494d74df4748
Author: Nick Vatamaniuc <va...@gmail.com>
AuthorDate: Fri Feb 24 18:14:28 2023 -0500

    Use persistent terms for features
    
    This is intended to speed up feature checks.
---
 src/config/src/config.erl | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/src/config/src/config.erl b/src/config/src/config.erl
index 7c38ff58f..7cd7251e6 100644
--- a/src/config/src/config.erl
+++ b/src/config/src/config.erl
@@ -31,7 +31,7 @@
 -export([get_float/3, set_float/3, set_float/4]).
 -export([get_boolean/3, set_boolean/3, set_boolean/4]).
 
--export([features/0, enable_feature/1, disable_feature/1]).
+-export([features/0, enable_feature/1, disable_feature/1, is_enabled/1]).
 
 -export([listen_for_changes/2]).
 -export([subscribe_for_changes/1]).
@@ -42,7 +42,7 @@
 
 -export([is_sensitive/2]).
 
--define(FEATURES, "features").
+-define(FEATURES, features).
 
 -define(TIMEOUT, 30000).
 -define(INVALID_SECTION, <<"Invalid configuration section">>).
@@ -212,23 +212,23 @@ delete(Section, Key, Persist, Reason) when is_list(Section), is_list(Key) ->
     ).
 
 features() ->
-    application:get_env(config, enabled_features, []).
+    Map = persistent_term:get({?MODULE, ?FEATURES}, #{}),
+    lists:sort(maps:keys(Map)).
 
 enable_feature(Feature) when is_atom(Feature) ->
-    application:set_env(
-        config,
-        enabled_features,
-        lists:usort([Feature | features()]),
-        [{persistent, true}]
-    ).
+    Map = persistent_term:get({?MODULE, ?FEATURES}, #{}),
+    Map1 = Map#{Feature => true},
+    persistent_term:put({?MODULE, ?FEATURES}, Map1).
 
 disable_feature(Feature) when is_atom(Feature) ->
-    application:set_env(
-        config,
-        enabled_features,
-        features() -- [Feature],
-        [{persistent, true}]
-    ).
+    Map = persistent_term:get({?MODULE, ?FEATURES}, #{}),
+    Map1 = maps:remove(Feature, Map),
+    persistent_term:put({?MODULE, ?FEATURES}, Map1).
+
+is_enabled(Feature) when is_atom(Feature) ->
+    Map = persistent_term:get({?MODULE, ?FEATURES}, #{}),
+    maps:get(Feature, Map, false).
+
 
 listen_for_changes(CallbackModule, InitialState) ->
     config_listener_mon:subscribe(CallbackModule, InitialState).