imfreedom/email-ansible

Add the dovecot stuff I was working on awhile ago
draft default tip
2021-07-07, Gary Kramlich
ed91bd614149
Parents 188cd7f859ca
Children
Add the dovecot stuff I was working on awhile ago
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/roles/mail/files/dovecot-10-auth-lua.conf Wed Jul 07 01:43:48 2021 -0500
@@ -0,0 +1,10 @@
+passdb {
+ driver = lua
+ args = file=/etc/dovecot/auth-imf-hub.lua blocking=yes
+}
+
+userdb {
+ driver = lua
+ args = file=/etc/dovecot/auth-imf-hub.lua blocking=yes
+}
+
--- a/roles/mail/tasks/dovecot.yaml Wed Jul 07 01:24:58 2021 -0500
+++ b/roles/mail/tasks/dovecot.yaml Wed Jul 07 01:43:48 2021 -0500
@@ -36,4 +36,21 @@
label: "{{ item.dest }}"
notify:
- "reload dovecot"
+- name: "check for packaged 10-auth.conf"
+ stat:
+ path: "/etc/dovecot/conf.d/10-auth.conf"
+ register: "packaged_10_auth_conf"
+- name: "move 10-auth.conf out of the way"
+ command: "mv /etc/dovecot/conf.d/10-auth.conf /etc/dovecot/conf.d/10-auth.conf.orig"
+ when: "false and packaged_10_auth_conf.stat.exists"
+ notify:
+ - "reload dovecot"
+- name: "add lua based auth configuration"
+ file:
+ copy:
+ src: "dovecot-10-auth-lua.conf"
+ dest: "/etc/dovecot/conf.d/10-auth-lua.conf"
+ mode: "0644"
+ notify:
+ - "reload dovecot"
--- a/roles/mail/tasks/software.yaml Wed Jul 07 01:24:58 2021 -0500
+++ b/roles/mail/tasks/software.yaml Wed Jul 07 01:43:48 2021 -0500
@@ -1,14 +1,15 @@
-- name: install software
+- name: "install dovecot"
apt:
name: "{{ mail_packages }}"
- state: present
+ state: "present"
cache_valid_time: 3600
- force_apt_get: yes
+ force_apt_get: "yes"
vars:
mail_packages:
- - dovecot-auth-lua
- - dovecot-core
- - dovecot-imapd
- - dovecot-lmtpd
+ - "dovecot-auth-lua"
+ - "dovecot-core"
+ - "dovecot-imapd"
+ - "dovecot-lmtpd"
+ - "lua-socket"
tags:
- - dovecot
+ - "dovecot"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/roles/mail/templates/auth-imf-hub.lua.j2 Wed Jul 07 01:43:48 2021 -0500
@@ -0,0 +1,121 @@
+-- vi:ft=lua
+-- This is a templated file created by ansible and any changes made to it will
+-- be over written during the next run. So do NOT edit this file, please go
+-- through the ansible workflow to keep everyone happy.
+
+local string = require("string")
+
+local mime = require("mime")
+
+local hub_url = "{{ mail_hub_url }}"
+local hub_scopes = "{{ mail_hub_scopes }}"
+local hub_client_id = "{{ mail_hub_client_id }}"
+local hub_client_secret = "{{ mail_hub_client_secret }}"
+
+local hub_domain_groups = {
+ "pidgin.im" = { "Pidgin Developer", "Pidgin Contributor", "Pidgin Email" };
+ "imfreedom.org" = {"IMF Board"};
+}
+
+-- The following code is borrowed from prosody's util.http.
+local url_codes = {};
+for i = 0, 255 do
+ local c = char(i);
+ local u = format("%%%02x", i);
+ url_codes[c] = u;
+ url_codes[u] = c;
+ url_codes[u:upper()] = c;
+end
+
+local function urlencode(s)
+ return s and (s:gsub("[^a-zA-Z0-9.~_-]", url_codes));
+end
+
+local function _formencodepart(s)
+ return s and (urlencode(s):gsub("%%20", "+"));
+end
+
+local function formencode(form)
+ local result = {};
+ if form[1] then -- Array of ordered { name, value }
+ for _, field in ipairs(form) do
+ table.insert(result, _formencodepart(field.name).."=".._formencodepart(field.value));
+ end
+ else -- Unordered map of name -> value
+ for name, value in pairs(form) do
+ table.insert(result, _formencodepart(name).."=".._formencodepart(value));
+ end
+ end
+ return table.concat(result, "&");
+end
+
+function http_request(url, headers, body)
+ local ltn12 = require("ltn12")
+
+ local request;
+
+ if string.sub(url, 1, string.len("https")) == "https" then
+ request = require("ssl.https").request
+ else
+ request = require("socket.http").request
+ end
+
+ local t = {}
+
+ _, code, _ = request{
+ url = url,
+ headers = headers,
+ sink = ltn12.sink.table(t),
+ }
+
+ if body and #body > 0 then
+ request["body"] = formencode(body)
+ end
+
+ return {
+ code = code,
+ content = table.concat(t)
+ }
+end
+-- end of prosody util.http
+
+-- verify the users password
+function auth_password_verify(request, password)
+ if request.domain == "" then
+ return dovecot.auth.USERDB_RESULT_USER_UNKNOWN, "no domain specified"
+ end
+
+ if hub_domain_groups[request.domain] == nil then
+ return dovecot.auth.USERDB_RESULT_USER_UNKNOWN, "unknown domain"
+ end
+
+ request.log_info("testing password for " . request.user)
+
+ local token = mime.b64(string.format("%s:%s", hub_client_id, hub_client_secret))
+ local headers = {
+ Authorization = string.format("Basic %s", token);
+ };
+ local body = {
+ "grant_type" = "password";
+ "username" = request.username;
+ "password" = password;
+ "scope" = hub_scopes;
+ };
+
+ local resp = http_request(hub_url.."/api/rest/oauth2/token", headers, body);
+
+end
+
+-- verify the user exists
+function auth_userdb_lookup(request)
+end
+
+-- script lifecycle
+function script_init()
+ return 0
+end
+
+function script_deinit()
+end
+
+