# SSP - Self Service Password

# Implantação do LDAP Tool Box Self Service Password (SSP) via Docker

**IFSP Salto — VM `10.114.50.200` (Ubuntu Server 22.04) | LDAP: `ldap.slt.ifsp.edu.br` (`10.114.50.57`)**

Este guia cobre: instalação via Docker, LDAP, captcha, recuperação de senha por e-mail, bloqueio por tentativas e integração com Postfix. Baseado na documentação oficial ([instalação](https://self-service-password.readthedocs.io/en/latest/installation.html) e [Docker Hub](https://hub.docker.com/r/ltbproject/self-service-password)).

> A instalação do Docker foi desconsiderada, conforme solicitado — a VM já o possui.

---

## 1. Visão geral da arquitetura

<div aria-label="Código" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="14:1-22:4;637-1013" id="bkmrk--0" role="group" tabindex="0"><div aria-label="Código" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="14:1-22:4;637-1013" role="group" tabindex="0"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">  
</div></div><div class="overflow-x-auto"></div></div></div>```
[Usuário] --HTTPS--> [Nginx no host, TLS]
                            |
                            v
                    [container ssp:80] --LDAP(389)--> ldap.slt.ifsp.edu.br (10.114.50.57)
                            |
                            v (rede interna Docker "ssp_net", porta 587)
                    [container postfix] --> relay/smarthost institucional
```

A imagem oficial do SSP só expõe a aplicação em HTTP (porta 80) internamente — não há suporte documentado a TLS dentro do container. Por isso, o guia usa um **reverse proxy no host** (Nginx) para TLS. O Postfix roda em **outro container** (`boky/postfix`), na mesma rede Docker `ssp_net` do SSP — os dois se falam pelo nome do serviço, sem precisar tocar no host. É a imagem mais usada e madura para "Postfix como relay em container" (766+ estrelas no GitHub, focada exatamente nesse caso de uso — enviar e-mails de dentro do Docker sem gerenciar um MTA completo).

---

## 2. Estrutura de diretórios no host

<div aria-label="Código bash" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="37:1-40:4;1625-1712" id="bkmrk-bash" role="group" tabindex="0"><div aria-label="Código bash" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="37:1-40:4;1625-1712" role="group" tabindex="0"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">  
</div></div><div class="text-text-500 font-small p-3.5 pb-0">bash</div><div class="overflow-x-auto"></div></div></div>```bash
sudo mkdir -p /opt/ssp/{conf,data/ratelimit,data/postfix-spool}
cd /opt/ssp
```

Copie os arquivos `docker-compose.yml` e `conf/config.inc.local.php` (anexos) para dentro dessa estrutura.

---

## 3. docker-compose.yml

Veja o arquivo `docker-compose.yml` anexo. Pontos-chave:

- Monta `./conf` em `/var/www/conf` (caminho oficial de configuração da imagem).
- Monta `./data/ratelimit` em `/var/www/ratelimit`, um diretório **gravável** para persistir o controle de bloqueio por tentativas entre reinícios do container (por padrão ele usaria `/tmp`, que é perdido a cada restart).
- Sobe também o serviço `postfix` (imagem `boky/postfix`), na mesma rede `ssp_net` — é para ele que o SSP manda os e-mails de recuperação (detalhes na seção 8).
- Publica apenas `0.0.0.0:8090:80` do serviço `ssp` — a aplicação só fica acessível localmente; quem expõe para a rede é o Nginx com TLS. O Postfix não publica nenhuma porta para o host, só é alcançável de dentro da rede `ssp_net`.

Ajuste permissões do diretório de rate-limit para o usuário do Apache dentro do container (normalmente `www-data`, uid `33` na imagem padrão; `82` na `alpine-latest`):

<div aria-label="Código bash" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="67:1-72:4;2803-2985" id="bkmrk-bash-0" role="group" tabindex="0"><div aria-label="Código bash" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="67:1-72:4;2803-2985" role="group" tabindex="0"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">  
</div></div><div class="text-text-500 font-small p-3.5 pb-0">bash</div><div class="overflow-x-auto"></div></div></div>```bash
# confira o uid depois de subir o container:
docker exec -it ssp id www-data
# depois ajuste no host, por exemplo para uid 33:
sudo chown -R 33:33 /opt/ssp/data/ratelimit
```

Subir o serviço:

<div aria-label="Código bash" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="76:1-80:4;3005-3072" id="bkmrk-bash-1" role="group" tabindex="0"><div aria-label="Código bash" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="76:1-80:4;3005-3072" role="group" tabindex="0"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">  
</div></div><div class="text-text-500 font-small p-3.5 pb-0">bash</div><div class="overflow-x-auto"></div></div></div>```bash
cd /opt/ssp
docker compose up -d
docker compose logs -f
```

---

## 4. Configuração do SSP (`conf/config.inc.local.php`)

Veja o arquivo `config.inc.local.php` anexo — já preenchido com os dados que você passou. **Antes de subir em produção, ajuste obrigatoriamente:**

<div class="overflow-x-auto w-full px-2 mb-6 print:overflow-x-visible" data-sourcepos="89:1-94:94;3284-3784" dir="ltr" id="bkmrk-par%C3%A2metro-o-que-faze"><table class="min-w-full border-collapse text-sm leading-[1.7] whitespace-normal"><thead class="text-left"><tr><th class="text-text-100 border-b-0.5 border-[hsl(var(--border-300)/0.6)] py-2 pr-4 align-top font-bold" scope="col">Parâmetro</th><th class="text-text-100 border-b-0.5 border-[hsl(var(--border-300)/0.6)] py-2 pr-4 align-top font-bold" scope="col">O que fazer</th></tr></thead><tbody><tr><td class="border-b-0.5 border-[hsl(var(--border-300)/0.3)] py-2 pr-4 align-top">`$ldap_bindpw`</td><td class="border-b-0.5 border-[hsl(var(--border-300)/0.3)] py-2 pr-4 align-top">Senha real da conta `cn=admin,dc=slt,dc=ifsp,dc=local`. **Não deixe em texto puro no repositório** — veja a nota de segurança abaixo.</td></tr><tr><td class="border-b-0.5 border-[hsl(var(--border-300)/0.3)] py-2 pr-4 align-top">`$ldap_filter`</td><td class="border-b-0.5 border-[hsl(var(--border-300)/0.3)] py-2 pr-4 align-top">Confirme o `objectClass` real dos usuários no seu diretório (`posixAccount`, `inetOrgPerson`, etc.)</td></tr><tr><td class="border-b-0.5 border-[hsl(var(--border-300)/0.3)] py-2 pr-4 align-top">`$mail_attributes`</td><td class="border-b-0.5 border-[hsl(var(--border-300)/0.3)] py-2 pr-4 align-top">Confirme o nome do atributo de e-mail no schema (geralmente `mail`)</td></tr><tr><td class="border-b-0.5 border-[hsl(var(--border-300)/0.3)] py-2 pr-4 align-top">—</td><td class="border-b-0.5 border-[hsl(var(--border-300)/0.3)] py-2 pr-4 align-top">(nenhum pendente no relay de e-mail — credencial já confirmada e preenchida, seção 8)</td></tr></tbody></table>

</div>### Sobre `$ldap_binddn` / `$who_change_password = "manager"`

Como a recuperação por e-mail (token) precisa trocar a senha **sem o usuário informar a senha antiga**, o SSP precisa de uma conta com poderes de administrador — por isso usamos `cn=admin,...` como bind e `$who_change_password = "manager"`. Isso é obrigatório para a funcionalidade de recuperação por e-mail funcionar.

### Nota de segurança sobre a senha do admin LDAP

Evite deixar `$ldap_bindpw` em texto puro no arquivo versionado. Duas opções:

1. Restrinja permissões do arquivo (`chmod 640`, dono root, grupo do serviço) e mantenha fora de qualquer repositório Git.
2. Ou use uma conta de serviço LDAP dedicada, com permissão apenas de escrita no atributo de senha (`userPassword`) dos usuários da OU relevante — mais seguro do que usar o `cn=admin` completo. Recomendo fortemente essa segunda opção para produção.

---

## 5. Captcha

Já habilitado no `config.inc.local.php`:

<div aria-label="Código php" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="114:1-117:4;4731-4798" id="bkmrk-php" role="group" tabindex="0"><div aria-label="Código php" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="114:1-117:4;4731-4798" role="group" tabindex="0"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">  
</div></div><div class="text-text-500 font-small p-3.5 pb-0">php</div><div class="overflow-x-auto"></div></div></div>```php
$use_captcha = true;
$captcha_class = "InternalCaptcha";
```

O `InternalCaptcha` é gerado localmente via `php-gd` (já incluso na imagem oficial) e **não depende de nenhum serviço externo** — ideal para uma rede interna do campus. Ele aparece em todos os formulários (troca de senha, token de recuperação, perguntas). Se no futuro quiser Google reCAPTCHA ou Friendly Captcha, basta trocar `$captcha_class` — os parâmetros extras estão comentados no fim do arquivo de config para referência.

---

## 6. Recuperação de senha por e-mail

Já configurado:

<div aria-label="Código php" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="132:1-138:4;5291-5591" id="bkmrk-php-0" role="group" tabindex="0"><div aria-label="Código php" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="132:1-138:4;5291-5591" role="group" tabindex="0"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">  
</div></div><div class="text-text-500 font-small p-3.5 pb-0">php</div><div class="overflow-x-auto"></div></div></div>```php
$use_tokens = true;
$mail_address_use_ldap = true;   // busca o e-mail direto no LDAP, não pergunta ao usuário
$crypt_tokens = true;
$token_lifetime = "3600";        // link válido por 1h
$obscure_usernotfound_sendtoken = true;  // não revela se o login existe (evita enumeração de contas)
```

Fluxo: usuário informa o login → SSP busca o e-mail cadastrado no atributo `mail` do LDAP → envia um link com token → usuário clica e define nova senha.

---

## 7. Bloqueio por tentativas

O SSP tem um mecanismo de rate limit por login **e** por IP:

<div aria-label="Código php" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="149:1-155:4;5845-6162" id="bkmrk-php-1" role="group" tabindex="0"><div aria-label="Código php" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="149:1-155:4;5845-6162" role="group" tabindex="0"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">  
</div></div><div class="text-text-500 font-small p-3.5 pb-0">php</div><div class="overflow-x-auto"></div></div></div>```php
$use_ratelimit = true;
$ratelimit_dbdir = '/var/www/ratelimit';  // volume persistente (seção 3)
$max_attempts_per_user = 3;   // bloqueia após 3 tentativas do mesmo login
$max_attempts_per_ip = 5;     // bloqueia após 5 tentativas do mesmo IP
$max_attempts_block_seconds = "300";  // bloqueio de 5 minutos
```

Isso cobre a troca de senha, o envio de token por e-mail e (se usado) o envio por SMS/perguntas — protegendo contra força bruta e contra abuso do envio de e-mails de recuperação.

**Importante:** esse controle vive em arquivos temporários dentro do container (por isso montamos um volume dedicado). Se você rodar múltiplas réplicas do SSP atrás de um load balancer no futuro, vai precisar de sessões persistentes (sticky sessions) ou migrar para outra estratégia — não é o caso aqui com um único container.

Um bloqueio complementar, no lado do LDAP (via overlay `ppolicy` do OpenLDAP, atributo `pwdFailureTime`), pode ser adicionado depois habilitando `$ldap_use_ppolicy_control = true;` — mas isso exige configurar o overlay `ppolicy` no próprio servidor `ldap.slt.ifsp.edu.br`, o que está fora do escopo desta VM. O rate limit do SSP já atende ao requisito pedido.

---

## 8. Integração com Postfix (também em container)

O `docker-compose.yml` já inclui um segundo serviço, `postfix`, usando a imagem `boky/postfix` — um relay Postfix "pronto para uso" pensado exatamente para rodar ao lado de outra aplicação em Docker. Ele **não** expõe porta nenhuma para fora: o container `ssp` fala com ele direto pelo nome do serviço (`postfix`) dentro da rede `ssp_net`.

O relay externo já está configurado com os dados oficiais do [manual de e-mail do IFSP — "Configurações SMTP para e-mail de sistemas (não-responda.ifsp.edu.br)"](https://manuais.ifsp.edu.br/books/e-mail/page/configuracoes-smtp-para-e-mail-de-sistemas-nao-respondaifspedubr):

<div class="overflow-x-auto w-full px-2 mb-6 print:overflow-x-visible" data-sourcepos="187:1-192:32;7707-8028" dir="ltr" id="bkmrk-par%C3%A2metro-valor-host"><table class="min-w-full border-collapse text-sm leading-[1.7] whitespace-normal"><thead class="text-left"><tr><th class="text-text-100 border-b-0.5 border-[hsl(var(--border-300)/0.6)] py-2 pr-4 align-top font-bold" scope="col">Parâmetro</th><th class="text-text-100 border-b-0.5 border-[hsl(var(--border-300)/0.6)] py-2 pr-4 align-top font-bold" scope="col">Valor</th></tr></thead><tbody><tr><td class="border-b-0.5 border-[hsl(var(--border-300)/0.3)] py-2 pr-4 align-top">Host</td><td class="border-b-0.5 border-[hsl(var(--border-300)/0.3)] py-2 pr-4 align-top">`nao-responda.ifsp.edu.br`</td></tr><tr><td class="border-b-0.5 border-[hsl(var(--border-300)/0.3)] py-2 pr-4 align-top">Porta</td><td class="border-b-0.5 border-[hsl(var(--border-300)/0.3)] py-2 pr-4 align-top">`587` (STARTTLS) — preferida pelo manual; `465` (SSL/TLS implícito) é o fallback</td></tr><tr><td class="border-b-0.5 border-[hsl(var(--border-300)/0.3)] py-2 pr-4 align-top">Autenticação</td><td class="border-b-0.5 border-[hsl(var(--border-300)/0.3)] py-2 pr-4 align-top">usuário `slt` (confirmado pelo sistema legado `email.class.php`, que já enviava com sucesso por esse relay)</td></tr><tr><td class="border-b-0.5 border-[hsl(var(--border-300)/0.3)] py-2 pr-4 align-top">Segurança</td><td class="border-b-0.5 border-[hsl(var(--border-300)/0.3)] py-2 pr-4 align-top">TLS obrigatório</td></tr></tbody></table>

</div>### O que já está configurado no `docker-compose.yml`

<div aria-label="Código yaml" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="196:1-216:4;8085-8634" id="bkmrk-yaml" role="group" tabindex="0"><div aria-label="Código yaml" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="196:1-216:4;8085-8634" role="group" tabindex="0"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">  
</div></div><div class="text-text-500 font-small p-3.5 pb-0">yaml</div><div class="overflow-x-auto"></div></div></div>```yaml
postfix:
  image: boky/postfix:latest
  environment:
    - ALLOWED_SENDER_DOMAINS=nao-responda.ifsp.edu.br
    - POSTFIX_myhostname=ssp-postfix.slt.ifsp.edu.br
    - RELAYHOST=[nao-responda.ifsp.edu.br]:587
    - RELAYHOST_USERNAME=slt
    - RELAYHOST_PASSWORD_FILE=/run/secrets/relayhost_password
    - POSTFIX_smtp_tls_security_level=encrypt
  secrets:
    - relayhost_password
  volumes:
    - ./data/postfix-spool:/var/spool/postfix
  networks:
    - ssp_net

secrets:
  relayhost_password:
    file: ./secrets/relayhost_password.txt
```

- `ALLOWED_SENDER_DOMAINS=nao-responda.ifsp.edu.br`: precisa bater com o domínio da conta autenticada — é esse domínio que o relay aceita como remetente válido vindo deste Postfix.
- **Senha da conta `slt`** — já preenchida no `secrets/relayhost_password.txt` anexo (extraída do `email.class.php` do sistema legado). Ela fica fora do `docker-compose.yml` (que pode ir para um repositório) e é passada via **Docker secret**:

<div aria-label="Código bash" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="225:1-231:4;9070-9284" id="bkmrk-bash-2" role="group" tabindex="0"><div aria-label="Código bash" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="225:1-231:4;9070-9284" role="group" tabindex="0"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">  
</div></div><div class="text-text-500 font-small p-3.5 pb-0">bash</div><div class="overflow-x-auto"></div></div></div>```bash
  cd /opt/ssp
  mkdir -p secrets
  # copie o secrets/relayhost_password.txt anexo para dentro dessa pasta
  chmod 600 secrets/relayhost_password.txt
  echo "secrets/relayhost_password.txt" >> .gitignore
```

> **Importante:** essa senha está em texto puro no arquivo — trate como qualquer credencial de produção. Restrinja o acesso ao diretório `/opt/ssp/secrets` (só root), nunca comite em repositório público, e considere pedir à equipe de e-mail do IFSP para rotacioná-la já que estava hardcoded num sistema legado (`email.class.php`) — se aquele arquivo circulou por outros lugares, a senha pode já estar exposta.

- **Porta 465 (fallback)**: se a 587 estiver bloqueada na rede do campus, troque `RELAYHOST=[nao-responda.ifsp.edu.br]:465` e adicione mais uma variável (a imagem `boky/postfix` aceita qualquer opção do Postfix via `POSTFIX_<nome>`):

<div aria-label="Código yaml" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="242:1-244:4;9957-10005" id="bkmrk-yaml-0" role="group" tabindex="0"><div aria-label="Código yaml" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="242:1-244:4;9957-10005" role="group" tabindex="0"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">  
</div></div><div class="text-text-500 font-small p-3.5 pb-0">yaml</div><div class="overflow-x-auto"></div></div></div>```yaml
  - POSTFIX_smtp_tls_wrappermode=yes
```

(465 é SSL/TLS implícito desde a conexão — sem esse modo o Postfix tentaria STARTTLS na 465 e falharia.)

- `mynetworks` já cobre por padrão as faixas privadas usuais do Docker (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`), então normalmente não é preciso mexer nisso.

### Alinhamento do remetente (`$mail_from`)

Já ajustado no `config.inc.local.php` anexo:

<div aria-label="Código php" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="255:1-257:4;10378-10433" id="bkmrk-php-2" role="group" tabindex="0"><div aria-label="Código php" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="255:1-257:4;10378-10433" role="group" tabindex="0"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">  
</div></div><div class="text-text-500 font-small p-3.5 pb-0">php</div><div class="overflow-x-auto"></div></div></div>```php
$mail_from = "slt@nao-responda.ifsp.edu.br";
```

Servidores institucionais (o IFSP usa Zimbra) costumam rejeitar ou sobrescrever o campo "From" quando ele não corresponde à conta autenticada no SMTP, a menos que exista uma permissão de "enviar como" liberada para outro endereço. Se quiserem manter um remetente mais amigável como `senha@slt.ifsp.edu.br`, é preciso confirmar com a equipe de e-mail do IFSP se essa permissão pode ser concedida para a conta `slt@nao-responda.ifsp.edu.br`.

### Subindo e testando

<div aria-label="Código bash" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="268:1-272:4;10900-10975" id="bkmrk-bash-3" role="group" tabindex="0"><div aria-label="Código bash" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="268:1-272:4;10900-10975" role="group" tabindex="0"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">  
</div></div><div class="text-text-500 font-small p-3.5 pb-0">bash</div><div class="overflow-x-auto"></div></div></div>```bash
cd /opt/ssp
docker compose up -d
docker compose logs -f postfix
```

Teste de envio isolado, de dentro do container SSP:

<div aria-label="Código bash" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="276:1-280:4;11030-11291" id="bkmrk-bash-4" role="group" tabindex="0"><div aria-label="Código bash" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="276:1-280:4;11030-11291" role="group" tabindex="0"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">  
</div></div><div class="text-text-500 font-small p-3.5 pb-0">bash</div><div class="overflow-x-auto"></div></div></div>```bash
docker compose exec ssp sh -c "apk add --no-cache curl 2>/dev/null; \
  echo -e 'Subject: Teste SSP\n\nMensagem de teste' | \
  curl -s smtp://postfix:587 --mail-from slt@nao-responda.ifsp.edu.br --mail-rcpt seuemail@slt.ifsp.edu.br --upload-file -"
```

Ou, mais simples, direto no container do Postfix:

<div aria-label="Código bash" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="284:1-287:4;11344-11479" id="bkmrk-bash-5" role="group" tabindex="0"><div aria-label="Código bash" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="284:1-287:4;11344-11479" role="group" tabindex="0"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">  
</div></div><div class="text-text-500 font-small p-3.5 pb-0">bash</div><div class="overflow-x-auto"></div></div></div>```bash
docker compose exec postfix sh -c "echo 'Teste SSP' | mail -s 'Teste' seuemail@slt.ifsp.edu.br"
docker compose logs postfix
```

Se a autenticação falhar, o log do Postfix mostra o motivo (usuário/senha incorretos, TLS recusado, etc.) — confira `docker compose logs postfix` logo após o teste.

### Como o container SSP acessa o Postfix

No `config.inc.local.php` (já ajustado no arquivo anexo):

<div aria-label="Código php" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="297:1-301:4;11749-11969" id="bkmrk-php-3" role="group" tabindex="0"><div aria-label="Código php" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="297:1-301:4;11749-11969" role="group" tabindex="0"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">  
</div></div><div class="text-text-500 font-small p-3.5 pb-0">php</div><div class="overflow-x-auto"></div></div></div>```php
$mail_smtp_host = 'postfix';   // nome do serviço no docker-compose.yml
$mail_smtp_port = 587;         // porta de submission da imagem boky/postfix (interna, entre os dois containers)
$mail_smtp_auth = false;
```

Essa porta 587 é **interna** (SSP → container Postfix, sem senha, protegida só pela rede Docker) — não confundir com a porta 587 do relay externo (`nao-responda.ifsp.edu.br`, essa sim autenticada com TLS).

Como os dois containers estão na mesma rede `ssp_net`, o Docker resolve o nome `postfix` automaticamente via DNS interno — não é preciso IP fixo nem `extra_hosts`.

### Persistência da fila

O volume `./data/postfix-spool` guarda a fila de e-mails do Postfix entre reinícios do container (evita perder mensagens em trânsito num restart). Crie o diretório antes de subir:

<div aria-label="Código bash" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="317:1-319:4;12550-12598" id="bkmrk-bash-6" role="group" tabindex="0"><div aria-label="Código bash" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100" data-sourcepos="317:1-319:4;12550-12598" role="group" tabindex="0"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10">  
</div></div><div class="text-text-500 font-small p-3.5 pb-0">bash</div><div class="overflow-x-auto"></div></div></div>```bash
mkdir -p /opt/ssp/data/postfix-spool
```