Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Admin cannot be accessed on FIPS-mode system #10184

Closed
nutjob4life opened this issue Mar 3, 2023 · 4 comments · Fixed by #10192
Closed

Admin cannot be accessed on FIPS-mode system #10184

nutjob4life opened this issue Mar 3, 2023 · 4 comments · Fixed by #10192
Labels
status:Unconfirmed Issue, usually a bug, that has not yet been validated as a confirmed problem. type:Bug

Comments

@nutjob4life
Copy link
Contributor

nutjob4life commented Mar 3, 2023

Issue Summary

When accessing the Wagtail admin on a security-restricted system (such as on SELinux with FIPS mode enabled), "Server Error (500)" is produced. This prevents Wagtail from being used on new U.S. Government systems, for example.

The issue stems from this code (dealing with the Gravatar handling for the sidebar on the admin UI):

hash=hashlib.md5(email.lower().encode("utf-8")).hexdigest(),

On security-restricted systems, the MD5 hash algorithm is disabled and produces an error such as:

[digital envelope routines: EVP_DigestInit_ex] disabled for FIPS

Starting with Python 3.9, it's possible to pass a flag usedforsecurity=False to MD5 to indicate that it's not being used for security but for fingerprinting or other non-secure purposes.

Steps to Reproduce

$ cat /etc/redhat-release 
Red Hat Enterprise Linux release 8.7 (Ootpa)
$ fips-mode-setup --check
FIPS mode is enabled.
$ python3 -c 'import hashlib; hashlib.md5(b"")'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ValueError: [digital envelope routines: EVP_DigestInit_ex] disabled for FIPS
$ python3 -m venv venv
$ cd venv
$ . bin/activate
(venv) $ pip install --quiet wagtail
(venv) $ wagtail start fips
Creating a Wagtail project called fips
Success! fips has been created
(venv) $ cd fips
(venv) $ ./manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, home, sessions, taggit, wagtailadmin, wagtailcore, wagtaildocs, wagtailembeds, wagtailforms, wagtailimages, wagtailredirects, wagtailsearch, wagtailusers
Running migrations:
  Applying contenttypes.0001_initial... OK
…
Applying wagtailusers.0011_userprofile_dismissibles... OK
(venv) $ ./manage.py createsuperuser --email x@y.com --username admin 
Password: 
Password (again): 
Superuser created successfully.
(venv) $ ./manage.py runserver 8080
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
March 03, 2023 - 15:36:51
Django version 4.1.7, using settings 'fips.settings.dev'
Starting development server at http://127.0.0.1:8080/
Quit the server with CONTROL-C.

At this point, visit http://localhost:8080/admin and log in. The console then displays:

[03/Mar/2023 15:40:22] "POST /admin/login/ HTTP/1.1" 302 0
Internal Server Error: /admin/
Traceback (most recent call last):
  File "venv/lib/python3.9/site-packages/telepath/__init__.py", line 318, in build_node
    existing_node = self.nodes[obj_id]
KeyError: 139742870267072

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "venv/lib/python3.9/site-packages/telepath/__init__.py", line 318, in build_node
    existing_node = self.nodes[obj_id]
KeyError: 139742896011872

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "venv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 56, in inner
    response = get_response(request)
  File "venv/lib/python3.9/site-packages/django/core/handlers/base.py", line 220, in _get_response
    response = response.render()
  File "venv/lib/python3.9/site-packages/django/template/response.py", line 114, in render
    self.content = self.rendered_content
  File "venv/lib/python3.9/site-packages/django/template/response.py", line 92, in rendered_content
    return template.render(context, self._request)
  File "venv/lib/python3.9/site-packages/django/template/backends/django.py", line 61, in render
    return self.template.render(context)
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 175, in render
    return self._render(context)
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 167, in _render
    return self.nodelist.render(context)
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
  File "venv/lib/python3.9/site-packages/django/template/loader_tags.py", line 157, in render
    return compiled_parent._render(context)
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 167, in _render
    return self.nodelist.render(context)
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
  File "venv/lib/python3.9/site-packages/django/template/loader_tags.py", line 157, in render
    return compiled_parent._render(context)
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 167, in _render
    return self.nodelist.render(context)
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
  File "venv/lib/python3.9/site-packages/django/template/loader_tags.py", line 157, in render
    return compiled_parent._render(context)
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 167, in _render
    return self.nodelist.render(context)
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
  File "venv/lib/python3.9/site-packages/django/template/loader_tags.py", line 157, in render
    return compiled_parent._render(context)
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 167, in _render
    return self.nodelist.render(context)
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
  File "venv/lib/python3.9/site-packages/django/template/loader_tags.py", line 63, in render
    result = block.nodelist.render(context)
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
  File "venv/lib/python3.9/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
  File "venv/lib/python3.9/site-packages/django/template/library.py", line 237, in render
    output = self.func(*resolved_args, **resolved_kwargs)
  File "venv/lib/python3.9/site-packages/wagtail/admin/templatetags/wagtailadmin_tags.py", line 944, in sidebar_props
    "modules": JSContext().pack(modules),
  File "venv/lib/python3.9/site-packages/telepath/__init__.py", line 220, in pack
    return ValueContext(self).build_node(obj).emit()
  File "venv/lib/python3.9/site-packages/telepath/__init__.py", line 321, in build_node
    node = self._build_new_node(val)
  File "venv/lib/python3.9/site-packages/telepath/__init__.py", line 354, in _build_new_node
    return ListNode([self.build_node(item) for item in items])
  File "venv/lib/python3.9/site-packages/telepath/__init__.py", line 354, in <listcomp>
    return ListNode([self.build_node(item) for item in items])
  File "venv/lib/python3.9/site-packages/telepath/__init__.py", line 321, in build_node
    node = self._build_new_node(val)
  File "venv/lib/python3.9/site-packages/telepath/__init__.py", line 339, in _build_new_node
    return adapter.build_node(obj, self)
  File "venv/lib/python3.9/site-packages/telepath/__init__.py", line 167, in build_node
    constructor, args = self.pack(obj, context)
  File "venv/lib/python3.9/site-packages/telepath/__init__.py", line 164, in pack
    return (self.js_constructor, self.js_args(obj))
  File "venv/lib/python3.9/site-packages/wagtail/telepath.py", line 68, in <lambda>
    "js_args": lambda self, obj: obj.js_args(),
  File "venv/lib/python3.9/site-packages/wagtail/admin/ui/sidebar.py", line 211, in js_args
    "avatarUrl": avatar_url(self.user, size=50),
  File "venv/lib/python3.9/site-packages/wagtail/admin/templatetags/wagtailadmin_tags.py", line 714, in avatar_url
    gravatar_url = get_gravatar_url(user.email, size=size)
  File "venv/lib/python3.9/site-packages/wagtail/users/utils.py", line 43, in get_gravatar_url
    hash=hashlib.md5(email.lower().encode("utf-8")).hexdigest(),
ValueError: [digital envelope routines: EVP_DigestInit_ex] disabled for FIPS
[03/Mar/2023 15:40:22] "GET /admin/ HTTP/1.1" 500 340554
  • I have confirmed that this issue can be reproduced as described on a fresh Wagtail project: yes ✅

Workaround

Patch wagtail/users/utils.py changing 43 to read:

hash=hashlib.md5(email.lower().encode("utf-8"), usedforsecurity=False).hexdigest(),

👉 Note: This requires Python 3.9 or higher.

Additional Uses

@laymonage points out that there's a second call to md5 in

def get_embed_hash(url, max_width=None, max_height=None):
h = md5()

Technical details

  • Python version: Run python --version: Python 3.9.12
  • Django version: Look in your requirements.txt, or run pip show django | grep Version: Version: 4.1.7
  • Wagtail version: Look at the bottom of the Settings menu in the Wagtail admin, or run pip show wagtail | grep Version:: Version: 4.2
  • Browser version: You can use https://www.whatsmybrowser.org/ to find this out: You're using Lynx 2. If you believe this is incorrect, then please email us to let us know!
@nutjob4life nutjob4life added status:Unconfirmed Issue, usually a bug, that has not yet been validated as a confirmed problem. type:Bug labels Mar 3, 2023
nutjob4life added a commit to nutjob4life/wagtail that referenced this issue Mar 5, 2023
@zerolab
Copy link
Contributor

zerolab commented Mar 6, 2023

Hey @nutjob4life,

Thank you for raising this. A PR would be most welcome.

If you are not using Gravatar, you could set WAGTAIL_GRAVATAR_PROVIDER_URL to None in your settings to disable that logic completely. At least as a temporary workaround

@nutjob4life
Copy link
Contributor Author

Hi @zerolab, I've got a PR nearly, but I'm not sure how to write a unit test for it. I guess I could monkey-patch the hashlib and fake an md5? Would it be okay to submit the PR without the test coverage?

@laymonage
Copy link
Member

laymonage commented Mar 6, 2023

Hi @nutjob4life, indeed it is a bit tricky to test this one. For reference, Django handled this in django/django#14763 and it seems they didn't add tests either (unless I missed it in a separate commit outside of that PR).

We also have another usage of md5 here:

def get_embed_hash(url, max_width=None, max_height=None):
h = md5()

I think ideally we should fix it in both instances. Feel free to start a draft PR and we'll see what we can do!

@nutjob4life
Copy link
Contributor Author

You got it @laymonage! Draft PR posted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status:Unconfirmed Issue, usually a bug, that has not yet been validated as a confirmed problem. type:Bug
Projects
None yet
4 participants