Skip to content

fix(docker): replace passlib with libpass in the server image - #4322

Open
muhammadyusuf-kurbonov wants to merge 1 commit into
semaphoreui:developfrom
muhammadyusuf-kurbonov:fix/libpass-bcrypt-password-hash
Open

muhammadyusuf-kurbonov wants to merge 1 commit into
semaphoreui:developfrom
muhammadyusuf-kurbonov:fix/libpass-bcrypt-password-hash

Conversation

@muhammadyusuf-kurbonov

@muhammadyusuf-kurbonov muhammadyusuf-kurbonov commented Oct 8, 2026 •

Copy link
Copy Markdown

Summary

Fixes #4180.

passlib 1.7.4 (last release 2020) cannot load its bcrypt backend with bcrypt 5, which paramiko pulls into the server image. libpass is the maintained fork: it installs the same passlib package, so Ansible needs no other change.

 # deployment/docker/server/Dockerfile
-pip3 install --upgrade pip ansible==${ANSIBLE_VERSION} boto3 botocore requests pywinrm passlib paramiko
+pip3 install --upgrade pip ansible==${ANSIBLE_VERSION} boto3 botocore requests pywinrm libpass paramiko

bcrypt is deliberately not added as a dependency; it stays an optional backend that arrives only via paramiko. The runner and job images are unchanged.

Evidence

  • Before (passlib 1.7.4 + bcrypt 5.0.0, ansible-core 2.20.8, venv):

    {{ 'test' | password_hash('bcrypt') }}
    FAILED: The filter plugin 'ansible.builtin.password_hash' failed: Could not hash the secret:
    password cannot be longer than 72 bytes, truncate manually if necessary (e.g. my_password[:72])
    
    from passlib.apache import HtpasswdFile   # used by community.general.htpasswd
    ValueError: password cannot be longer than 72 bytes ...
    

    After (server image built from this branch: Python 3.14.8, ansible 13.5.0, ansible-core 2.20.10, libpass 1.9.3, bcrypt 5.0.0, paramiko 5.0.0):

    TASK [debug]  password_hash('bcrypt')
    ok: msg: $2b$12$fd3JXvyTi7d5ejwp6Ttl5uKHhFNaC9BHVpLSfmVuEMT1AVGohBOL.
    TASK [debug]  password_hash('sha512', 'saltsaltsalt')
    ok: msg: $6$rounds=656000$saltsaltsalt$NzKfSyeL3Y4j...   (identical to passlib output)
    TASK [community.general.htpasswd]  hash_scheme=bcrypt
    changed
    PLAY RECAP: ok=3 changed=1 failed=0
    
  • Also checked in venvs with libpass 1.9.3:

    • password_hash('bcrypt') works on ansible-core 2.16.5 / Python 3.12 (the Ansible 9.x line used by release builds).
    • Every passlib import used by ansible 13.5.0 core and collections loads (passlib.hash, passlib.context, passlib.apache, passlib.utils.handlers, passlib.utils.binary).
    • The bcrypt hash verifies with bcrypt.checkpw; an existing $2a$ hash still verifies.
    • ansible.netcommon.type5_pw works.

Merge Danger

Door: two-way. One word in one Dockerfile; reverting restores passlib.

Blast Radius: the server image's Ansible venv only.

  • Passwords longer than 72 bytes raise an error under bcrypt 5 instead of being silently truncated. This comes from bcrypt 5 itself, not from libpass.
  • libpass dropped some legacy passlib internals; playbooks or custom plugins that import private passlib APIs could be affected. None of the imports in ansible 13.5.0 are.
  • Not tested: the exact ansible==9.4.0 build on the Alpine image, and the runner/job images (unchanged here).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Updated the server deployment configuration. No user-facing features or behavior changed in this release.

passlib 1.7.4 is unmaintained and fails to load its bcrypt backend with
bcrypt 5, which paramiko pulls in. This broke password_hash('bcrypt') and
community.general.htpasswd. libpass is the maintained drop-in fork that
provides the same passlib package and supports bcrypt 5.

Fixes semaphoreui#4180

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 61cc7239-1dca-456b-b851-50f4855e3239
📥 Commits

Reviewing files that changed from the base of the PR and between fc66be9 and 6817e45.

📒 Files selected for processing (1)
  • deployment/docker/server/Dockerfile

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The server Dockerfile now installs libpass instead of passlib in the Ansible virtual environment.

Changes

Ansible password hashing

Layer / File(s) Summary
Ansible package installation
deployment/docker/server/Dockerfile
The Ansible environment installation replaces passlib with libpass.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~5 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: fiftin

Merge Risk: ⚪ Minimal · up to 6817e

The server image’s current package resolution supports bcrypt hashing for ordinary passwords. The acknowledged 72-byte bcrypt limit and possible future dependency drift do not establish a current merge blocker.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 6817e

The change is confined to the server’s password-hashing dependency. No expansion of runtime privileges or credential access was found. Remaining uncertainty concerns compatibility across published image variants and dependency versions selected by future builds.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The directly changed security-sensitive behavior is password hashing performed through Ansible in the server image. Effects on managed credentials depend on the playbooks executed; the evidence does not establish a tenant, asset, or downstream data-store count.

Trust Boundaries and Controls

  • inferred — The fork replaces trusted dependency code inside the existing Ansible environment. The comparison shows no new credential source, runtime identity, invocation path, or authority grant; the provider changes within the existing execution boundary.

Hardening Proposals

  • proposed — Record and constrain the tested libpass/bcrypt dependency combination for each published Ansible image variant, and retain a known-compatible image for rollback. This would reduce hashing-provider drift; it is a hardening proposal, not an observed vulnerability.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: replacing passlib with libpass in the server Docker image.
Linked Issues check ✅ Passed Issue #4180 requires fixing password_hash('bcrypt') in the official Docker image when passlib 1.7.4 meets bcrypt 5.0.0. The whole-PR diff changes the server image installation from passlib to …
Out of Scope Changes check ✅ Passed The whole-PR diff contains one dependency change in deployment/docker/server/Dockerfile. The change directly implements issue #4180. It does not add a direct bcrypt dependency and does not modify …
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Problem: password_hash('bcrypt') fails in Docker image — unpinned passlib + bcrypt 5.0.0

1 participant