Skip to content

Commit 07b46c7

Browse files
committed
Feat: Add the posibility to automatically set hostname on run
1 parent a99abca commit 07b46c7

File tree

6 files changed

+123
-6
lines changed

6 files changed

+123
-6
lines changed

README.md

+22
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Feel free to pick your favourite distro.
1919
- [General options](#general-options)
2020
- [Inbound debugging](#inbound-debugging)
2121
- [`ALLOWED_SENDER_DOMAINS` and `ALLOW_EMPTY_SENDER_DOMAINS`](#allowed_sender_domains-and-allow_empty_sender_domains)
22+
- [`AUTOSET_HOSTNAME` and `AUTOSET_HOSTNAME_SERVICES`](#autoset_hostname-and-autoset_hostname_services)
2223
- [Log format](#log-format)
2324
- [Postfix-specific options](#postfix-specific-options)
2425
- [`RELAYHOST`, `RELAYHOST_USERNAME` and `RELAYHOST_PASSWORD`](#relayhost-relayhost_username-and-relayhost_password)
@@ -167,6 +168,7 @@ Available for all your favourite architectures. Run in your server cluster. Run
167168
* `INBOUND_DEBUGGING` = Set to `1` to enable detailed debugging in the logs
168169
* `ALLOWED_SENDER_DOMAINS` = domains which are allowed to send email via this server
169170
* `ALLOW_EMPTY_SENDER_DOMAINS` = if value is set (i.e: `true`), `ALLOWED_SENDER_DOMAINS` can be unset
171+
* `AUTOSET_HOSTNAME` and `AUTOSET_HOSTNAME_SERVICES` - use to automatically resolve hostname
170172
* `LOG_FORMAT` = Set your log format (JSON or plain)
171173

172174
#### Inbound debugging
@@ -190,6 +192,26 @@ If you want to set the restrictions on the recipient and not on the sender (anyo
190192
for instance), set `ALLOW_EMPTY_SENDER_DOMAINS` to a non-empty value (e.g. `true`) and `ALLOWED_SENDER_DOMAINS` to an empty
191193
string. Then extend this image through custom scripts to configure Postfix further.
192194

195+
#### `AUTOSET_HOSTNAME` and `AUTOSET_HOSTNAME_SERVICES`
196+
197+
This image can automatically set postfix variable `myhostname` based on reverse DNS resolution of your public IP. To use this
198+
feature, set `AUTOSET_HOSTNAME` to `1`. The image will then:
199+
200+
- use an external service to get the public IP address of the image
201+
- do a reverse DNS lookup to get the hostname associated with that IP
202+
203+
The image will, by default, try to get the public IP from any of these services, which will be queried in the order defined below:
204+
205+
1. https://ipinfo.io/ip
206+
2. https://ifconfig.me/ip
207+
3. https://icanhazip.com
208+
4. https://ipecho.net/plain
209+
5. https://ifconfig.co
210+
6. https://myexternalip.com/raw
211+
212+
The first service to return a non-empty, non-error response will be deemed successful. If you have a preference of another order
213+
and/or wish to use a different service, you can do that by setting the bash array `AUTOSET_HOSTNAME_SERVICES`.
214+
193215
#### Log format
194216

195217
The image will by default output logs in human-readable (`plain`) format. If you are deploying the image to Kubernetes, it might

build-scripts/postfix-install.sh

+34-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,21 @@ do_alpine() {
2222
apk add --upgrade cyrus-sasl cyrus-sasl-static cyrus-sasl-digestmd5 cyrus-sasl-crammd5 cyrus-sasl-login cyrus-sasl-ntlm libsasl
2323
apk add postfix postfix-pcre postfix-ldap ${architecture_specific_packages}
2424
apk add opendkim
25-
apk add --upgrade ca-certificates tzdata supervisor rsyslog musl musl-utils bash opendkim-utils libcurl jsoncpp lmdb logrotate netcat-openbsd
25+
apk add --upgrade \
26+
bash \
27+
bind-tools \
28+
ca-certificates \
29+
jsoncpp \
30+
libcurl \
31+
lmdb \
32+
logrotate \
33+
musl \
34+
musl-utils \
35+
netcat-openbsd \
36+
opendkim-utils \
37+
rsyslog \
38+
supervisor \
39+
tzdata
2640
}
2741

2842

@@ -51,7 +65,25 @@ do_ubuntu() {
5165
if [ "$(apt-cache search --names-only '^libcurl4t64$')" != "" ]; then
5266
libcurl="libcurl4t64"
5367
fi
54-
apt-get install -y ca-certificates tzdata supervisor rsyslog bash opendkim-tools curl ${libcurl} libjsoncpp25 sasl2-bin postfix-lmdb procps logrotate cron net-tools colorized-logs netcat-openbsd ${RELEASE_SPECIFIC_PACKAGES}
68+
apt-get install -y \
69+
${libcurl} ${RELEASE_SPECIFIC_PACKAGES} \
70+
bash \
71+
ca-certificates \
72+
colorized-logs \
73+
cron \
74+
curl \
75+
dnsutils \
76+
libjsoncpp25 \
77+
logrotate \
78+
net-tools \
79+
netcat-openbsd \
80+
opendkim-tools \
81+
postfix-lmdb \
82+
procps \
83+
rsyslog \
84+
sasl2-bin \
85+
supervisor \
86+
tzdata \
5587
apt-get clean
5688
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
5789
}

scripts/common-run.sh

+15-2
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,8 @@ postfix_upgrade_daemon_directory() {
228228
local dir_alpine="/usr/libexec/postfix" # Alpine
229229

230230

231-
# Some people will keep the configuration of postfix on an external drive, although this is not strictly neccessary by this
232-
# image. And when they switch between different distrubtions (Alpine -> Debian and vice versa), the image will fail with the
231+
# Some people will keep the configuration of postfix on an external drive, although this is not strictly necessary by this
232+
# image. And when they switch between different distributions (Alpine -> Debian and vice versa), the image will fail with the
233233
# old configuration. This is a quick and dirty check to solve this issue so we don't get issues like these:
234234
# https://github.com/bokysan/docker-postfix/issues/147
235235
local daemon_directory="$(get_postconf "daemon_directory")"
@@ -307,9 +307,22 @@ postfix_reject_invalid_helos() {
307307
}
308308

309309
postfix_set_hostname() {
310+
local ip
311+
local hostname
310312
do_postconf -# myhostname
313+
if [[ -z "$POSTFIX_myhostname" ]] && [[ "${AUTOSET_HOSTNAME}" == "1" ]]; then
314+
warn "Both ${emphasis}POSTFIX_myhostname${reset} and ${emphasis}AUTOSET_HOSTNAME${reset} are set. ${emphasis}POSTFIX_myhostname${reset} will take precedence and ${emphasis}AUTOSET_HOSTNAME${reset} will be ignored."
315+
fi
316+
311317
if [[ -z "$POSTFIX_myhostname" ]]; then
312318
POSTFIX_myhostname="${HOSTNAME}"
319+
elif [[ "${AUTOSET_HOSTNAME}" == "1" ]]; then
320+
ip=$(get_public_ip)
321+
hostname=$(dig +short -x $IP)
322+
# Remove the trailing dot
323+
hostname="${hostname%.}"
324+
notice "Automatically setting Postfix hostname to ${emphasis}${hostname}${reset} based on your public IP address ${emphasis}${ip}${reset}..."
325+
POSTFIX_myhostname="${hostname}"
313326
fi
314327
}
315328

scripts/common.sh

+30-1
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ trim() {
242242
}
243243

244244
###################################################################
245-
# Potential fix for #180. Plugin names do not neccessarily match
245+
# Potential fix for #180. Plugin names do not necessarily match
246246
# filter names.
247247
#
248248
# This is an utility method which converts SASL plugin names into
@@ -284,4 +284,33 @@ convert_plugin_names_to_filter_names() {
284284
done
285285
}
286286

287+
###################################################################
288+
# Get the public IP of the server. Try different services to ensure
289+
# that at least one works
290+
###################################################################
291+
get_public_ip() {
292+
local services=(https://ipinfo.io/ip https://ifconfig.me/ip https://icanhazip.com https://ipecho.net/ip https://ifconfig.co https://myexternalip.com/raw)
293+
local ip
294+
if [[ -n "${AUTOSET_HOSTNAME_SERVICES}" ]]; then
295+
services=("${AUTOSET_HOSTNAME_SERVICES}")
296+
notice "Using user defined ${emphasis}AUTOSET_HOSTNAME_SERVICES${reset}=${emphasis}${AUTOSET_HOSTNAME_SERVICES}${reset} for IP detection"
297+
else
298+
debug "Public IP detection will use ${emphasis}${services}${reset} to detect the IP."
299+
fi
300+
301+
for service in "${services[@]}"; do
302+
if ip="$(curl --fail-early --retry-max-time 30 --retry 10 --connect-timeout 5 --max-time 10 -s)"; then
303+
# Some services, such as ifconfig.co will return a line feed at the end of the response.
304+
ip="$(printf "%s" "${ip}" | trim)"
305+
if [[ -n "${ip}" ]]; then
306+
info "Detected public IP address as ${emphasis}${services}${ip}${reset}."
307+
break
308+
fi
309+
fi
310+
done
311+
312+
error "Unable to detect public IP. Please check your internet connection and firewall settings."
313+
return 1
314+
}
315+
287316
export reset green yellow orange orange_emphasis lightblue red gray emphasis underline

scripts/run.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ postfix_setup_xoauth2_post_setup # (Post) Setup XOAUTH2 authentication
3232
postfix_setup_networks # Set MYNETWORKS
3333
postfix_setup_debugging # Enable debugging, if defined
3434
postfix_setup_sender_domains # Configure allowed sender domains
35-
postfix_setup_masquarading # Setup masquaraded domains
35+
postfix_setup_masquarading # Setup masqueraded domains
3636
postfix_setup_header_checks # Enable SMTP header checks, if defined
3737
postfix_setup_dkim # Configure DKIM, if enabled
3838
postfix_setup_smtpd_sasl_auth # Enable sender SASL auth, if defined
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env bats
2+
3+
load /code/scripts/common.sh
4+
5+
assert_equals() {
6+
local expected="$1"
7+
local actual="$2"
8+
if [[ "${expected}" != "${actual}" ]]; then
9+
echo "Expected: \"${expected}\". Got: \"${actual}\"." >&2
10+
exit 1
11+
fi
12+
}
13+
14+
@test "check if get_public_ip works" {
15+
local ip1
16+
local ip2
17+
ip1=get_public_ip
18+
AUTOSET_HOSTNAME_SERVICES=(https://ifconfig.co) ip2=get_public_ip
19+
assert_equals "${ip1}" "${ip2}"
20+
}
21+

0 commit comments

Comments
 (0)