danny-avila / LibreChat

Enhanced ChatGPT Clone: Features Anthropic, AWS, OpenAI, Assistants API, Azure, Groq, o1, GPT-4o, Mistral, OpenRouter, Vertex AI, Gemini, Artifacts, AI model switching, message search, langchain, DALL-E-3, ChatGPT Plugins, OpenAI Functions, Secure Multi-User System, Presets, completely open-source for self-hosting. Actively in public development.
https://librechat.ai/
MIT License
18.96k stars 3.15k forks source link

[Bug]: Unsigned email .env doesn't work as intended #2142

Closed KHaririNL closed 7 months ago

KHaririNL commented 7 months ago

What happened?

If I put in the .env "EMAIL_ALLOW_SELFSIGNED=true ", I expect the emailer to work on selfsigned certs. THis however doesn;'t work;

2024-03-20 00:43:34 error: [sendEmail] self-signed certificate

This is because in sendEmail.js , this line:

` // Whether to accept unsigned certificates

    rejectUnauthorized: process.env.EMAIL_ALLOW_SELFSIGNED === 'true',`

It should be set to false, so that it will reject unauthorized will be set to TRUE, in the case self signed is not allowed

Steps to Reproduce

  1. Put email_allowed_selfsigned = true
  2. try password reset

What browsers are you seeing the problem on?

No response

Relevant log output

No response

Screenshots

No response

Code of Conduct

danny-avila commented 7 months ago

Can you try manually editing the file like this?

const fs = require('fs');
const path = require('path');
const nodemailer = require('nodemailer');
const handlebars = require('handlebars');
const { isEnabled } = require('~/server/utils');
const logger = require('~/config/winston');

const sendEmail = async (email, subject, payload, template) => {
  try {
    const transporterOptions = {
      // Use STARTTLS by default instead of obligatory TLS
      secure: process.env.EMAIL_ENCRYPTION === 'tls',
      // If explicit STARTTLS is set, require it when connecting
      requireTls: process.env.EMAIL_ENCRYPTION === 'starttls',
      tls: {
        // Whether to accept unsigned certificates
        rejectUnauthorized: isEnabled(process.env.EMAIL_ALLOW_SELFSIGNED),
      },
      auth: {
        user: process.env.EMAIL_USERNAME,
        pass: process.env.EMAIL_PASSWORD,
      },
    };

    if (process.env.EMAIL_ENCRYPTION_HOSTNAME) {
      // Check the certificate against this name explicitly
      transporterOptions.tls.servername = process.env.EMAIL_ENCRYPTION_HOSTNAME;
    }

    // Mailer service definition has precedence
    if (process.env.EMAIL_SERVICE) {
      transporterOptions.service = process.env.EMAIL_SERVICE;
    } else {
      transporterOptions.host = process.env.EMAIL_HOST;
      transporterOptions.port = process.env.EMAIL_PORT ?? 25;
    }

    const transporter = nodemailer.createTransport(transporterOptions);

    const source = fs.readFileSync(path.join(__dirname, 'emails', template), 'utf8');
    const compiledTemplate = handlebars.compile(source);
    const options = () => {
      return {
        // Header address should contain name-addr
        from:
          `"${process.env.EMAIL_FROM_NAME || process.env.APP_TITLE}"` +
          `<${process.env.EMAIL_FROM}>`,
        to: `"${payload.name}" <${email}>`,
        envelope: {
          // Envelope from should contain addr-spec
          // Mistake in the Nodemailer documentation?
          from: process.env.EMAIL_FROM,
          to: email,
        },
        subject: subject,
        html: compiledTemplate(payload),
      };
    };

    // Send email
    transporter.sendMail(options(), (error, info) => {
      if (error) {
        logger.error('[sendEmail]', error);
        return error;
      } else {
        logger.debug('[sendEmail]', info);
        return info;
      }
    });
  } catch (error) {
    logger.error('[sendEmail]', error);
    return error;
  }
};

module.exports = sendEmail;

Unless I'm misunderstanding this, that should fix it in case there is a discrepancy of expected type values.

danny-avila commented 7 months ago

or do you mean it should be negated to be the opposite value of EMAIL_ALLOW_SELFSIGNED so EMAIL_ALLOW_SELFSIGNED !== 'true' ?

I didn't implement email handling so I don't have a ready environment for testing.

KHaririNL commented 7 months ago

Hi, yes, I fixed it by putting it like this:

rejectUnauthorized: process.env.EMAIL_ALLOW_SELFSIGNED === 'false',

To put it in a different way, if you put rejectUnauthorized: true, it means that self-signed certificates should not be allowed. This means, that EMAIL_ALLOW_SELFSIGNED is set to false

danny-avila commented 7 months ago

Hi, yes, I fixed it by putting it like this:

rejectUnauthorized: process.env.EMAIL_ALLOW_SELFSIGNED === 'false',

To put it in a different way, if you put rejectUnauthorized: true, it means that self-signed certificates should not be allowed. This means, that EMAIL_ALLOW_SELFSIGNED is set to false

Thanks for reporting this, the fix is merged to main; if relying on docker images, give it 20 min.