fix(snappymail): set admin password via Python+PHP directly in application.ini
All checks were successful
CI/CD / syntax-check (push) Successful in 1m32s
CI/CD / deploy (push) Successful in 15m57s

djmaze/snappymail does not reliably apply SNAPPYMAIL_ADMIN_PASSWORD.
Instead: read current hash from application.ini, verify it against vault
password using password_verify() in container PHP, update only if wrong.
Idempotent — no restart if password is already correct.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jack 2026-03-22 21:51:58 +07:00
parent 1b3f3e62b9
commit e09e2fe04a

View file

@ -147,11 +147,68 @@
state: present state: present
pull: missing pull: missing
# ── SnappyMail: force restart so entrypoint applies SNAPPYMAIL_ADMIN_PASSWORD ── # ── SnappyMail admin password — write bcrypt hash directly to application.ini ──
# The env var is processed by the container entrypoint on every start. # djmaze/snappymail does not reliably apply SNAPPYMAIL_ADMIN_PASSWORD env var;
# Explicit restart ensures the password is always written to config correctly. # instead we verify and update the hash in the config file on every deploy.
- name: Restart SnappyMail to apply admin password from env - name: Ensure SnappyMail admin password is set correctly
ansible.builtin.shell: |
python3 << 'PYEOF'
import subprocess, re, sys
config_path = "{{ tools_root }}/snappymail/data/_data_/_default_/configs/application.ini"
password = "{{ snappymail_admin_password }}"
try:
with open(config_path) as f:
content = f.read()
except FileNotFoundError:
print("CONFIG_NOT_FOUND")
sys.exit(1)
# Extract current hash (bcrypt hashes start with $2y$)
m = re.search(r'^admin_password\s*=\s*"?(\$2y\$[^"\n]+)', content, re.M)
current_hash = m.group(1).strip() if m else ""
if current_hash:
r = subprocess.run(
["docker", "exec", "snappymail", "php", "-r",
f"echo password_verify('{password}', '{current_hash}') ? 'yes' : 'no';"],
capture_output=True, text=True
)
if r.stdout.strip() == "yes":
print("ALREADY_SET")
sys.exit(0)
# Generate a new bcrypt hash using PHP inside the container
r = subprocess.run(
["docker", "exec", "snappymail", "php", "-r",
f"echo password_hash('{password}', PASSWORD_BCRYPT);"],
capture_output=True, text=True
)
new_hash = r.stdout.strip()
if not new_hash.startswith("$2y$"):
print(f"HASH_ERROR: {r.stderr}")
sys.exit(1)
new_content = re.sub(
r'^admin_password\s*=.*$',
f'admin_password = "{new_hash}"',
content, flags=re.M
)
with open(config_path, "w") as f:
f.write(new_content)
print("UPDATED")
PYEOF
register: snappymail_pw_result
changed_when: "'UPDATED' in snappymail_pw_result.stdout"
failed_when: >
snappymail_pw_result.rc != 0 or
'CONFIG_NOT_FOUND' in snappymail_pw_result.stdout or
'HASH_ERROR' in snappymail_pw_result.stdout
- name: Restart SnappyMail after password update
ansible.builtin.command: docker restart snappymail ansible.builtin.command: docker restart snappymail
when: snappymail_pw_result.changed
changed_when: true changed_when: true
# ── Mail accounts (idempotent: check host-side config file) ────────────────── # ── Mail accounts (idempotent: check host-side config file) ──────────────────