Repository: netbox-community/netbox

Issue:
Restrict all access to authenticated users by default
### NetBox version

v4.0.1

### Feature type

Change to existing functionality

### Proposed functionality

Currently, the [`LOGIN_REQUIRED`](https://docs.netbox.dev/en/stable/configuration/security/#login_required) configuration parameter is disabled by default. This issue proposes changing its default value to true (enabled).

### Use case

This change will provide a more secure default configuration.

It should be noted that this change will effect a temporarily breaking change for all deployments in which the `LOGIN_REQUIRED` parameter is not defined, as its default value will change. However, we should expect these to be minimal, as the parameter is included in the [example configuration file](https://github.com/netbox-community/netbox/blob/develop/netbox/netbox/configuration_example.py) provided for reference and typically [copied at the time of installation](https://docs.netbox.dev/en/stable/installation/3-netbox/#configuration).

### Database changes

None

### External dependencies

None


Discussion:

Resolving patch:
diff --git a/netbox/netbox/configuration_example.py b/netbox/netbox/configuration_example.py
--- a/netbox/netbox/configuration_example.py
+++ b/netbox/netbox/configuration_example.py
@@ -157,9 +157,8 @@
 # authenticated to NetBox indefinitely.
 LOGIN_PERSISTENCE = False
 
-# Setting this to True will permit only authenticated users to access any part of NetBox. By default, anonymous users
-# are permitted to access most data in NetBox but not make any changes.
-LOGIN_REQUIRED = False
+# Setting this to False will permit unauthenticated users to access most areas of NetBox (but not make any changes).
+LOGIN_REQUIRED = True
 
 # The length of time (in seconds) for which a user will remain logged into the web UI before being prompted to
 # re-authenticate. (Default: 1209600 [14 days])
diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py
--- a/netbox/netbox/settings.py
+++ b/netbox/netbox/settings.py
@@ -105,7 +105,7 @@
 LANGUAGE_COOKIE_PATH = CSRF_COOKIE_PATH
 LOGGING = getattr(configuration, 'LOGGING', {})
 LOGIN_PERSISTENCE = getattr(configuration, 'LOGIN_PERSISTENCE', False)
-LOGIN_REQUIRED = getattr(configuration, 'LOGIN_REQUIRED', False)
+LOGIN_REQUIRED = getattr(configuration, 'LOGIN_REQUIRED', True)
 LOGIN_TIMEOUT = getattr(configuration, 'LOGIN_TIMEOUT', None)
 LOGOUT_REDIRECT_URL = getattr(configuration, 'LOGOUT_REDIRECT_URL', 'home')
 MEDIA_ROOT = getattr(configuration, 'MEDIA_ROOT', os.path.join(BASE_DIR, 'media')).rstrip('/')


Test patch:
diff --git a/netbox/netbox/tests/test_plugins.py b/netbox/netbox/tests/test_plugins.py
--- a/netbox/netbox/tests/test_plugins.py
+++ b/netbox/netbox/tests/test_plugins.py
@@ -42,6 +42,7 @@ def test_admin(self):
         url = reverse('admin:dummy_plugin_dummymodel_add')
         self.assertEqual(url, '/admin/dummy_plugin/dummymodel/add/')
 
+    @override_settings(LOGIN_REQUIRED=False)
     def test_views(self):
 
         # Test URL resolution
@@ -53,7 +54,7 @@ def test_views(self):
         response = client.get(url)
         self.assertEqual(response.status_code, 200)
 
-    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
+    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], LOGIN_REQUIRED=False)
     def test_api_views(self):
 
         # Test URL resolution
@@ -65,6 +66,7 @@ def test_api_views(self):
         response = client.get(url)
         self.assertEqual(response.status_code, 200)
 
+    @override_settings(LOGIN_REQUIRED=False)
     def test_registered_views(self):
 
         # Test URL resolution
diff --git a/netbox/utilities/testing/api.py b/netbox/utilities/testing/api.py
--- a/netbox/utilities/testing/api.py
+++ b/netbox/utilities/testing/api.py
@@ -73,7 +73,7 @@ class APIViewTestCases:
 
     class GetObjectViewTestCase(APITestCase):
 
-        @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
+        @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], LOGIN_REQUIRED=False)
         def test_get_object_anonymous(self):
             """
             GET a single object as an unauthenticated user.
@@ -135,7 +135,7 @@ def test_options_object(self):
     class ListObjectsViewTestCase(APITestCase):
         brief_fields = []
 
-        @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
+        @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], LOGIN_REQUIRED=False)
         def test_list_objects_anonymous(self):
             """
             GET a list of objects as an unauthenticated user.
diff --git a/netbox/utilities/testing/views.py b/netbox/utilities/testing/views.py
--- a/netbox/utilities/testing/views.py
+++ b/netbox/utilities/testing/views.py
@@ -62,7 +62,7 @@ class GetObjectViewTestCase(ModelViewTestCase):
         """
         Retrieve a single instance.
         """
-        @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
+        @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], LOGIN_REQUIRED=False)
         def test_get_object_anonymous(self):
             # Make the request as an unauthenticated user
             self.client.logout()
@@ -421,7 +421,7 @@ class ListObjectsViewTestCase(ModelViewTestCase):
         """
         Retrieve multiple instances.
         """
-        @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
+        @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], LOGIN_REQUIRED=False)
         def test_list_objects_anonymous(self):
             # Make the request as an unauthenticated user
             self.client.logout()