Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IPA-EPN: First version. #4676

Closed
wants to merge 4 commits into from
Closed

IPA-EPN: First version. #4676

wants to merge 4 commits into from

Conversation

fcami
Copy link
Contributor

@fcami fcami commented May 8, 2020

EPN stands for Expiring Password Notification. It is a standalone
tool designed to build a list of users whose password would expire
in the near future, and either display the list in a machine-readable
format, or send email notifications to these users.

EPN provides command-line options to display the list of affected users.
This provides data introspection and helps understand how many emails
would be sent for a given day, or a given date range.
The command-line options can also be used by a monitoring system to alert
whenever a number of emails over the SMTP quota would be sent.

EPN is meant to be launched once a day from an IPA client (preferred)
or replica from a systemd timer.

EPN does not keep state. The list of affected users is built at runtime
but never kept.

TLS/STARTTLS SMTP code is untested and unlikely to work as-is.

Parts of code contributed by Rob Crittenden.
Ideas and feedback contributed by Christian Heimes and Michal Polovka.

Fixes: https://pagure.io/freeipa/issue/3687

@fcami fcami added WIP Work in progress - not ready yet for review ipa-4-8 Mark for backport to ipa 4.8 labels May 8, 2020
@rcritten rcritten self-assigned this May 8, 2020
@rcritten
Copy link
Contributor

rcritten commented May 8, 2020

Note to self: something to be investigated:
[08/May/2020:14:46:09.221690605 -0400] conn=63 op=1 SRCH base="cn=users,cn=accounts,dc=example,dc=test" scope=2 filter="(&(!(nsAccountLock=TRUE))(krbPasswordExpiration<=20200515000000Z)(krbPasswordExpiration>=20200514000000Z))" attrs="krbPasswordExpiration mail cn uid"
[08/May/2020:14:46:09.222218146 -0400] conn=63 op=1 RESULT err=0 tag=101 nentries=1 etime=0.000655780 notes=U details="Partially Unindexed Filter

@fcami
Copy link
Contributor Author

fcami commented May 8, 2020

/AzurePipelines run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@rcritten rcritten added needs review Pull Request is waiting for a review and removed WIP Work in progress - not ready yet for review labels May 12, 2020
@rcritten
Copy link
Contributor

While not 100% feature complete this patch represents the majority of the minimal viable product of sending expiring e-mail.

I'll squash the patches together to clean up the history but doing it now would mess up PR-CI's understanding of the test patch.

Left to do for MVP:

  • log rotation for epn logs (and I may rename it from ipaepn.log to ipa-epn.log for consistency
  • add a configurable delay between mails sent to not spam the queue
  • add a max number of emails to send but this will require saving state and since epn only runs once a day by default could fall behind. This may not make MVP.
  • enable and document TLS and STARTTLS support

@flo-renaud
Copy link
Contributor

The azure pipeline failure should be fixed with a rebase including b6fbee53

@rcritten
Copy link
Contributor

/AzurePipelines run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

ipaclient/install/ipa_epn.py Show resolved Hide resolved
@@ -0,0 +1,5 @@
Hi {{ fullname }},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about your turn the template into a RFC 822 message and parse the output of JINJA with email.message_from_string?

This would allow an admin to specify email headers including subject or custom headers in the template. I did a quick test. The parser automatically converts headers with non-ASCII char.

>>> text = """\
... Subject: Blä
... 
... Test äöü
... """
>>> print(email.message_from_string(text).as_string())
Subject: =?utf-8?q?Bl=C3=A4?=

Test äöü

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future EPN could also look at the language and locale attribute of the user and choose a language specific template (e.g. expire_msg.fr.template). With my proposal you have the subject and body in one file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it'd be nice to have a configurable subject. Francois took the approach of having the body of the message be an attachment so the encoding is automatic. Arguably this will make it difficult for text-based readers. He did want to allow html-based e-mails which is why there is the content subtype (defaults to plain). We could add subject as an option in the config file, and may allso allow X- headers to be defined.

I'm not sure about separate templates. We don't have information on the target other than what is in LDAP and we don't currently maintain language.

self._msg["From"] = formataddr(("IPA-EPN", mail_from))
self._msg["To"] = ", ".join(self._subscribers)
self._msg["Date"] = formatdate(localtime=True)
self._msg["Subject"] = Header(self._subject, self._charset)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend to include a Message-ID. MTAs tend to dislike and block messages without a proper id. You can use email.utils.make_msgid to generate one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

postfix is generating a message-id for us:

Message-Id: 20200515145329.668C8389A20E@ipa.example.test

We are trying to be agnostic about the MTA so I suppose adding it would be fine.

@rcritten
Copy link
Contributor

Added patch to include message-id, configurable subject and better template error handling.

@rcritten
Copy link
Contributor

So I don't lose this, Christian suggested moving some of the options into new sections. The current ipa config only supports a single section, global. He provided a candidate patch to look in all sections.

diff --git a/ipalib/config.py b/ipalib/config.py
index 4c7ad4920..c3afbed31 100644
--- a/ipalib/config.py
+++ b/ipalib/config.py
@@ -378,13 +378,22 @@ class Env:
         if not parser.has_section(CONFIG_SECTION):
             parser.add_section(CONFIG_SECTION)
         items = parser.items(CONFIG_SECTION)
-        if len(items) == 0:
-            return 0, 0
         i = 0
         for (key, value) in items:
             if key not in self:
                 self[key] = value
                 i += 1
+
+        # also load other section and map the entries to section_key = value
+        for section in parser.sections():
+            if section == CONFIG_SECTION:
+                pass
+            for key, value in parser.items(section):
+                key = f"{section}_{key}"
+                if key not in self:
+                    self[key] = value
+                    i += 1
+
         if 'config_loaded' not in self: # we loaded at least 1 file
             self['config_loaded'] = True
         return i, len(items)

I'm not sure what if any confusion this would cause. It could be difficult to troubleshoot since all the sections are treated equally and with the ipa config reader the first one "wins".

@rcritten
Copy link
Contributor

Things I need help with. At this point I've worked on it enough that I'm a bit biased.

  • The name of the log file. /var/log/ipaepn.log doesn't fit the model of ipa-*.log (see next)
  • Should we configure log rotation? I think probably so, but it's going to require crond and more effort from users when configuring. We could create /var/log/epn/something.log
  • Is the current logging reasonable enough for auditing, debugging, etc?
  • I fixed starttls/ssl modes and have unit tests for them but the tests can't pass because of the package installation bug. Worth pushing in the client-side changes without the tests or wait? (I'm still working on the ssl part, it's quite a lot of config to do)

A sample of an e-mail sent out via postfix is:

From root@localhost  Fri May 15 11:49:43 2020
Return-Path: <root@localhost>
X-Original-To: tuser1@example.test
Delivered-To: tuser1@example.test
Received: from ipa.example.test (localhost [IPv6:::1])
        by ipa.example.test (Postfix) with ESMTP id 644B2389A21D
        for <tuser1@example.test>; Fri, 15 May 2020 11:49:43 -0400 (EDT)
Content-Type: multipart/mixed; -charset="utf8"; boundary="===============1923913668708037366=="
MIME-Version: 1.0
From: IPA-EPN <noreply@example.test>
To: tuser1@example.test
Date: Fri, 15 May 2020 11:49:43 -0400
Subject: =?utf8?q?Your_password_will_expire_soon=2E?=
Message-Id: <158955778339.27665.7541123698243046586@ipa.example.test>
X-Mailer: IPA-EPN

Multipart message
--===============1923913668708037366==
Content-Type: text/plain; charset="utf8"
MIME-Version: 1.0
Content-Transfer-Encoding: base64

U3ViamVjdDogWW91ciBwYXNzd29yZCB3aWxsIGV4cGlyZSBzb29uClgtTWFpbGVyOiBmb28KCkhp
ICBVc2VyLApZb3VyIGxvZ2luIGVudHJ5IHR1c2VyMSBpcyBnb2luZyB0byBleHBpcmUgb24KMjAy
MC0wNS0yMiAxNDozMzo1My4gUGxlYXNlIGNoYW5nZSBpdCBzb29uLgoKWW91ciBmcmllbmRseSBu
ZWlnaGJvcmhvb2QgYWRtaW5zLgoK

--===============1923913668708037366==--

@rcritten
Copy link
Contributor

@tiran suggested moving ipa_epn.py into its own subdirectory inside ipaclient. That results in:

ipaclient/epn/ipa_epn.py:42: [W9901(ipa-forbidden-import), ] Forbidden import ipaclient.install.client.is_ipa_client_installed (can't import from ipaclient.install in ipaclient))
ipaclient/epn/ipa_epn.py:45: [W9901(ipa-forbidden-import), ] Forbidden import ipalib.install.sysrestore (can't import from ipalib.install in ipaclient))

@rcritten
Copy link
Contributor

I squashed from 21 to 12 patches and moved the temp commit to the top of the queue to verify that I got the rebases correct.

@rcritten
Copy link
Contributor

Tests pass, removing temp commit per @tiran

@rcritten rcritten added the re-run Trigger a new run of PR-CI label May 21, 2020
@freeipa-pr-ci freeipa-pr-ci removed the re-run Trigger a new run of PR-CI label May 21, 2020
@tiran
Copy link
Member

tiran commented May 22, 2020

I just realized that the PR introduces a new top level directory tools. I find it confusing to have a new tools directory for client tools while server-side tools are in install/tools.

@rcritten
Copy link
Contributor

If desired I can move ipa-epn into client and rename to ipa-epn.in so it gets processed like the other client tools and the man pages into client/man.

ipaclient/install/ipa_epn.py Outdated Show resolved Hide resolved
@fcami
Copy link
Contributor Author

fcami commented Jun 4, 2020

hi @tiran I think I have addressed all your concerns, and squashed commits into manageable chunks. Could you please review a final time?

except Exception:
# postfix isn't yet used so this shouldn't cause existing
# tests to fail.
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to do away with this try/except. This was to workaround the PR-CI issue where packages weren't being installed properly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually PR-CI is not fixed yet.
Failed run without the try/except:
http://freeipa-org-pr-ci.s3-website.eu-central-1.amazonaws.com/jobs/983787d0-a703-11ea-bfc3-fa163e401a00/report.html
There is a proposed fix from @wladich in:
freeipa/freeipa-pr-ci#363

Copy link

@wladich wladich Jun 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fcami My PR freeipa/freeipa-pr-ci#373 is not about fixing dnf issue, it is about fixing other issue (resolv.conf overwritten by NetworkManager) without breaking the dnf on master.
The dnf issue was expected to be resolved by freeipa/freeipa-pr-ci#367 which reverted my previous attempt to solve aforementioned problem with resolv.conf.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wladich argh, I mixed things up. Do you know if the current runners are up to date?
Because that's what I get (on a client):

[ipatests.pytest_ipa.integration.host.Host.master.cmd48] Error: Error downloading packages:
[ipatests.pytest_ipa.integration.host.Host.master.cmd48]   Curl error (6): Couldn't resolve host name for https://mirrors.fedoraproject.org/metalink?repo=updates-released-f32&arch=x86_64 [Could not resolve host: mirrors.fedoraproject.org]
[ipatests.pytest_ipa.integration.host.Host.master.cmd48] Exit code: 1
ipa: ERROR: stderr: Error: Error downloading packages:
  Curl error (6): Couldn't resolve host name for https://mirrors.fedoraproject.org/metalink?repo=updates-released-f32&arch=x86_64 [Could not resolve host: mirrors.fedoraproject.org]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's strange is that ipa-healthcheck similarly installs itself during its testing and that is passing. I wonder if the timing of the package install is different between the tests (e.g. before/after ipa-server-install).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch... Let me try changing that

@wladich
Copy link

wladich commented Jun 5, 2020

@fcami
Unfortunately I do not know, I think we should ask @netoarmando

@fcami
Copy link
Contributor Author

fcami commented Jun 5, 2020

For reference, installing packages before installing IPA provides a good run.

client/man/epn.conf.5 Outdated Show resolved Hide resolved
client/man/epn.conf.5 Outdated Show resolved Hide resolved
client/man/ipa-epn.1 Outdated Show resolved Hide resolved
client/man/ipa-epn.1 Outdated Show resolved Hide resolved
client/man/ipa-epn.1 Outdated Show resolved Hide resolved
freeipa.spec.in Outdated Show resolved Hide resolved
init/systemd/ipa-epn.service.in Outdated Show resolved Hide resolved
init/systemd/ipa-epn.timer.in Outdated Show resolved Hide resolved
ipaclient/install/ipa_epn.py Show resolved Hide resolved
ipaclient/install/ipa_epn.py Show resolved Hide resolved
Copy link
Member

@miskopo miskopo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apart from aforementioned comments, nice! 😸

@miskopo miskopo self-requested a review June 8, 2020 16:10
Copy link
Member

@miskopo miskopo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, see inline comments

fcami and others added 4 commits June 8, 2020 22:59
EPN stands for Expiring Password Notification. It is a standalone
tool designed to build a list of users whose password would expire
in the near future, and either display the list in a machine-readable
format, or send email notifications to these users.

EPN provides command-line options to display the list of affected users.
This provides data introspection and helps understand how many emails
would be sent for a given day, or a given date range.
The command-line options can also be used by a monitoring system to alert
whenever a number of emails over the SMTP quota would be sent.

EPN is meant to be launched once a day from an IPA client (preferred)
or replica from a systemd timer.

EPN does not keep state. The list of affected users is built at runtime
but never kept.

TLS/STARTTLS SMTP code is untested and unlikely to work as-is.

Parts of code contributed by Rob Crittenden.
Ideas and feedback contributed by Christian Heimes and Michal Polovka.

Fixes: https://pagure.io/freeipa/issue/3687
Signed-off-by: François Cami <fcami@redhat.com>
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Initial test suite for EPN.

Fixes: https://pagure.io/freeipa/issue/3687
Signed-off-by: François Cami <fcami@redhat.com>
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Add options for character set (default utf8) and message
subtype (default plain). This will allow for more control
for users to do either HTML mail or use ascii for the character
set so the attachment is not base64-encoded to make it easier
for all mail clients.

Collect first and last name as well for each user in order to
provide more options for the template engine.

Make the From address configurable, defaulting to noreply@ipa_domain
Make Subject configurable too.

Don't rely on the MTA to set Message-Id: set it using the email
module.

Fixes: https://pagure.io/freeipa/issue/3687
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Expiring Password Notifications search for expiring passwords
between dates. Add an equality index for this attribute.

https://pagure.io/freeipa/issue/3687
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
@fcami
Copy link
Contributor Author

fcami commented Jun 8, 2020

hi @miskopo I've addressed all your concerns I think.
Please let me know if I can remove the temp commit to get a complete gating run.

@miskopo
Copy link
Member

miskopo commented Jun 8, 2020

@fcami it looks good, please remove the temp commit, I'll ACK in the morning.

@fcami
Copy link
Contributor Author

fcami commented Jun 8, 2020

Thanks @miskopo temp commit removed. Running gating now.

@miskopo miskopo added ack Pull Request approved, can be merged and removed needs review Pull Request is waiting for a review labels Jun 9, 2020
@miskopo
Copy link
Member

miskopo commented Jun 9, 2020

Hi @fcami, you have a GO for take-off... I mean, pushing.

@fcami fcami added the pushed Pull Request has already been pushed label Jun 9, 2020
@fcami
Copy link
Contributor Author

fcami commented Jun 9, 2020

master:

  • b8886c3 IPA-EPN: First version.
  • 3805eff IPA-EPN: Test suite.
  • 03caa7f Add a jinja2 e-mail template for EPN
  • 451cbae Add index for krbPasswordExpiration for EPN

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ack Pull Request approved, can be merged ipa-4-8 Mark for backport to ipa 4.8 pushed Pull Request has already been pushed
Projects
None yet
7 participants