init
This commit is contained in:
commit
a66d7d6e8d
15
LICENSE
Normal file
15
LICENSE
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
autonomic.discourse-email: Simple e-mail stack for Discourse
|
||||||
|
Copyright (C) 2022 Autonomic Co-operative <helo@autonomic.zone>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
3
defaults/main.yml
Normal file
3
defaults/main.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
DISCOURSE_API_USER: system
|
||||||
|
postfix_opendkim_port: 8892
|
118
files/discourse-smtp-fast-rejection
Normal file
118
files/discourse-smtp-fast-rejection
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
require 'syslog'
|
||||||
|
require 'json'
|
||||||
|
require 'uri'
|
||||||
|
require 'cgi'
|
||||||
|
require 'net/http'
|
||||||
|
|
||||||
|
ENV_FILE = "/etc/postfix/mail-receiver-environment.json"
|
||||||
|
|
||||||
|
def logger
|
||||||
|
@logger ||= Syslog.open("smtp-reject", Syslog::LOG_PID, Syslog::LOG_MAIL)
|
||||||
|
end
|
||||||
|
|
||||||
|
def fatal(*args)
|
||||||
|
logger.crit *args
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def main
|
||||||
|
unless File.exists?(ENV_FILE)
|
||||||
|
fatal "Config file %s does not exist. Aborting.", ENV_FILE
|
||||||
|
end
|
||||||
|
|
||||||
|
real_env = JSON.parse(File.read(ENV_FILE))
|
||||||
|
|
||||||
|
%w{DISCOURSE_BASE_URL DISCOURSE_API_KEY DISCOURSE_API_USERNAME}.each do |kw|
|
||||||
|
fatal "env var %s is required", kw unless real_env[kw]
|
||||||
|
end
|
||||||
|
|
||||||
|
process_requests(real_env)
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_requests(env)
|
||||||
|
$stdout.sync = true # unbuffered output
|
||||||
|
|
||||||
|
args = {}
|
||||||
|
while line = gets
|
||||||
|
# Fill up args with the request details.
|
||||||
|
# logger.err "KDDEBUG line %s", line
|
||||||
|
line = line.chomp
|
||||||
|
if line.empty?
|
||||||
|
process_single_request(args, env)
|
||||||
|
args = {} # reset for next request.
|
||||||
|
else
|
||||||
|
k,v = line.chomp.split('=', 2)
|
||||||
|
args[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_single_request(args, env)
|
||||||
|
# logger.err "KDDEBUG args %s", args
|
||||||
|
action = 'dunno'
|
||||||
|
if args['request'] != 'smtpd_access_policy'
|
||||||
|
action = 'defer_if_permit Internal error, Request type invalid'
|
||||||
|
elsif args['protocol_state'] != 'RCPT'
|
||||||
|
action = 'dunno'
|
||||||
|
elsif args['sender'].nil?
|
||||||
|
action = 'defer_if_permit No sender specified'
|
||||||
|
elsif args['recipient'].nil?
|
||||||
|
action = 'defer_if_permit No recipient specified'
|
||||||
|
else
|
||||||
|
action = maybe_reject_email(args['sender'], args['recipient'], env)
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "action=#{action}"
|
||||||
|
puts ''
|
||||||
|
end
|
||||||
|
|
||||||
|
def maybe_reject_email(from, to, env)
|
||||||
|
endpoint = "#{env['DISCOURSE_BASE_URL']}/admin/email/smtp_should_reject.json"
|
||||||
|
key = env["DISCOURSE_API_KEY"]
|
||||||
|
username = env["DISCOURSE_API_USERNAME"]
|
||||||
|
# just maker sure we have something in the from field
|
||||||
|
# so we can test for addresses remotely
|
||||||
|
if from == ''
|
||||||
|
from = 'test@example.org'
|
||||||
|
end
|
||||||
|
uri = URI.parse(endpoint)
|
||||||
|
fromarg = CGI::escape(from)
|
||||||
|
toarg = CGI::escape(to)
|
||||||
|
|
||||||
|
api_qs = "from=#{fromarg}&to=#{toarg}"
|
||||||
|
if uri.query and !uri.query.empty?
|
||||||
|
uri.query += "&#{api_qs}"
|
||||||
|
else
|
||||||
|
uri.query = api_qs
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
http = Net::HTTP.new(uri.host, uri.port)
|
||||||
|
http.use_ssl = uri.scheme == "https"
|
||||||
|
# logger.err "KDDEBUG request_uri %s", uri.request_uri
|
||||||
|
get = Net::HTTP::Get.new(uri.request_uri, "api-key" => "#{key}", "api-username" => "#{username}")
|
||||||
|
response = http.request(get)
|
||||||
|
rescue StandardError => ex
|
||||||
|
logger.err "Failed to GET smtp_should_reject answer from %s: %s (%s)", endpoint, ex.message, ex.class
|
||||||
|
logger.err ex.backtrace.map { |l| " #{l}" }.join("\n")
|
||||||
|
return "defer_if_permit Internal error, API request preparation failed"
|
||||||
|
ensure
|
||||||
|
http.finish if http && http.started?
|
||||||
|
end
|
||||||
|
|
||||||
|
if Net::HTTPSuccess === response
|
||||||
|
reply = JSON.parse(response.body)
|
||||||
|
if reply['reject']
|
||||||
|
return "reject #{reply['reason']}"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
logger.err "Failed to GET smtp_should_reject answer from %s: %s", endpoint, response.code
|
||||||
|
return "defer_if_permit Internal error, API request failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
return "dunno" # let future tests also be allowed to reject this one.
|
||||||
|
end
|
||||||
|
|
||||||
|
main if __FILE__ == $0
|
75
files/receive-mail
Normal file
75
files/receive-mail
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
ENV_FILE = "/etc/postfix/mail-receiver-environment.json"
|
||||||
|
EX_TEMPFAIL = 75
|
||||||
|
EX_SUCCESS = 0
|
||||||
|
|
||||||
|
require 'syslog'
|
||||||
|
require 'json'
|
||||||
|
require "uri"
|
||||||
|
require "net/http"
|
||||||
|
|
||||||
|
def logger
|
||||||
|
@logger ||= Syslog.open("receive-mail", Syslog::LOG_PID, Syslog::LOG_MAIL)
|
||||||
|
end
|
||||||
|
|
||||||
|
def fatal(*args)
|
||||||
|
logger.crit *args
|
||||||
|
exit EX_TEMPFAIL
|
||||||
|
end
|
||||||
|
|
||||||
|
def main
|
||||||
|
unless File.exists?(ENV_FILE)
|
||||||
|
fatal "Config file %s does not exist. Aborting.", ENV_FILE
|
||||||
|
end
|
||||||
|
|
||||||
|
real_env = JSON.parse(File.read(ENV_FILE))
|
||||||
|
|
||||||
|
%w{DISCOURSE_BASE_URL DISCOURSE_API_KEY DISCOURSE_API_USERNAME}.each do |kw|
|
||||||
|
fatal "env var %s is required", kw unless real_env[kw]
|
||||||
|
end
|
||||||
|
|
||||||
|
recipient = ARGV.first
|
||||||
|
mail = $stdin.read
|
||||||
|
|
||||||
|
logger.debug "Recipient: #{recipient}"
|
||||||
|
fatal "No recipient passed on command line." unless recipient
|
||||||
|
fatal "No message passed on stdin." if mail.nil? || mail.empty?
|
||||||
|
|
||||||
|
post_email(recipient, mail, real_env)
|
||||||
|
rescue StandardError => ex
|
||||||
|
logger.err "Unexpected error while invoking mail processor: %s (%s)", ex.message, ex.class
|
||||||
|
logger.err ex.backtrace.map { |l| " #{l}" }.join("\n")
|
||||||
|
|
||||||
|
exit EX_TEMPFAIL
|
||||||
|
end
|
||||||
|
|
||||||
|
def post_email(_recipient, mail, env)
|
||||||
|
endpoint = "#{env['DISCOURSE_BASE_URL']}/admin/email/handle_mail"
|
||||||
|
key = env["DISCOURSE_API_KEY"]
|
||||||
|
username = env["DISCOURSE_API_USERNAME"]
|
||||||
|
|
||||||
|
uri = URI.parse(endpoint)
|
||||||
|
|
||||||
|
begin
|
||||||
|
http = Net::HTTP.new(uri.host, uri.port)
|
||||||
|
http.use_ssl = uri.scheme == "https"
|
||||||
|
post = Net::HTTP::Post.new(uri.request_uri,"api-key" => "#{key}", "api-username" => "#{username}")
|
||||||
|
post.set_form_data(email: mail)
|
||||||
|
|
||||||
|
response = http.request(post)
|
||||||
|
rescue StandardError => ex
|
||||||
|
logger.err "Failed to POST the e-mail to %s: %s (%s)", endpoint, ex.message, ex.class
|
||||||
|
logger.err ex.backtrace.map { |l| " #{l}" }.join("\n")
|
||||||
|
exit EX_TEMPFAIL
|
||||||
|
ensure
|
||||||
|
http.finish if http && http.started?
|
||||||
|
end
|
||||||
|
|
||||||
|
exit EX_SUCCESS if Net::HTTPSuccess === response
|
||||||
|
|
||||||
|
logger.err "Failed to POST the e-mail to %s: %s", endpoint, response.code
|
||||||
|
exit EX_TEMPFAIL
|
||||||
|
end
|
||||||
|
|
||||||
|
main if __FILE__ == $0
|
1
handlers/main.yml
Normal file
1
handlers/main.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
---
|
12
meta/main.yml
Normal file
12
meta/main.yml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
galaxy_info:
|
||||||
|
author: "autonomic-roles"
|
||||||
|
company: "Autonomic Cooperative"
|
||||||
|
license: "GPLv3"
|
||||||
|
description: "Simple e-mail stack for Discourse"
|
||||||
|
dependencies: []
|
||||||
|
min_ansible_version: 2.4.3
|
||||||
|
platforms:
|
||||||
|
- name: Debian
|
||||||
|
versions:
|
||||||
|
- jessie
|
2
molecule/default/Dockerfile.j2
Normal file
2
molecule/default/Dockerfile.j2
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
FROM {{ item.image }}
|
||||||
|
RUN /bin/sh -c 'apt-get update && apt-get upgrade -y && apt-get install -y python sudo bash'
|
41
molecule/default/create.yml
Normal file
41
molecule/default/create.yml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
- name: Setup
|
||||||
|
hosts: localhost
|
||||||
|
connection: local
|
||||||
|
gather_facts: False
|
||||||
|
no_log: "{{ not lookup('env', 'MOLECULE_DEBUG') | bool }}"
|
||||||
|
vars:
|
||||||
|
molecule_file: "{{ lookup('env', 'MOLECULE_FILE') }}"
|
||||||
|
molecule_ephemeral_directory: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}"
|
||||||
|
molecule_scenario_directory: "{{ lookup('env', 'MOLECULE_SCENARIO_DIRECTORY') }}"
|
||||||
|
molecule_yml: "{{ lookup('file', molecule_file) | from_yaml }}"
|
||||||
|
tasks:
|
||||||
|
- name: Create Dockerfiles from image names
|
||||||
|
template:
|
||||||
|
src: "{{ molecule_scenario_directory }}/Dockerfile.j2"
|
||||||
|
dest: "{{ molecule_ephemeral_directory }}/Dockerfile_{{ item.image | regex_replace('[^a-zA-Z0-9_]', '_') }}"
|
||||||
|
with_items: "{{ molecule_yml.platforms }}"
|
||||||
|
register: platforms
|
||||||
|
|
||||||
|
- name: Build an Ansible compatible image
|
||||||
|
docker_image:
|
||||||
|
path: "{{ molecule_ephemeral_directory }}"
|
||||||
|
name: "molecule_local/{{ item.item.image }}"
|
||||||
|
dockerfile: "{{ item.item.dockerfile | default(item.invocation.module_args.dest) }}"
|
||||||
|
force: "{{ item.item.force | default(True) }}"
|
||||||
|
with_items: "{{ platforms.results }}"
|
||||||
|
when: platforms.changed
|
||||||
|
|
||||||
|
- name: Create molecule instance(s)
|
||||||
|
docker_container:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
hostname: "{{ item.name }}"
|
||||||
|
image: "molecule_local/{{ item.image }}"
|
||||||
|
state: started
|
||||||
|
recreate: False
|
||||||
|
log_driver: json-file
|
||||||
|
command: "{{ item.command | default('sleep infinity') }}"
|
||||||
|
privileged: "{{ item.privileged | default(omit) }}"
|
||||||
|
volumes: "{{ item.volumes | default(omit) }}"
|
||||||
|
capabilities: "{{ item.capabilities | default(omit) }}"
|
||||||
|
with_items: "{{ molecule_yml.platforms }}"
|
16
molecule/default/destroy.yml
Normal file
16
molecule/default/destroy.yml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
- name: Teardown
|
||||||
|
hosts: localhost
|
||||||
|
connection: local
|
||||||
|
gather_facts: False
|
||||||
|
no_log: "{{ not lookup('env', 'MOLECULE_DEBUG') | bool }}"
|
||||||
|
vars:
|
||||||
|
molecule_file: "{{ lookup('env','MOLECULE_FILE') }}"
|
||||||
|
molecule_yml: "{{ lookup('file', molecule_file) | from_yaml }}"
|
||||||
|
tasks:
|
||||||
|
- name: Destroy molecule instance(s)
|
||||||
|
docker_container:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
state: absent
|
||||||
|
force_kill: "{{ item.force_kill | default(True) }}"
|
||||||
|
with_items: "{{ molecule_yml.platforms }}"
|
45
molecule/default/molecule.yml
Normal file
45
molecule/default/molecule.yml
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
dependency:
|
||||||
|
name: gilt
|
||||||
|
|
||||||
|
driver:
|
||||||
|
name: docker
|
||||||
|
|
||||||
|
lint:
|
||||||
|
name: yamllint
|
||||||
|
|
||||||
|
platforms:
|
||||||
|
- name: container
|
||||||
|
image: debian:stretch
|
||||||
|
privileged: true
|
||||||
|
|
||||||
|
provisioner:
|
||||||
|
name: ansible
|
||||||
|
lint:
|
||||||
|
name: ansible-lint
|
||||||
|
connection_options:
|
||||||
|
ansible_ssh_user: 'root'
|
||||||
|
ansible_ssh_common_args: -o ServerAliveInterval=30 -o ControlMaster=auto -o ControlPersist=60s
|
||||||
|
|
||||||
|
scenario:
|
||||||
|
name: default
|
||||||
|
test_sequence:
|
||||||
|
- dependency
|
||||||
|
- lint
|
||||||
|
- cleanup
|
||||||
|
- destroy
|
||||||
|
- syntax
|
||||||
|
- create
|
||||||
|
- prepare
|
||||||
|
- converge
|
||||||
|
# no idempotence for the moment
|
||||||
|
# - idempotence
|
||||||
|
- side_effect
|
||||||
|
- verify
|
||||||
|
- cleanup
|
||||||
|
- destroy
|
||||||
|
|
||||||
|
verifier:
|
||||||
|
name: testinfra
|
||||||
|
lint:
|
||||||
|
name: flake8
|
11
molecule/default/playbook.yml
Normal file
11
molecule/default/playbook.yml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
- name: Converge
|
||||||
|
hosts: all
|
||||||
|
gather_facts: True
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- role: autonomic.discourse.email
|
||||||
|
vars:
|
||||||
|
hostname: forum.example.com
|
||||||
|
root_email_forward: nobody@example.com
|
||||||
|
DISCOURSE_API_KEY: foobar
|
30
molecule/default/prepare.yml
Normal file
30
molecule/default/prepare.yml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
- name: Prepare
|
||||||
|
hosts: all
|
||||||
|
gather_facts: True
|
||||||
|
become: True
|
||||||
|
roles:
|
||||||
|
- role: geerlingguy.docker
|
||||||
|
tasks:
|
||||||
|
- name: Install python for Ansible
|
||||||
|
raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal python-zipstream)
|
||||||
|
become: true
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Install Ansible Python packages
|
||||||
|
apt:
|
||||||
|
package:
|
||||||
|
- python-setuptools
|
||||||
|
- python-pip
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Install python-docker
|
||||||
|
pip:
|
||||||
|
name: docker
|
||||||
|
|
||||||
|
- name: Create dummy Discourse container
|
||||||
|
docker_container:
|
||||||
|
name: app
|
||||||
|
image: alpine
|
||||||
|
state: started
|
||||||
|
privileged: true
|
2
molecule/default/requirements.yml
Normal file
2
molecule/default/requirements.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
- geerlingguy.docker
|
9
molecule/default/tests/test_default.yml
Normal file
9
molecule/default/tests/test_default.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
file:
|
||||||
|
/etc/mail-receiver-environment.json:
|
||||||
|
exists: true
|
||||||
|
|
||||||
|
package:
|
||||||
|
postfix:
|
||||||
|
installed: true
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
ansible==2.4.3.0
|
||||||
|
docker<3.0 # https://github.com/ansible/ansible/issues/35612
|
||||||
|
molecule==2.22
|
98
tasks/dkim_domain.yml
Normal file
98
tasks/dkim_domain.yml
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
---
|
||||||
|
- name: "Directory for opendkim keys for {{ domain }} present"
|
||||||
|
file:
|
||||||
|
path: "/etc/opendkim/keys/{{ domain }}"
|
||||||
|
state: directory
|
||||||
|
owner: opendkim
|
||||||
|
group: opendkim
|
||||||
|
mode: 0700
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: "OpenDKIM selector present for {{ domain }}"
|
||||||
|
shell: "date +%Y%m%d > /etc/opendkim/{{ domain }}_selector.txt"
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
creates: "/etc/opendkim/{{ domain }}_selector.txt"
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: "OpenDKIM selector selector read for {{ domain }}"
|
||||||
|
slurp:
|
||||||
|
src: "/etc/opendkim/{{ domain }}_selector.txt"
|
||||||
|
register: "selector_b64encoded"
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: "Set a fact for the selector for {{ domain }}"
|
||||||
|
set_fact:
|
||||||
|
selector: "{{ selector_b64encoded['content'] | b64decode | trim }}"
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: "Keys for {{ domain }} present"
|
||||||
|
command: "opendkim-genkey -b 2048 -h sha256 -s {{ selector }} -d {{ domain }} -D /etc/opendkim/keys/{{ domain }}"
|
||||||
|
args:
|
||||||
|
creates: "/etc/opendkim/keys/{{ domain }}/{{ selector }}.private"
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: "SPF record added to /etc/opendkim/keys/{{ domain }}/{{ selector }}.txt"
|
||||||
|
lineinfile:
|
||||||
|
path: "/etc/opendkim/keys/{{ domain }}/{{ selector }}.txt"
|
||||||
|
line: '{{ domain }}. IN TXT "v=spf1 a mx include:{{ domain }} ~all"'
|
||||||
|
state: present
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: "OpenDKIM private key for {{ domain }} owned and only readable by opendkim user"
|
||||||
|
file:
|
||||||
|
path: "/etc/opendkim/keys/{{ domain }}/{{ selector }}.private"
|
||||||
|
owner: opendkim
|
||||||
|
group: opendkim
|
||||||
|
mode: 0600
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: "OpenDKIM key check for {{ domain }}"
|
||||||
|
shell: "opendkim-testkey -d {{ domain }} -s {{ selector }} -k {{ selector }}.private -vvv || echo 'key FAIL'"
|
||||||
|
args:
|
||||||
|
chdir: "/etc/opendkim/keys/{{ domain }}"
|
||||||
|
check_mode: false
|
||||||
|
register: opendkim_check
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: "DNS configuration needed for {{ domain }}"
|
||||||
|
debug:
|
||||||
|
msg: "Please add the DNS record from /etc/opendkim/keys/{{ domain }}/{{ selector }}.txt"
|
||||||
|
when: '"key OK" not in opendkim_check.stdout'
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: "OpenDKIM key check passed so {{ domain }} added to new KeyTable and SigningTable files"
|
||||||
|
block:
|
||||||
|
|
||||||
|
- name: "KeyTable for {{ domain }} {{ opendkim_check.stdout }}"
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/opendkim/KeyTable.new
|
||||||
|
line: "{{ selector }}._domainkey.{{ domain }} {{ domain }}:{{ selector }}:/etc/opendkim/keys/{{ domain }}/{{ selector }}.private"
|
||||||
|
regexp: "\\._domainkey\\.{{ domain }} {{ domain }}:{{ selector }}:"
|
||||||
|
state: present
|
||||||
|
create: true
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: "SigningTable for {{ domain }} {{ opendkim_check.stdout }}"
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/opendkim/SigningTable.new
|
||||||
|
line: "*@{{ domain }} {{ selector }}._domainkey.{{ domain }}"
|
||||||
|
regexp: "^\\*@{{ domain }} "
|
||||||
|
state: present
|
||||||
|
create: true
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
when: '"key OK" in opendkim_check.stdout'
|
||||||
|
...
|
397
tasks/main.yml
Normal file
397
tasks/main.yml
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
---
|
||||||
|
- name: Ruby packages installed
|
||||||
|
apt:
|
||||||
|
pkg:
|
||||||
|
- ruby2.3
|
||||||
|
- ruby-addressable
|
||||||
|
- ruby-json
|
||||||
|
- ruby-net-http-persistent
|
||||||
|
- ruby-syslog-logger
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Ruby script receive-mail in place
|
||||||
|
copy:
|
||||||
|
src: files/receive-mail
|
||||||
|
dest: /usr/local/bin/receive-mail
|
||||||
|
mode: 0755
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Ruby script discourse-smtp-fast-rejection in place
|
||||||
|
copy:
|
||||||
|
src: files/discourse-smtp-fast-rejection
|
||||||
|
dest: /usr/local/bin/discourse-smtp-fast-rejection
|
||||||
|
mode: 0755
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Old, unneeded files removed
|
||||||
|
file:
|
||||||
|
path: /usr/local/bin/discourse-smtp-rcpt-acl
|
||||||
|
state: absent
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: debconf-utils installed for Ansible
|
||||||
|
apt:
|
||||||
|
name: debconf-utils
|
||||||
|
state: present
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Debconf Postfix hostname set
|
||||||
|
debconf:
|
||||||
|
name: postfix
|
||||||
|
question: "postfix/mailname"
|
||||||
|
value: "{{ hostname }}"
|
||||||
|
vtype: string
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Debconf Postfix set to be a internet server
|
||||||
|
debconf:
|
||||||
|
name: postfix
|
||||||
|
question: "postfix/main_mailer_type"
|
||||||
|
value: "Internet Site"
|
||||||
|
vtype: string
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix and related email packages installed
|
||||||
|
apt:
|
||||||
|
pkg:
|
||||||
|
- ca-certificates
|
||||||
|
- curl
|
||||||
|
- debian-archive-keyring
|
||||||
|
- dnsutils
|
||||||
|
- mailutils
|
||||||
|
- mutt
|
||||||
|
- opendkim
|
||||||
|
- opendkim-tools
|
||||||
|
- postfix
|
||||||
|
- pwgen
|
||||||
|
- whois
|
||||||
|
state: present
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix smtpd_relay_restrictions set
|
||||||
|
command: postconf -e "smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated reject_unauth_destination"
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix set not to use /etc/aliases
|
||||||
|
command: postconf -e "alias_maps = "
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix mydestination set to localhost
|
||||||
|
command: postconf -e "mydestination = localhost"
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: python-docker installed
|
||||||
|
apt:
|
||||||
|
pkg:
|
||||||
|
- python3-docker
|
||||||
|
state: present
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Fetch app container information
|
||||||
|
docker_container_info:
|
||||||
|
name: app
|
||||||
|
register: containerinfo
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Get the app container IP address
|
||||||
|
set_fact:
|
||||||
|
app_ip_address: '{{ containerinfo.container.NetworkSettings.IPAddress }}'
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix my networks set to include {{ app_ip_address }}
|
||||||
|
command: postconf -e "mynetworks = 127.0.0.0/8,{{ app_ip_address }}"
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix relay domains set to {{ hostname }}
|
||||||
|
command: postconf -e "relay_domains = {{ hostname }}"
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix smtpd_recipient_restrictions set
|
||||||
|
command: postconf -e "smtpd_recipient_restrictions = permit_mynetworks, check_policy_service unix:private/policy"
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix opportunistic TLS enabled
|
||||||
|
command: postconf -e "smtp_tls_security_level = may"
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix set to use sub-addresing
|
||||||
|
command: postconf -e "recipient_delimiter = +"
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix disable UTF-8 SMTP input
|
||||||
|
command: postconf -e "smtputf8_enable=no"
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix Time Zone and Lang set
|
||||||
|
command: postconf -e "export_environment='TZ LANG'"
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix set for ipv4 only
|
||||||
|
command: postconf -e "inet_protocols = ipv4"
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix set to use /usr/local/bin/receive-mail
|
||||||
|
command: postconf -M -e "discourse/unix=discourse unix - n n - - pipe user=nobody:nogroup argv=/usr/local/bin/receive-mail ${recipient}"
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix transport in place
|
||||||
|
template:
|
||||||
|
src: templates/transport.j2
|
||||||
|
dest: /etc/postfix/transport
|
||||||
|
mode: 0644
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix Transport Maps file set
|
||||||
|
command: postconf -e "transport_maps=hash:/etc/postfix/transport"
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postmap run with Transport Maps file
|
||||||
|
command: postmap /etc/postfix/transport
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix set to reject incorrect email addresses
|
||||||
|
command: postconf -M -e "policy/unix=policy unix - n n - - spawn user=nobody argv=/usr/local/bin/discourse-smtp-fast-rejection"
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Stat "/var/discourse/shared/standalone/letsencrypt/{{ hostname }}/{{ hostname }}.cer"
|
||||||
|
stat:
|
||||||
|
path: "/var/discourse/shared/standalone/letsencrypt/{{ hostname }}/{{ hostname }}.cer"
|
||||||
|
check_mode: false
|
||||||
|
register: le_cert
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- block:
|
||||||
|
|
||||||
|
- name: Postfix configured to use Let's Encrypt RSA cert for incoming email
|
||||||
|
command: postconf -e "smtpd_tls_cert_file = /var/discourse/shared/standalone/letsencrypt/{{ hostname }}/{{ hostname }}.cer"
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Postfix configured to use Let's Encrypt RSA key for incoming email
|
||||||
|
command: postconf -e "smtpd_tls_key_file = /var/discourse/shared/standalone/letsencrypt/{{ hostname }}/{{ hostname }}.key"
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
when: le_cert.stat.exists
|
||||||
|
|
||||||
|
- name: Directories for opendkim keys and configuration present
|
||||||
|
file:
|
||||||
|
path: "{{ dir.name }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ dir.owner }}"
|
||||||
|
group: "{{ dir.group }}"
|
||||||
|
mode: "{{ dir.mode }}"
|
||||||
|
loop:
|
||||||
|
- name: /etc/opendkim
|
||||||
|
mode: "0750"
|
||||||
|
owner: root
|
||||||
|
group: opendkim
|
||||||
|
- name: /etc/opendkim/keys
|
||||||
|
mode: "0750"
|
||||||
|
owner: root
|
||||||
|
group: opendkim
|
||||||
|
loop_control:
|
||||||
|
loop_var: dir
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Set a fact for the postfix_dkim_domains array if it it not defined
|
||||||
|
set_fact:
|
||||||
|
dkim_domains:
|
||||||
|
- "{{ hostname | default(inventory_hostname) }}"
|
||||||
|
when: ( dkim_domains is not defined ) or ( dkim_domains == [] )
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Generate new KeyTable and SigningTable files
|
||||||
|
template:
|
||||||
|
src: "{{ template }}.j2"
|
||||||
|
dest: "/etc/opendkim/{{ template }}.new"
|
||||||
|
loop:
|
||||||
|
- KeyTable
|
||||||
|
- SigningTable
|
||||||
|
loop_control:
|
||||||
|
loop_var: template
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Loop through the postfix_dkim_domains array including DKIM tasks
|
||||||
|
include_tasks: dkim_domain.yml
|
||||||
|
loop: "{{ dkim_domains }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: domain
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Copy the new KeyTable and SigningTable files into place if changed
|
||||||
|
copy:
|
||||||
|
src: "{{ file }}.new"
|
||||||
|
dest: "{{ file }}"
|
||||||
|
remote_src: true
|
||||||
|
loop:
|
||||||
|
- /etc/opendkim/KeyTable
|
||||||
|
- /etc/opendkim/SigningTable
|
||||||
|
loop_control:
|
||||||
|
loop_var: file
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Check if the KeyTable has more than one line
|
||||||
|
command: wc -l /etc/opendkim/KeyTable
|
||||||
|
check_mode: false
|
||||||
|
changed_when: false
|
||||||
|
register: opendkim_keytable_check
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Check if the SigningTable has more than one line
|
||||||
|
command: wc -l /etc/opendkim/SigningTable
|
||||||
|
check_mode: false
|
||||||
|
changed_when: false
|
||||||
|
register: opendkim_signingtable_check
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Set fact for KeyTable and SigningTable file lengths
|
||||||
|
set_fact:
|
||||||
|
opendkim_keytable_length: "{{ opendkim_keytable_check.stdout | replace('/etc/opendkim/KeyTable', '') | trim | int }}"
|
||||||
|
opendkim_signingtable_length: "{{ opendkim_signingtable_check.stdout | replace('/etc/opendkim/SigningTable', '') | trim | int }}"
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Enable OpenDKIM
|
||||||
|
block:
|
||||||
|
|
||||||
|
- name: Configure TrustedHosts
|
||||||
|
template:
|
||||||
|
src: templates/TrustedHosts.j2
|
||||||
|
dest: /etc/opendkim/TrustedHosts
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: 0644
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: OpenDKIM configuration in place
|
||||||
|
template:
|
||||||
|
src: templates/opendkim.conf.j2
|
||||||
|
dest: /etc/opendkim.conf
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Run postconf to add DKIM configuration to main.cf
|
||||||
|
command: postconf -e "{{ edit }}"
|
||||||
|
loop:
|
||||||
|
- "milter_default_action = accept"
|
||||||
|
- "milter_protocol = 6"
|
||||||
|
- "smtpd_milters = inet:localhost:{{ postfix_opendkim_port }}"
|
||||||
|
- "non_smtpd_milters = inet:localhost:{{ postfix_opendkim_port }}"
|
||||||
|
loop_control:
|
||||||
|
loop_var: edit
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: OpenDKIM enabled and restarted
|
||||||
|
service:
|
||||||
|
name: opendkim
|
||||||
|
enabled: true
|
||||||
|
state: restarted
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
when: ( opendkim_keytable_length | int > 1 ) and ( opendkim_signingtable_length | int > 1 )
|
||||||
|
|
||||||
|
- name: Disable OpenDKIM
|
||||||
|
block:
|
||||||
|
|
||||||
|
- name: Run postconf to remove DKIM configuration from main.cf
|
||||||
|
command: postconf -X "{{ remove }}"
|
||||||
|
loop:
|
||||||
|
- "milter_default_action"
|
||||||
|
- "milter_protocol"
|
||||||
|
- "smtpd_milters"
|
||||||
|
- "non_smtpd_milters"
|
||||||
|
loop_control:
|
||||||
|
loop_var: remove
|
||||||
|
changed_when: false
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: OpenDKIM disabled and stopped
|
||||||
|
service:
|
||||||
|
name: opendkim
|
||||||
|
enabled: false
|
||||||
|
state: stopped
|
||||||
|
when: ( postfix_dkim_dns_configured is not defined ) or ( not postfix_dkim_dns_configured )
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
when: ( opendkim_keytable_length | int == 1 ) or ( opendkim_signingtable_length | int == 1 )
|
||||||
|
|
||||||
|
- name: mail-receiver-environment in place
|
||||||
|
template:
|
||||||
|
src: templates/mail-receiver-environment.json.j2
|
||||||
|
dest: /etc/postfix/mail-receiver-environment.json
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: 0644
|
||||||
|
|
||||||
|
- name: Postfix restarted
|
||||||
|
service:
|
||||||
|
name: postfix
|
||||||
|
state: restarted
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
|
||||||
|
- name: Root .forward in place
|
||||||
|
template:
|
||||||
|
src: templates/forward.j2
|
||||||
|
dest: /root/.forward
|
||||||
|
tags:
|
||||||
|
- email
|
||||||
|
...
|
1
templates/KeyTable.j2
Normal file
1
templates/KeyTable.j2
Normal file
@ -0,0 +1 @@
|
|||||||
|
# {{ ansible_managed }}
|
1
templates/SigningTable.j2
Normal file
1
templates/SigningTable.j2
Normal file
@ -0,0 +1 @@
|
|||||||
|
# {{ ansible_managed }}
|
4
templates/TrustedHosts.j2
Normal file
4
templates/TrustedHosts.j2
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# {{ ansible_managed }}
|
||||||
|
127.0.0.1
|
||||||
|
localhost
|
||||||
|
{{ app_ip_address }}
|
4
templates/dkim.hosts.j2
Normal file
4
templates/dkim.hosts.j2
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# {{ ansible_managed }}
|
||||||
|
# Add these entries to the DNS:
|
||||||
|
{{ hostname | default(inventory_hostname) }}. IN TXT "v=spf1 a mx include:{{ hostname | default(inventory_hostname) }} ~all"
|
||||||
|
{{ postfix_dkim_selector_hostname | default(inventory_hostname) }}._domainkey.{{ hostname | default(inventory_hostname) }}. IN TXT "v=DKIM1;k=rsa;t=s;s=email;p={{ postfix_dkim_pub_key_stripped.stdout }}"
|
1
templates/forward.j2
Normal file
1
templates/forward.j2
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{ root_email_forward }}
|
5
templates/mail-receiver-environment.json.j2
Normal file
5
templates/mail-receiver-environment.json.j2
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"DISCOURSE_BASE_URL": "https://{{ hostname }}",
|
||||||
|
"DISCOURSE_API_KEY": "{{ DISCOURSE_API_KEY }}",
|
||||||
|
"DISCOURSE_API_USERNAME": "{{ DISCOURSE_API_USER }}"
|
||||||
|
}
|
90
templates/opendkim.conf.j2
Normal file
90
templates/opendkim.conf.j2
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
# {{ ansible_managed }}
|
||||||
|
#
|
||||||
|
# This is a basic configuration that can easily be adapted to suit a standard
|
||||||
|
# installation. For more advanced options, see opendkim.conf(5) and/or
|
||||||
|
# /usr/share/doc/opendkim/examples/opendkim.conf.sample.
|
||||||
|
|
||||||
|
# Log to syslog
|
||||||
|
Syslog yes
|
||||||
|
SyslogSuccess yes
|
||||||
|
LogWhy yes
|
||||||
|
# Required to use local socket with MTAs that access the socket as a non-
|
||||||
|
# privileged user (e.g. Postfix)
|
||||||
|
UMask 002
|
||||||
|
|
||||||
|
# Sign for example.com with key in /etc/dkimkeys/dkim.key using
|
||||||
|
# selector '2007' (e.g. 2007._domainkey.example.com)
|
||||||
|
# Domain example.com
|
||||||
|
# KeyFile /etc/dkimkeys/dkim.key
|
||||||
|
# Selector 2007
|
||||||
|
|
||||||
|
# Commonly-used options; the commented-out versions show the defaults.
|
||||||
|
#Canonicalization simple
|
||||||
|
#Mode sv
|
||||||
|
#SubDomains no
|
||||||
|
|
||||||
|
# Socket smtp://localhost
|
||||||
|
#
|
||||||
|
# ## Socket socketspec
|
||||||
|
# ##
|
||||||
|
# ## Names the socket where this filter should listen for milter connections
|
||||||
|
# ## from the MTA. Required. Should be in one of these forms:
|
||||||
|
# ##
|
||||||
|
# ## inet:port@address to listen on a specific interface
|
||||||
|
# ## inet:port to listen on all interfaces
|
||||||
|
# ## local:/path/to/socket to listen on a UNIX domain socket
|
||||||
|
#
|
||||||
|
Socket inet:{{ postfix_opendkim_port }}@localhost
|
||||||
|
#Socket local:/var/run/opendkim/opendkim.sock
|
||||||
|
|
||||||
|
## PidFile filename
|
||||||
|
### default (none)
|
||||||
|
###
|
||||||
|
### Name of the file where the filter should write its pid before beginning
|
||||||
|
### normal operations.
|
||||||
|
#
|
||||||
|
PidFile /var/run/opendkim/opendkim.pid
|
||||||
|
|
||||||
|
|
||||||
|
# Always oversign From (sign using actual From and a null From to prevent
|
||||||
|
# malicious signatures header fields (From and/or others) between the signer
|
||||||
|
# and the verifier. From is oversigned by default in the Debian pacakge
|
||||||
|
# because it is often the identity key used by reputation systems and thus
|
||||||
|
# somewhat security sensitive.
|
||||||
|
OversignHeaders From
|
||||||
|
|
||||||
|
## ResolverConfiguration filename
|
||||||
|
## default (none)
|
||||||
|
##
|
||||||
|
## Specifies a configuration file to be passed to the Unbound library that
|
||||||
|
## performs DNS queries applying the DNSSEC protocol. See the Unbound
|
||||||
|
## documentation at http://unbound.net for the expected content of this file.
|
||||||
|
## The results of using this and the TrustAnchorFile setting at the same
|
||||||
|
## time are undefined.
|
||||||
|
## In Debian, /etc/unbound/unbound.conf is shipped as part of the Suggested
|
||||||
|
## unbound package
|
||||||
|
|
||||||
|
# ResolverConfiguration /etc/unbound/unbound.conf
|
||||||
|
|
||||||
|
## TrustAnchorFile filename
|
||||||
|
## default (none)
|
||||||
|
##
|
||||||
|
## Specifies a file from which trust anchor data should be read when doing
|
||||||
|
## DNS queries and applying the DNSSEC protocol. See the Unbound documentation
|
||||||
|
## at http://unbound.net for the expected format of this file.
|
||||||
|
|
||||||
|
TrustAnchorFile /usr/share/dns/root.key
|
||||||
|
|
||||||
|
## Userid userid
|
||||||
|
### default (none)
|
||||||
|
###
|
||||||
|
### Change to user "userid" before starting normal operation? May include
|
||||||
|
### a group ID as well, separated from the userid by a colon.
|
||||||
|
#
|
||||||
|
UserID opendkim:opendkim
|
||||||
|
|
||||||
|
## Signing options
|
||||||
|
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
|
||||||
|
InternalHosts refile:/etc/opendkim/TrustedHosts
|
||||||
|
KeyTable refile:/etc/opendkim/KeyTable
|
||||||
|
SigningTable refile:/etc/opendkim/SigningTable
|
23
templates/opendkim.j2
Normal file
23
templates/opendkim.j2
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# {{ ansible_managed }}
|
||||||
|
# Command-line options specified here will override the contents of
|
||||||
|
# /etc/opendkim.conf. See opendkim(8) for a complete list of options.
|
||||||
|
#DAEMON_OPTS=""
|
||||||
|
# Change to /var/spool/postfix/var/run/opendkim to use a Unix socket with
|
||||||
|
# postfix in a chroot:
|
||||||
|
#RUNDIR=/var/spool/postfix/var/run/opendkim
|
||||||
|
RUNDIR=/var/run/opendkim
|
||||||
|
#
|
||||||
|
# Uncomment to specify an alternate socket
|
||||||
|
# Note that setting this will override any Socket value in opendkim.conf
|
||||||
|
# default:
|
||||||
|
# SOCKET=local:$RUNDIR/opendkim.sock
|
||||||
|
# listen on all interfaces on port 54321:
|
||||||
|
#SOCKET=inet:54321
|
||||||
|
# listen on loopback on port 12345:
|
||||||
|
SOCKET=inet:{{ postfix_opendkim_port }}@localhost
|
||||||
|
# listen on 192.0.2.1 on port 12345:
|
||||||
|
#SOCKET=inet:12345@192.0.2.1
|
||||||
|
USER=opendkim
|
||||||
|
GROUP=opendkim
|
||||||
|
PIDFILE=$RUNDIR/$NAME.pid
|
||||||
|
EXTRAAFTER=
|
1
templates/transport.j2
Normal file
1
templates/transport.j2
Normal file
@ -0,0 +1 @@
|
|||||||
|
{{ hostname }} discourse:
|
2
vars/main.yml
Normal file
2
vars/main.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
ansible_user: "{{ lookup('env', 'ANSIBLE_USER') }}"
|
Reference in New Issue
Block a user