fix(snappymail): set admin password via Python+PHP directly in application.ini
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:
parent
1b3f3e62b9
commit
e09e2fe04a
1 changed files with 61 additions and 4 deletions
|
|
@ -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) ──────────────────
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue