mirror of
https://github.com/processone/ejabberd
synced 2025-10-03 09:49:18 +02:00
Merge pull request #4340 from badlop/containers-frictionless
CTL_OVER_HTTP and containers frictionless
This commit is contained in:
commit
29616dc163
10 changed files with 929 additions and 325 deletions
92
.github/container/Dockerfile
vendored
92
.github/container/Dockerfile
vendored
|
@ -1,20 +1,22 @@
|
|||
#' Define default build variables
|
||||
## specifc ARGs for METHOD='direct'
|
||||
ARG OTP_VSN='27.2'
|
||||
ARG ELIXIR_VSN='1.18.1'
|
||||
## specifc ARGs for METHOD='package'
|
||||
ARG ALPINE_VSN='3.19'
|
||||
## general ARGs
|
||||
ARG UID='9000'
|
||||
ARG USER='ejabberd'
|
||||
ARG HOME="opt/$USER"
|
||||
ARG METHOD='direct'
|
||||
ARG BUILD_DIR="/$USER"
|
||||
ARG VERSION='master'
|
||||
|
||||
################################################################################
|
||||
#' METHOD='direct' - build and install ejabberd directly from source
|
||||
FROM docker.io/erlang:${OTP_VSN}-alpine AS direct
|
||||
#' Compile ejabberdapi
|
||||
FROM docker.io/golang:1.23-alpine AS api
|
||||
RUN go install -v \
|
||||
github.com/processone/ejabberd-api/cmd/ejabberd@master \
|
||||
&& mv bin/ejabberd bin/ejabberdapi
|
||||
|
||||
################################################################################
|
||||
#' build and install ejabberd directly from source
|
||||
FROM docker.io/erlang:${OTP_VSN}-alpine AS ejabberd
|
||||
|
||||
RUN apk -U add --no-cache \
|
||||
autoconf \
|
||||
|
@ -39,7 +41,8 @@ ARG ELIXIR_VSN
|
|||
RUN wget -O - https://github.com/elixir-lang/elixir/archive/v$ELIXIR_VSN.tar.gz \
|
||||
| tar -xzf -
|
||||
|
||||
WORKDIR elixir-$ELIXIR_VSN
|
||||
WORKDIR /elixir-$ELIXIR_VSN
|
||||
ENV ERL_FLAGS="+JPperf true"
|
||||
RUN make install clean
|
||||
|
||||
RUN mix local.hex --force \
|
||||
|
@ -50,6 +53,7 @@ COPY / $BUILD_DIR/
|
|||
|
||||
WORKDIR $BUILD_DIR
|
||||
RUN mv .github/container/ejabberdctl.template . \
|
||||
&& mv .github/container/ejabberd.yml.example . \
|
||||
&& ./autogen.sh \
|
||||
&& ./configure --with-rebar=mix --enable-all \
|
||||
&& make deps \
|
||||
|
@ -67,36 +71,18 @@ RUN cp -p $BUILD_DIR/tools/captcha*.sh $HOME-$VERSION/lib
|
|||
RUN find "$HOME-$VERSION/bin" -name 'ejabberd' -delete \
|
||||
&& find "$HOME-$VERSION/releases" -name 'COOKIE' -delete
|
||||
|
||||
RUN wget -O "$HOME/conf/cacert.pem" 'https://curl.se/ca/cacert.pem' \
|
||||
&& sed -i '/^loglevel:/a \ \
|
||||
\nca_file: /opt/ejabberd/conf/cacert.pem \
|
||||
\ncertfiles: \
|
||||
\n - /opt/ejabberd/conf/server.pem' "$HOME/conf/ejabberd.yml"
|
||||
RUN wget -O "$HOME/conf/cacert.pem" 'https://curl.se/ca/cacert.pem'
|
||||
|
||||
################################################################################
|
||||
#' METHOD='package' - install ejabberd from binary tarball package
|
||||
FROM docker.io/alpine:${ALPINE_VSN} AS package
|
||||
COPY tarballs/ejabberd-*-linux-musl-*.tar.gz /tmp/
|
||||
WORKDIR /rootfs
|
||||
ARG HOME
|
||||
RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \
|
||||
&& mkdir -p $home_root_dir \
|
||||
&& ARCH=$(uname -m | sed -e 's/x86_64/x64/;s/aarch64/arm64/') \
|
||||
&& tar -xzf /tmp/ejabberd-*-linux-musl-$ARCH.tar.gz -C $home_root_dir
|
||||
|
||||
################################################################################
|
||||
#' Prepare ejabberd for runtime
|
||||
FROM ${METHOD} AS ejabberd
|
||||
RUN apk -U add --no-cache \
|
||||
git \
|
||||
libcap \
|
||||
openssl
|
||||
|
||||
WORKDIR /rootfs
|
||||
ARG HOME
|
||||
RUN mkdir -p usr/local/bin $HOME/conf $HOME/database $HOME/logs $HOME/upload
|
||||
|
||||
ARG BUILD_DIR
|
||||
COPY --from=api /go/bin/ejabberdapi usr/local/bin/
|
||||
|
||||
RUN if [ ! -d $HOME/.ejabberd-modules ]; \
|
||||
then \
|
||||
if [ -d $BUILD_DIR/.ejabberd-modules ]; \
|
||||
|
@ -116,11 +102,35 @@ RUN export PEM=$HOME/conf/server.pem \
|
|||
-days 3650 \
|
||||
-subj "/CN=localhost"
|
||||
|
||||
RUN sed -i 's|^#CTL_OVER_HTTP=|CTL_OVER_HTTP=../|' "$HOME/conf/ejabberdctl.cfg"
|
||||
|
||||
RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \
|
||||
&& setcap 'cap_net_bind_service=+ep' $(find $home_root_dir -name beam.smp) \
|
||||
&& echo -e \
|
||||
"#!/bin/sh \
|
||||
\n[ -z \$ERLANG_NODE_ARG ] && export ERLANG_NODE_ARG=ejabberd@localhost \
|
||||
\nexport EMA=\"\$EJABBERD_MACRO_ADMIN\" \
|
||||
\nexport HOST=\"\${EJABBERD_MACRO_HOST:-localhost}\" \
|
||||
\nif [ -n \"\$EMA\" ] \
|
||||
\nthen \
|
||||
\n if [ \"\$EMA\" != \"\${EMA%%@*}\" ] \
|
||||
\n then \
|
||||
\n export USERNAME=\"\${EMA%%@*}\" \
|
||||
\n export HOST=\"\${EMA##*@}\" \
|
||||
\n else \
|
||||
\n export USERNAME=\"\$EMA\" \
|
||||
\n export SHOW_WARNING=\"true\" \
|
||||
\n fi \
|
||||
\nelif [ -n \"\$REGISTER_ADMIN_PASSWORD\" ] \
|
||||
\nthen \
|
||||
\n export USERNAME=\"admin\" \
|
||||
\nelse \
|
||||
\n export USERNAME=\"\$(od -A n -N 8 -t x8 /dev/urandom)\" \
|
||||
\nfi \
|
||||
\nexport EJABBERD_MACRO_ADMIN=\"\$USERNAME@\$HOST\" \
|
||||
\n[ -n \"\$SHOW_WARNING\" ] && echo \"WARNING: The EJABBERD_MACRO_ADMIN environment variable was set to '\$EMA', but it should include the host... I'll overwrite it to become '\$EJABBERD_MACRO_ADMIN'.\" \
|
||||
\n[ -n \"\$CTL_ON_CREATE\" ] && export SEPARATOR=\";\" \
|
||||
\n[ -n \"\$REGISTER_ADMIN_PASSWORD\" ] && export CTL_ON_CREATE=\"register \${EJABBERD_MACRO_ADMIN%%@*} \${EJABBERD_MACRO_ADMIN##*@} \$REGISTER_ADMIN_PASSWORD \$SEPARATOR \$CTL_ON_CREATE\" \
|
||||
\nexport CONFIG_DIR=/$HOME/conf \
|
||||
\nexport LOGS_DIR=/$HOME/logs \
|
||||
\nexport SPOOL_DIR=/$HOME/database \
|
||||
|
@ -137,28 +147,28 @@ RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \
|
|||
ARG UID
|
||||
RUN chown -R $UID:$UID $HOME
|
||||
|
||||
RUN cp /rootfs/$HOME-$VERSION/lib/captcha*.sh usr/local/bin/
|
||||
RUN mkdir $HOME/sql \
|
||||
&& find /rootfs/$HOME-$VERSION/lib/ -name *.sql -exec cp {} $HOME/sql \; -exec cp {} $HOME/database \;
|
||||
|
||||
################################################################################
|
||||
#' METHOD='direct' - Remove erlang/OTP & rebar3
|
||||
FROM docker.io/erlang:${OTP_VSN}-alpine AS runtime-direct
|
||||
#' Remove erlang/OTP & rebar3
|
||||
FROM docker.io/erlang:${OTP_VSN}-alpine AS runtime
|
||||
RUN apk del .erlang-rundeps \
|
||||
&& rm -f $(which rebar3) \
|
||||
&& find /usr -type d -name 'erlang' -exec rm -rf {} + \
|
||||
&& find /usr -type l -exec test ! -e {} \; -delete
|
||||
|
||||
################################################################################
|
||||
#' METHOD='package' - define runtime base image
|
||||
FROM docker.io/alpine:${ALPINE_VSN} AS runtime-package
|
||||
|
||||
################################################################################
|
||||
#' Update alpine, finalize runtime environment
|
||||
FROM runtime-${METHOD} AS runtime
|
||||
COPY --from=ejabberd /tmp/runDeps /tmp/runDeps
|
||||
RUN apk -U upgrade --available --no-cache \
|
||||
&& apk add --no-cache \
|
||||
$(cat /tmp/runDeps) \
|
||||
so:libcap.so.2 \
|
||||
so:libtdsodbc.so.0 \
|
||||
curl \
|
||||
tini \
|
||||
&& rm /tmp/runDeps \
|
||||
&& ln -fs /usr/lib/libtdsodbc.so.0 /usr/lib/libtdsodbc.so
|
||||
|
||||
ARG USER
|
||||
|
@ -167,9 +177,13 @@ ARG HOME
|
|||
RUN addgroup $USER -g $UID \
|
||||
&& adduser -s /sbin/nologin -D -u $UID -h /$HOME -G $USER $USER
|
||||
|
||||
RUN ln -fs /usr/local/bin/ /opt/ejabberd/bin
|
||||
RUN rm -rf /home \
|
||||
&& ln -fs /opt /home
|
||||
|
||||
################################################################################
|
||||
#' Build together production image
|
||||
FROM scratch AS prod
|
||||
FROM scratch
|
||||
ARG USER
|
||||
ARG HOME
|
||||
|
||||
|
@ -186,7 +200,7 @@ HEALTHCHECK \
|
|||
WORKDIR /$HOME
|
||||
USER $USER
|
||||
VOLUME ["/$HOME"]
|
||||
EXPOSE 1883 4369-4399 5210 5222 5269 5280 5443
|
||||
EXPOSE 1880 1883 4369-4399 5210 5222 5269 5280 5443
|
||||
|
||||
ENTRYPOINT ["/sbin/tini","--","ejabberdctl"]
|
||||
CMD ["foreground"]
|
||||
|
|
278
.github/container/ejabberd.yml.example
vendored
Normal file
278
.github/container/ejabberd.yml.example
vendored
Normal file
|
@ -0,0 +1,278 @@
|
|||
###
|
||||
### ejabberd configuration file
|
||||
###
|
||||
### The parameters used in this configuration file are explained at
|
||||
###
|
||||
### https://docs.ejabberd.im/admin/configuration
|
||||
###
|
||||
### The configuration file is written in YAML.
|
||||
### *******************************************************
|
||||
### ******* !!! WARNING !!! *******
|
||||
### ******* YAML IS INDENTATION SENSITIVE *******
|
||||
### ******* MAKE SURE YOU INDENT SECTIONS CORRECTLY *******
|
||||
### *******************************************************
|
||||
### Refer to http://en.wikipedia.org/wiki/YAML for the brief description.
|
||||
###
|
||||
|
||||
define_macro:
|
||||
HOST: localhost
|
||||
## ADMIN: ... # set by /usr/local/bin/ejabberdctl
|
||||
PORT_C2S: 5222
|
||||
PORT_C2S_TLS: 5223
|
||||
PORT_S2S: 5269
|
||||
PORT_HTTP_TLS: 5443
|
||||
PORT_HTTP: 5280
|
||||
PORT_BROWSER: 1880
|
||||
PORT_STUN: 5478
|
||||
PORT_MQTT: 1883
|
||||
PORT_PROXY65: 7777
|
||||
|
||||
hosts:
|
||||
- HOST
|
||||
|
||||
loglevel: info
|
||||
|
||||
## If you already have certificates, list them here
|
||||
# certfiles:
|
||||
# - /etc/letsencrypt/live/domain.tld/fullchain.pem
|
||||
# - /etc/letsencrypt/live/domain.tld/privkey.pem
|
||||
|
||||
ca_file: /opt/ejabberd/conf/cacert.pem
|
||||
certfiles:
|
||||
- /opt/ejabberd/conf/server.pem
|
||||
|
||||
listen:
|
||||
-
|
||||
port: PORT_C2S
|
||||
ip: "::"
|
||||
module: ejabberd_c2s
|
||||
max_stanza_size: 262144
|
||||
shaper: c2s_shaper
|
||||
access: c2s
|
||||
starttls_required: true
|
||||
-
|
||||
port: PORT_C2S_TLS
|
||||
ip: "::"
|
||||
module: ejabberd_c2s
|
||||
max_stanza_size: 262144
|
||||
shaper: c2s_shaper
|
||||
access: c2s
|
||||
tls: true
|
||||
-
|
||||
port: PORT_S2S
|
||||
ip: "::"
|
||||
module: ejabberd_s2s_in
|
||||
max_stanza_size: 524288
|
||||
shaper: s2s_shaper
|
||||
-
|
||||
port: PORT_HTTP_TLS
|
||||
ip: "::"
|
||||
module: ejabberd_http
|
||||
tls: true
|
||||
request_handlers:
|
||||
/admin: ejabberd_web_admin
|
||||
/api: mod_http_api
|
||||
/bosh: mod_bosh
|
||||
/captcha: ejabberd_captcha
|
||||
/upload: mod_http_upload
|
||||
/ws: ejabberd_http_ws
|
||||
-
|
||||
port: PORT_HTTP
|
||||
ip: "::"
|
||||
module: ejabberd_http
|
||||
request_handlers:
|
||||
/admin: ejabberd_web_admin
|
||||
/.well-known/acme-challenge: ejabberd_acme
|
||||
-
|
||||
port: PORT_BROWSER
|
||||
ip: "::"
|
||||
module: ejabberd_http
|
||||
request_handlers:
|
||||
/: ejabberd_web_admin
|
||||
-
|
||||
port: "unix:../sockets/ctl_over_http.sock"
|
||||
module: ejabberd_http
|
||||
unix_socket:
|
||||
mode: '0600'
|
||||
request_handlers:
|
||||
/ctl: ejabberd_ctl
|
||||
-
|
||||
port: PORT_STUN
|
||||
ip: "::"
|
||||
transport: udp
|
||||
module: ejabberd_stun
|
||||
use_turn: true
|
||||
## The server's public IPv4 address:
|
||||
# turn_ipv4_address: "203.0.113.3"
|
||||
## The server's public IPv6 address:
|
||||
# turn_ipv6_address: "2001:db8::3"
|
||||
-
|
||||
port: PORT_MQTT
|
||||
ip: "::"
|
||||
module: mod_mqtt
|
||||
backlog: 1000
|
||||
|
||||
s2s_use_starttls: optional
|
||||
|
||||
acl:
|
||||
local:
|
||||
user_regexp: ""
|
||||
loopback:
|
||||
ip:
|
||||
- 127.0.0.0/8
|
||||
- ::1/128
|
||||
admin:
|
||||
user:
|
||||
- ADMIN
|
||||
|
||||
access_rules:
|
||||
local:
|
||||
allow: local
|
||||
c2s:
|
||||
deny: blocked
|
||||
allow: all
|
||||
announce:
|
||||
allow: admin
|
||||
configure:
|
||||
allow: admin
|
||||
muc_create:
|
||||
allow: local
|
||||
pubsub_createnode:
|
||||
allow: local
|
||||
trusted_network:
|
||||
allow: loopback
|
||||
|
||||
api_permissions:
|
||||
"console commands":
|
||||
from: ejabberd_ctl
|
||||
who: all
|
||||
what: "*"
|
||||
"webadmin commands":
|
||||
from: ejabberd_web_admin
|
||||
who: admin
|
||||
what: "*"
|
||||
"admin access":
|
||||
who:
|
||||
access:
|
||||
allow:
|
||||
- acl: loopback
|
||||
- acl: admin
|
||||
oauth:
|
||||
scope: "ejabberd:admin"
|
||||
access:
|
||||
allow:
|
||||
- acl: loopback
|
||||
- acl: admin
|
||||
what:
|
||||
- "*"
|
||||
- "!stop"
|
||||
- "!start"
|
||||
"public commands":
|
||||
who:
|
||||
ip: 127.0.0.1/8
|
||||
what:
|
||||
- status
|
||||
- connected_users_number
|
||||
|
||||
shaper:
|
||||
normal:
|
||||
rate: 3000
|
||||
burst_size: 20000
|
||||
fast: 100000
|
||||
|
||||
shaper_rules:
|
||||
max_user_sessions: 10
|
||||
max_user_offline_messages:
|
||||
5000: admin
|
||||
100: all
|
||||
c2s_shaper:
|
||||
none: admin
|
||||
normal: all
|
||||
s2s_shaper: fast
|
||||
|
||||
modules:
|
||||
mod_adhoc: {}
|
||||
mod_admin_extra: {}
|
||||
mod_announce:
|
||||
access: announce
|
||||
mod_avatar: {}
|
||||
mod_blocking: {}
|
||||
mod_bosh: {}
|
||||
mod_caps: {}
|
||||
mod_carboncopy: {}
|
||||
mod_client_state: {}
|
||||
mod_configure: {}
|
||||
mod_disco: {}
|
||||
mod_fail2ban: {}
|
||||
mod_http_api: {}
|
||||
mod_http_upload:
|
||||
put_url: https://@HOST@:5443/upload
|
||||
custom_headers:
|
||||
"Access-Control-Allow-Origin": "https://@HOST@"
|
||||
"Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
|
||||
"Access-Control-Allow-Headers": "Content-Type"
|
||||
mod_last: {}
|
||||
mod_mam:
|
||||
## Mnesia is limited to 2GB, better to use an SQL backend
|
||||
## For small servers SQLite is a good fit and is very easy
|
||||
## to configure. Uncomment this when you have SQL configured:
|
||||
## db_type: sql
|
||||
assume_mam_usage: true
|
||||
default: always
|
||||
mod_mqtt: {}
|
||||
mod_muc:
|
||||
access:
|
||||
- allow
|
||||
access_admin:
|
||||
- allow: admin
|
||||
access_create: muc_create
|
||||
access_persistent: muc_create
|
||||
access_mam:
|
||||
- allow
|
||||
default_room_options:
|
||||
mam: true
|
||||
mod_muc_admin: {}
|
||||
mod_offline:
|
||||
access_max_user_messages: max_user_offline_messages
|
||||
mod_ping: {}
|
||||
mod_privacy: {}
|
||||
mod_private: {}
|
||||
mod_proxy65:
|
||||
access: local
|
||||
max_connections: 5
|
||||
port: PORT_PROXY65
|
||||
mod_pubsub:
|
||||
access_createnode: pubsub_createnode
|
||||
plugins:
|
||||
- flat
|
||||
- pep
|
||||
force_node_config:
|
||||
## Avoid buggy clients to make their bookmarks public
|
||||
storage:bookmarks:
|
||||
access_model: whitelist
|
||||
mod_push: {}
|
||||
mod_push_keepalive: {}
|
||||
mod_register:
|
||||
## Only accept registration requests from the "trusted"
|
||||
## network (see access_rules section above).
|
||||
## Think twice before enabling registration from any
|
||||
## address. See the Jabber SPAM Manifesto for details:
|
||||
## https://github.com/ge0rg/jabber-spam-fighting-manifesto
|
||||
ip_access: trusted_network
|
||||
mod_roster:
|
||||
versioning: true
|
||||
mod_s2s_bidi: {}
|
||||
mod_s2s_dialback: {}
|
||||
mod_shared_roster: {}
|
||||
mod_stream_mgmt:
|
||||
resend_on_timeout: if_offline
|
||||
mod_stun_disco: {}
|
||||
mod_vcard: {}
|
||||
mod_vcard_xupdate: {}
|
||||
mod_version:
|
||||
show_os: false
|
||||
|
||||
### Local Variables:
|
||||
### mode: yaml
|
||||
### End:
|
||||
### vim: set filetype=yaml tabstop=8
|
61
.github/container/ejabberdctl.template
vendored
61
.github/container/ejabberdctl.template
vendored
|
@ -305,6 +305,8 @@ stop_epmd()
|
|||
# if all ok, ensure runtime directory exists and make it current directory
|
||||
check_start()
|
||||
{
|
||||
ECSIMAGE_DBPATH=$HOME/database/$ERLANG_NODE
|
||||
[ ! -d "$ECSIMAGE_DBPATH" ] && ln -s $HOME/database $HOME/database/$ERLANG_NODE
|
||||
[ -n "$ERL_DIST_PORT" ] && return
|
||||
"$EPMD" -names 2>/dev/null | grep -q " ${ERLANG_NODE%@*} " && {
|
||||
pgrep -f "$ERLANG_NODE" >/dev/null && {
|
||||
|
@ -377,6 +379,54 @@ wait_status()
|
|||
[ $timeout -gt 0 ]
|
||||
}
|
||||
|
||||
exec_other_command()
|
||||
{
|
||||
if [ -z "$CTL_OVER_HTTP" ] || [ ! -S "$CTL_OVER_HTTP" ] \
|
||||
|| [ ! -x "$(command -v curl)" ] || [ -z "$1" ] || [ "$1" = "help" ] \
|
||||
|| [ "$1" = "mnesia_info_ctl" ]|| [ "$1" = "print_sql_schema" ] ; then
|
||||
run_erl "$(uid ctl)" -hidden -noinput \
|
||||
-eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \
|
||||
-s ejabberd_ctl \
|
||||
-extra "$ERLANG_NODE" $NO_TIMEOUT "$@"
|
||||
result=$?
|
||||
case $result in
|
||||
3) help;;
|
||||
*) :;;
|
||||
esac
|
||||
exit $result
|
||||
else
|
||||
exec_ctl_over_http_socket "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
exec_ctl_over_http_socket()
|
||||
{
|
||||
COMMAND=${1}
|
||||
CARGS=""
|
||||
while [ $# -gt 0 ]; do
|
||||
[ -z "$CARGS" ] && CARGS="[" || CARGS="${CARGS}, "
|
||||
CARGS="${CARGS}\"$1\""
|
||||
shift
|
||||
done
|
||||
CARGS="${CARGS}]"
|
||||
TEMPHEADERS=temp-headers.log
|
||||
curl \
|
||||
--unix-socket ${CTL_OVER_HTTP} \
|
||||
--header "Content-Type: application/json" \
|
||||
--header "Accept: application/json" \
|
||||
--data "${CARGS}" \
|
||||
--dump-header ${TEMPHEADERS} \
|
||||
--no-progress-meter \
|
||||
"http://localhost/ctl/${COMMAND}"
|
||||
result=$(sed -n 's/.*status-code: \([0-9]*\).*/\1/p' < $TEMPHEADERS)
|
||||
rm ${TEMPHEADERS}
|
||||
case $result in
|
||||
2|3) exec_other_command help ${COMMAND};;
|
||||
*) :;;
|
||||
esac
|
||||
exit $result
|
||||
}
|
||||
|
||||
# ensure we can change current directory to SPOOL_DIR
|
||||
[ -f "$SPOOL_DIR/schema.DAT" ] || FIRST_RUN=true
|
||||
[ -d "$SPOOL_DIR" ] || run_cmd mkdir -p "$SPOOL_DIR"
|
||||
|
@ -452,15 +502,6 @@ case $1 in
|
|||
;;
|
||||
*)
|
||||
set_dist_client
|
||||
run_erl "$(uid ctl)" -hidden -noinput \
|
||||
-eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \
|
||||
-s ejabberd_ctl \
|
||||
-extra "$ERLANG_NODE" $NO_TIMEOUT "$@"
|
||||
result=$?
|
||||
case $result in
|
||||
2|3) help;;
|
||||
*) :;;
|
||||
esac
|
||||
exit $result
|
||||
exec_other_command "$@"
|
||||
;;
|
||||
esac
|
||||
|
|
49
.github/workflows/container.yml
vendored
49
.github/workflows/container.yml
vendored
|
@ -1,8 +1,6 @@
|
|||
name: Container
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '22 2 */6 * *' # every 6 days to avoid gha cache being evicted
|
||||
push:
|
||||
paths-ignore:
|
||||
- '.devcontainer/**'
|
||||
|
@ -28,52 +26,6 @@ jobs:
|
|||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache build directory
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/build/
|
||||
key: ${{runner.os}}-ctr-ct-ng-1.27.0
|
||||
|
||||
- name: Get erlang/OTP version for bootstrapping
|
||||
run: |
|
||||
echo "OTP_VSN=$(awk '/^otp_vsn=/ {{gsub(/[^0-9.rc-]/, ""); print}}' tools/make-binaries)" >> $GITHUB_ENV
|
||||
echo "ELIXIR_VSN=$(awk '/^elixir_vsn=/ {{gsub(/[^0-9.]/, ""); print}}' tools/make-binaries)" >> $GITHUB_ENV
|
||||
|
||||
- name: Install prerequisites
|
||||
run: |
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get -qq install makeself
|
||||
# https://github.com/crosstool-ng/crosstool-ng/blob/master/testing/docker/ubuntu21.10/Dockerfile
|
||||
sudo apt-get -qq install build-essential autoconf bison flex gawk
|
||||
sudo apt-get -qq install help2man libncurses5-dev libtool libtool-bin
|
||||
sudo apt-get -qq install python3-dev texinfo unzip
|
||||
|
||||
- name: Install erlang/OTP
|
||||
uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: ${{ env.OTP_VSN }}
|
||||
elixir-version: ${{ env.ELIXIR_VSN }}
|
||||
version-type: strict
|
||||
|
||||
- name: Remove Elixir Matchers
|
||||
run: |
|
||||
echo "::remove-matcher owner=elixir-mixCompileWarning::"
|
||||
echo "::remove-matcher owner=elixir-credoOutputDefault::"
|
||||
echo "::remove-matcher owner=elixir-mixCompileError::"
|
||||
echo "::remove-matcher owner=elixir-mixTestFailure::"
|
||||
echo "::remove-matcher owner=elixir-dialyzerOutputDefault::"
|
||||
|
||||
- name: Build musl-libc based binary archives
|
||||
run: |
|
||||
sed -i "s|targets='.*'|targets='x86_64-linux-musl aarch64-linux-musl'|" tools/make-binaries
|
||||
mv .github/container/ejabberdctl.template .
|
||||
CHECK_DEPS=false tools/make-binaries
|
||||
|
||||
- name: Collect packages
|
||||
run: |
|
||||
mkdir tarballs
|
||||
mv ejabberd-*.tar.gz tarballs
|
||||
|
||||
- name: Checkout ejabberd-contrib
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
|
@ -111,7 +63,6 @@ jobs:
|
|||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
build-args: |
|
||||
METHOD=package
|
||||
VERSION=${{ steps.gitdescribe.outputs.ver }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
|
582
CONTAINER.md
582
CONTAINER.md
|
@ -1,10 +1,10 @@
|
|||
|
||||
[](https://github.com/processone/ejabberd/tags)
|
||||
[](https://github.com/processone/ejabberd/pkgs/container/ejabberd)
|
||||
[](https://github.com/processone/ejabberd/pkgs/container/ejabberd)
|
||||
[](https://hub.docker.com/r/ejabberd/ecs/)
|
||||
|
||||
`ejabberd` Container Image
|
||||
==========================
|
||||
ejabberd Container Images
|
||||
=========================
|
||||
|
||||
[ejabberd][home] is an open-source,
|
||||
robust, scalable and extensible realtime platform built using [Erlang/OTP][erlang],
|
||||
|
@ -16,26 +16,29 @@ that includes [XMPP][xmpp] Server, [MQTT][mqtt] Broker and [SIP][sip] Service.
|
|||
[mqtt]: https://mqtt.org/
|
||||
[sip]: https://en.wikipedia.org/wiki/Session_Initiation_Protocol
|
||||
|
||||
This document explains how to use the `ejabberd` container image available in
|
||||
[ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd),
|
||||
built using the files in `.github/container/`.
|
||||
This image is based in Alpine 3.19, includes Erlang/OTP 27.2 and Elixir 1.18.1.
|
||||
This page documents those container images ([images comparison](#images-comparison)):
|
||||
|
||||
Alternatively, there is also the `ecs` container image available in
|
||||
[docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/),
|
||||
built using the
|
||||
[docker-ejabberd/ecs](https://github.com/processone/docker-ejabberd/tree/master/ecs)
|
||||
repository.
|
||||
Check the [differences between `ejabberd` and `ecs` images](https://github.com/processone/docker-ejabberd/blob/master/ecs/HUB-README.md#alternative-image-in-github).
|
||||
- [](https://github.com/processone/ejabberd/pkgs/container/ejabberd)
|
||||
published in [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd),
|
||||
built using [ejabberd](https://github.com/processone/ejabberd/tree/master/.github/container) repository,
|
||||
both for stable ejabberd releases and the `master` branch, in x64 and arm64 architectures.
|
||||
|
||||
If you are using a Windows operating system, check the tutorials mentioned in
|
||||
[ejabberd Docs > Docker Image](https://docs.ejabberd.im/admin/install/container/#ejabberd-container-image).
|
||||
- [](https://hub.docker.com/r/ejabberd/ecs/)
|
||||
published in [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/),
|
||||
built using [docker-ejabberd/ecs](https://github.com/processone/docker-ejabberd/tree/master/ecs) repository
|
||||
for ejabberd stable releases in x64 architectures.
|
||||
|
||||
For Microsoft Windows, see
|
||||
[Docker Desktop for Windows 10](https://www.process-one.net/blog/install-ejabberd-on-windows-10-using-docker-desktop/),
|
||||
and [Docker Toolbox for Windows 7](https://www.process-one.net/blog/install-ejabberd-on-windows-7-using-docker-toolbox/).
|
||||
|
||||
For Kubernetes Helm, see [help-ejabberd](https://github.com/sando38/helm-ejabberd).
|
||||
|
||||
|
||||
Start ejabberd
|
||||
--------------
|
||||
|
||||
### With default configuration
|
||||
### daemon
|
||||
|
||||
Start ejabberd in a new container:
|
||||
|
||||
|
@ -46,22 +49,28 @@ docker run --name ejabberd -d -p 5222:5222 ghcr.io/processone/ejabberd
|
|||
That runs the container as a daemon,
|
||||
using ejabberd default configuration file and XMPP domain `localhost`.
|
||||
|
||||
Stop the running container:
|
||||
|
||||
```bash
|
||||
docker stop ejabberd
|
||||
```
|
||||
|
||||
Restart the stopped ejabberd container:
|
||||
|
||||
```bash
|
||||
docker restart ejabberd
|
||||
```
|
||||
|
||||
Stop the running container:
|
||||
|
||||
### Start with Erlang console attached
|
||||
```bash
|
||||
docker stop ejabberd
|
||||
```
|
||||
|
||||
Start ejabberd with an Erlang console attached using the `live` command:
|
||||
Remove the ejabberd container:
|
||||
|
||||
```bash
|
||||
docker rm ejabberd
|
||||
```
|
||||
|
||||
|
||||
### with Erlang console
|
||||
|
||||
Start ejabberd with an interactive Erlang console attached using the `live` command:
|
||||
|
||||
```bash
|
||||
docker run --name ejabberd -it -p 5222:5222 ghcr.io/processone/ejabberd live
|
||||
|
@ -70,48 +79,67 @@ docker run --name ejabberd -it -p 5222:5222 ghcr.io/processone/ejabberd live
|
|||
That uses the default configuration file and XMPP domain `localhost`.
|
||||
|
||||
|
||||
### Start with your configuration and database
|
||||
### with your data
|
||||
|
||||
Pass a configuration file as a volume
|
||||
and share the local directory to store database:
|
||||
|
||||
```bash
|
||||
mkdir database
|
||||
chown ejabberd database
|
||||
mkdir conf && cp ejabberd.yml.example conf/ejabberd.yml
|
||||
|
||||
cp ejabberd.yml.example ejabberd.yml
|
||||
mkdir database && chown ejabberd database
|
||||
|
||||
docker run --name ejabberd -it \
|
||||
-v $(pwd)/ejabberd.yml:/opt/ejabberd/conf/ejabberd.yml \
|
||||
-v $(pwd)/conf/ejabberd.yml:/opt/ejabberd/conf/ejabberd.yml \
|
||||
-v $(pwd)/database:/opt/ejabberd/database \
|
||||
-p 5222:5222 ghcr.io/processone/ejabberd live
|
||||
```
|
||||
|
||||
Notice that ejabberd runs in the container with an account named `ejabberd`,
|
||||
Notice that ejabberd runs in the container with an account named `ejabberd`
|
||||
with UID 9000 and group `ejabberd` with GID 9000,
|
||||
and the volumes you mount must grant proper rights to that account.
|
||||
|
||||
|
||||
Next steps
|
||||
----------
|
||||
|
||||
### Register the administrator account
|
||||
### Register admin account
|
||||
|
||||
The default ejabberd configuration does not grant admin privileges
|
||||
to any account,
|
||||
you may want to register a new account in ejabberd
|
||||
and grant it admin rights.
|
||||
#### [](https://github.com/processone/ejabberd/pkgs/container/ejabberd) [:orange_circle:](#images-comparison)
|
||||
|
||||
Register an account using the `ejabberdctl` script:
|
||||
If you set the `REGISTER_ADMIN_PASSWORD` environment variable,
|
||||
an account is automatically registered with that password,
|
||||
and admin privileges are granted to it.
|
||||
The account created depends on what variables you have set:
|
||||
|
||||
- `EJABBERD_MACRO_ADMIN=juliet@example.org` -> `juliet@example.org`
|
||||
- `EJABBERD_MACRO_HOST=example.org` -> `admin@example.org`
|
||||
- None of those variables are set -> `admin@localhost`
|
||||
|
||||
The account registration is shown in the container log:
|
||||
|
||||
```
|
||||
:> ejabberdctl register admin example.org somePassw0rd
|
||||
User admin@example.org successfully registered
|
||||
```
|
||||
|
||||
Alternatively, you can register the account manually yourself
|
||||
and edit `conf/ejabberd.yml` and add the ACL as explained in
|
||||
[ejabberd Docs: Administration Account](https://docs.ejabberd.im/admin/install/next-steps/#administration-account).
|
||||
|
||||
---
|
||||
|
||||
#### [](https://hub.docker.com/r/ejabberd/ecs/)
|
||||
|
||||
The default ejabberd configuration has already granted admin privilege
|
||||
to an account that would be called `admin@localhost`,
|
||||
so you just need to register it, for example:
|
||||
|
||||
```bash
|
||||
docker exec -it ejabberd ejabberdctl register admin localhost passw0rd
|
||||
```
|
||||
|
||||
Then edit conf/ejabberd.yml and add the ACL as explained in
|
||||
[ejabberd Docs: Administration Account](https://docs.ejabberd.im/admin/install/next-steps/#administration-account)
|
||||
|
||||
|
||||
### Check ejabberd log files
|
||||
### Check ejabberd log
|
||||
|
||||
Check the content of the log files inside the container,
|
||||
even if you do not put it on a shared persistent drive:
|
||||
|
@ -121,7 +149,7 @@ docker exec -it ejabberd tail -f logs/ejabberd.log
|
|||
```
|
||||
|
||||
|
||||
### Inspect the container files
|
||||
### Inspect container files
|
||||
|
||||
The container uses Alpine Linux. Start a shell inside the container:
|
||||
|
||||
|
@ -130,7 +158,7 @@ docker exec -it ejabberd sh
|
|||
```
|
||||
|
||||
|
||||
### Open ejabberd debug console
|
||||
### Open debug console
|
||||
|
||||
Open an interactive debug Erlang console attached to a running ejabberd in a running container:
|
||||
|
||||
|
@ -155,7 +183,7 @@ docker exec -it ejabberd vi conf/ejabberd.yml
|
|||
|
||||
and add this option:
|
||||
```yaml
|
||||
captcha_cmd: /opt/ejabberd-22.04/lib/captcha.sh
|
||||
captcha_cmd: "$HOME/bin/captcha.sh"
|
||||
```
|
||||
|
||||
Finally, reload the configuration file or restart the container:
|
||||
|
@ -176,20 +204,22 @@ For more details about CAPTCHA options, please check the
|
|||
documentation section.
|
||||
|
||||
|
||||
Advanced Container Configuration
|
||||
--------------------------------
|
||||
Advanced
|
||||
--------
|
||||
|
||||
### Ports
|
||||
|
||||
This container image exposes the ports:
|
||||
The container image exposes several ports
|
||||
(check also [Docs: Firewall Settings](https://docs.ejabberd.im/admin/guide/security/#firewall-settings)):
|
||||
|
||||
- `5222`: The default port for XMPP clients.
|
||||
- `5269`: For XMPP federation. Only needed if you want to communicate with users on other servers.
|
||||
- `5280`: For admin interface.
|
||||
- `5280`: For admin interface (URL is `admin/`).
|
||||
- `1880`: For admin interface (URL is `/`, useful for [podman-desktop](https://podman-desktop.io/) and [docker-desktop](https://www.docker.com/products/docker-desktop/)) [:orange_circle:](#images-comparison)
|
||||
- `5443`: With encryption, used for admin interface, API, CAPTCHA, OAuth, Websockets and XMPP BOSH.
|
||||
- `1883`: Used for MQTT
|
||||
- `4369-4399`: EPMD and Erlang connectivity, used for `ejabberdctl` and clustering
|
||||
- `5210`: Erlang connectivity when `ERL_DIST_PORT` is set, alternative to EPMD
|
||||
- `5210`: Erlang connectivity when `ERL_DIST_PORT` is set, alternative to EPMD [:orange_circle:](#images-comparison)
|
||||
|
||||
|
||||
### Volumes
|
||||
|
@ -206,11 +236,26 @@ You should back up or export the content of the directory to persistent storage
|
|||
- `/opt/ejabberd/logs/`: Directory containing log files
|
||||
- `/opt/ejabberd/upload/`: Directory containing uploaded files. This should also be backed up.
|
||||
|
||||
All these files are owned by `ejabberd` user inside the container.
|
||||
All these files are owned by an account named `ejabberd` with group `ejabberd` in the container.
|
||||
Its corresponding `UID:GID` is `9000:9000`.
|
||||
If you prefer bind mounts instead of volumes, then
|
||||
you need to map this to valid `UID:GID` on your host to get read/write access on
|
||||
mounted directories.
|
||||
|
||||
It's possible to install additional ejabberd modules using volumes,
|
||||
[this comment](https://github.com/processone/docker-ejabberd/issues/81#issuecomment-1036115146)
|
||||
explains how to install an additional module using docker-compose.
|
||||
If using Docker, try:
|
||||
```bash
|
||||
mkdir database
|
||||
sudo chown 9000:9000 database
|
||||
```
|
||||
|
||||
If using Podman, try:
|
||||
```bash
|
||||
mkdir database
|
||||
podman unshare chown 9000:9000 database
|
||||
```
|
||||
|
||||
It's possible to install additional ejabberd modules using volumes, check
|
||||
[this Docs tutorial](http://docs.ejabberd.im/developer/extending-ejabberd/modules/#your-module-in-ejabberd-modules-with-ejabberd-container).
|
||||
|
||||
|
||||
### Commands on start
|
||||
|
@ -234,10 +279,10 @@ Example usage (or check the [full example](#customized-example)):
|
|||
```
|
||||
|
||||
|
||||
### Macros in environment
|
||||
### Macros in environment [:high_brightness:](#images-comparison)
|
||||
|
||||
ejabberd reads `EJABBERD_MACRO_*` environment variables
|
||||
and uses them to define the `*`
|
||||
and uses them to define the corresponding
|
||||
[macros](https://docs.ejabberd.im/admin/configuration/file-format/#macros-in-configuration-file),
|
||||
overwriting the corresponding macro definition if it was set in the configuration file.
|
||||
This is supported since ejabberd 24.12.
|
||||
|
@ -247,18 +292,61 @@ For example, if you configure this in `ejabberd.yml`:
|
|||
```yaml
|
||||
acl:
|
||||
admin:
|
||||
user: ADMINJID
|
||||
user: ADMIN
|
||||
```
|
||||
|
||||
now you can define the admin account JID using an environment variable:
|
||||
```yaml
|
||||
environment:
|
||||
- EJABBERD_MACRO_ADMINJID=admin@localhost
|
||||
- EJABBERD_MACRO_ADMIN=admin@localhost
|
||||
```
|
||||
|
||||
Check the [full example](#customized-example) for other example.
|
||||
|
||||
|
||||
### ejabberdapi
|
||||
|
||||
When the container is running (and thus ejabberd), you can exec commands inside the container
|
||||
using `ejabberdctl` or any other of the available interfaces, see
|
||||
[Understanding ejabberd "commands"](https://docs.ejabberd.im/developer/ejabberd-api/#understanding-ejabberd-commands)
|
||||
|
||||
Additionally, the container image includes the `ejabberdapi` executable.
|
||||
Please check the [ejabberd-api homepage](https://github.com/processone/ejabberd-api)
|
||||
for configuration and usage details.
|
||||
|
||||
For example, if you configure ejabberd like this:
|
||||
```yaml
|
||||
listen:
|
||||
-
|
||||
port: 5282
|
||||
module: ejabberd_http
|
||||
request_handlers:
|
||||
"/api": mod_http_api
|
||||
|
||||
acl:
|
||||
loopback:
|
||||
ip:
|
||||
- 127.0.0.0/8
|
||||
- ::1/128
|
||||
- ::FFFF:127.0.0.1/128
|
||||
|
||||
api_permissions:
|
||||
"admin access":
|
||||
who:
|
||||
access:
|
||||
allow:
|
||||
acl: loopback
|
||||
what:
|
||||
- "register"
|
||||
```
|
||||
|
||||
Then you could register new accounts with this query:
|
||||
|
||||
```bash
|
||||
docker exec -it ejabberd ejabberdapi register --endpoint=http://127.0.0.1:5282/ --jid=admin@localhost --password=passw0rd
|
||||
```
|
||||
|
||||
|
||||
### Clustering
|
||||
|
||||
When setting several containers to form a
|
||||
|
@ -273,6 +361,8 @@ For this you can either:
|
|||
- edit `conf/ejabberdctl.cfg` and set variables `ERLANG_NODE` and `ERLANG_COOKIE`
|
||||
- set the environment variables `ERLANG_NODE_ARG` and `ERLANG_COOKIE`
|
||||
|
||||
---
|
||||
|
||||
Example to connect a local `ejabberdctl` to a containerized ejabberd:
|
||||
|
||||
1. When creating the container, export port 5210, and set `ERLANG_COOKIE`:
|
||||
|
@ -282,7 +372,7 @@ Example to connect a local `ejabberdctl` to a containerized ejabberd:
|
|||
-p 5210:5210 -p 5222:5222 \
|
||||
ghcr.io/processone/ejabberd
|
||||
```
|
||||
2. Set `ERL_DIST_PORT=5210` in ejabberdctl.cfg of container and local ejabberd
|
||||
2. Set `ERL_DIST_PORT=5210` in `ejabberdctl.cfg` of container and local ejabberd
|
||||
3. Restart the container
|
||||
4. Now use `ejabberdctl` in your local ejabberd deployment
|
||||
|
||||
|
@ -298,19 +388,175 @@ Example using environment variables (see full example [docker-compose.yml](https
|
|||
- ERLANG_COOKIE=dummycookie123
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Build a Container Image
|
||||
-----------------------
|
||||
Once you have the ejabberd nodes properly set and running,
|
||||
you can tell the secondary nodes to join the master node using the
|
||||
[`join_cluster`](https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#join-cluster)
|
||||
API call.
|
||||
|
||||
This container image includes ejabberd as a standalone OTP release built using Elixir.
|
||||
That OTP release is configured with:
|
||||
Example using environment variables (see the full
|
||||
[`docker-compose.yml` clustering example](#clustering-example)):
|
||||
```yaml
|
||||
environment:
|
||||
- ERLANG_NODE_ARG=ejabberd@replica
|
||||
- ERLANG_COOKIE=dummycookie123
|
||||
- CTL_ON_CREATE=join_cluster ejabberd@main
|
||||
```
|
||||
|
||||
### Change Mnesia Node Name
|
||||
|
||||
To use the same Mnesia database in a container with a different hostname,
|
||||
it is necessary to change the old hostname stored in Mnesia.
|
||||
|
||||
This section is equivalent to the ejabberd Documentation
|
||||
[Change Computer Hostname](https://docs.ejabberd.im/admin/guide/managing/#change-computer-hostname),
|
||||
but particularized to containers that use this
|
||||
ecs container image from ejabberd 23.01 or older.
|
||||
|
||||
#### Setup Old Container
|
||||
|
||||
Let's assume a container running ejabberd 23.01 (or older) from
|
||||
this ecs container image, with the database directory binded
|
||||
and one registered account.
|
||||
This can be produced with:
|
||||
```bash
|
||||
OLDCONTAINER=ejaold
|
||||
NEWCONTAINER=ejanew
|
||||
|
||||
mkdir database
|
||||
sudo chown 9000:9000 database
|
||||
docker run -d --name $OLDCONTAINER -p 5222:5222 \
|
||||
-v $(pwd)/database:/opt/ejabberd/database \
|
||||
ghcr.io/processone/ejabberd:23.01
|
||||
docker exec -it $OLDCONTAINER ejabberdctl started
|
||||
docker exec -it $OLDCONTAINER ejabberdctl register user1 localhost somepass
|
||||
docker exec -it $OLDCONTAINER ejabberdctl registered_users localhost
|
||||
```
|
||||
|
||||
Methods to know the Erlang node name:
|
||||
```bash
|
||||
ls database/ | grep ejabberd@
|
||||
docker exec -it $OLDCONTAINER ejabberdctl status
|
||||
docker exec -it $OLDCONTAINER grep "started in the node" logs/ejabberd.log
|
||||
```
|
||||
|
||||
#### Change Mnesia Node
|
||||
|
||||
First of all let's store the Erlang node names and paths in variables.
|
||||
In this example they would be:
|
||||
```bash
|
||||
OLDCONTAINER=ejaold
|
||||
NEWCONTAINER=ejanew
|
||||
OLDNODE=ejabberd@95145ddee27c
|
||||
NEWNODE=ejabberd@localhost
|
||||
OLDFILE=/opt/ejabberd/database/old.backup
|
||||
NEWFILE=/opt/ejabberd/database/new.backup
|
||||
```
|
||||
|
||||
1. Start your old container that can still read the Mnesia database correctly.
|
||||
If you have the Mnesia spool files,
|
||||
but don't have access to the old container anymore, go to
|
||||
[Create Temporary Container](#create-temporary-container)
|
||||
and later come back here.
|
||||
|
||||
2. Generate a backup file and check it was created:
|
||||
```bash
|
||||
docker exec -it $OLDCONTAINER ejabberdctl backup $OLDFILE
|
||||
ls -l database/*.backup
|
||||
```
|
||||
|
||||
3. Stop ejabberd:
|
||||
```bash
|
||||
docker stop $OLDCONTAINER
|
||||
```
|
||||
|
||||
4. Create the new container. For example:
|
||||
```bash
|
||||
docker run \
|
||||
--name $NEWCONTAINER \
|
||||
-d \
|
||||
-p 5222:5222 \
|
||||
-v $(pwd)/database:/opt/ejabberd/database \
|
||||
ghcr.io/processone/ejabberd:latest
|
||||
```
|
||||
|
||||
5. Convert the backup file to new node name:
|
||||
```bash
|
||||
docker exec -it $NEWCONTAINER ejabberdctl mnesia_change_nodename $OLDNODE $NEWNODE $OLDFILE $NEWFILE
|
||||
```
|
||||
|
||||
6. Install the backup file as a fallback:
|
||||
```bash
|
||||
docker exec -it $NEWCONTAINER ejabberdctl install_fallback $NEWFILE
|
||||
```
|
||||
|
||||
7. Restart the container:
|
||||
```bash
|
||||
docker restart $NEWCONTAINER
|
||||
```
|
||||
|
||||
8. Check that the information of the old database is available.
|
||||
In this example, it should show that the account `user1` is registered:
|
||||
```bash
|
||||
docker exec -it $NEWCONTAINER ejabberdctl registered_users localhost
|
||||
```
|
||||
|
||||
9. When the new container is working perfectly with the converted Mnesia database,
|
||||
you may want to remove the unneeded files:
|
||||
the old container, the old Mnesia spool files, and the backup files.
|
||||
|
||||
#### Create Temporary Container
|
||||
|
||||
In case the old container that used the Mnesia database is not available anymore,
|
||||
a temporary container can be created just to read the Mnesia database
|
||||
and make a backup of it, as explained in the previous section.
|
||||
|
||||
This method uses `--hostname` command line argument for docker,
|
||||
and `ERLANG_NODE_ARG` environment variable for ejabberd.
|
||||
Their values must be the hostname of your old container
|
||||
and the Erlang node name of your old ejabberd node.
|
||||
To know the Erlang node name please check
|
||||
[Setup Old Container](#setup-old-container).
|
||||
|
||||
Command line example:
|
||||
```bash
|
||||
OLDHOST=${OLDNODE#*@}
|
||||
docker run \
|
||||
-d \
|
||||
--name $OLDCONTAINER \
|
||||
--hostname $OLDHOST \
|
||||
-p 5222:5222 \
|
||||
-v $(pwd)/database:/opt/ejabberd/database \
|
||||
-e ERLANG_NODE_ARG=$OLDNODE \
|
||||
ghcr.io/processone/ejabberd:latest
|
||||
```
|
||||
|
||||
Check the old database content is available:
|
||||
```bash
|
||||
docker exec -it $OLDCONTAINER ejabberdctl registered_users localhost
|
||||
```
|
||||
|
||||
Now that you have ejabberd running with access to the Mnesia database,
|
||||
you can continue with step 2 of previous section
|
||||
[Change Mnesia Node](#change-mnesia-node).
|
||||
|
||||
|
||||
Build Container Image
|
||||
----------------
|
||||
|
||||
The container image includes ejabberd as a standalone OTP release built using Elixir.
|
||||
|
||||
### Build `ejabberd` [](https://github.com/processone/ejabberd/pkgs/container/ejabberd)
|
||||
|
||||
The ejabberd Erlang/OTP release is configured with:
|
||||
|
||||
- `mix.exs`: Customize ejabberd release
|
||||
- `vars.config`: ejabberd compilation configuration options
|
||||
- `config/runtime.exs`: Customize ejabberd paths
|
||||
- `ejabberd.yml.template`: ejabberd default config file
|
||||
|
||||
### Direct build
|
||||
#### Direct build
|
||||
|
||||
Build ejabberd Community Server container image from ejabberd master git repository:
|
||||
|
||||
|
@ -321,7 +567,7 @@ docker buildx build \
|
|||
.
|
||||
```
|
||||
|
||||
### Podman build
|
||||
#### Podman build
|
||||
|
||||
To build the image using Podman, please notice:
|
||||
|
||||
|
@ -346,30 +592,27 @@ podman stop eja1
|
|||
podman run --name eja1 -it -e EJABBERD_BYPASS_WARNINGS=true -p 5222:5222 localhost/ejabberd live
|
||||
```
|
||||
|
||||
### Package build for `arm64`
|
||||
### Build `ecs` [](https://hub.docker.com/r/ejabberd/ecs/)
|
||||
|
||||
By default, `.github/container/Dockerfile` builds this container by directly compiling ejabberd,
|
||||
it is a fast and direct method.
|
||||
However, a problem with QEMU prevents building the container in QEMU using Erlang/OTP 25
|
||||
for the `arm64` architecture.
|
||||
The ejabberd Erlang/OTP release is configured with:
|
||||
|
||||
Providing `--build-arg METHOD=package` is an alternate method to build the container
|
||||
used by the Github Actions workflow that provides `amd64` and `arm64` container images.
|
||||
It first builds an ejabberd binary package, and later installs it in the image.
|
||||
That method avoids using QEMU, so it can build `arm64` container images, but is extremely
|
||||
slow the first time it's used, and consequently not recommended for general use.
|
||||
- `rel/config.exs`: Customize ejabberd release
|
||||
- `rel/dev.exs`: ejabberd environment configuration for development release
|
||||
- `rel/prod.exs`: ejabberd environment configuration for production release
|
||||
- `vars.config`: ejabberd compilation configuration options
|
||||
- `conf/ejabberd.yml`: ejabberd default config file
|
||||
|
||||
In this case, to build the ejabberd container image for arm64 architecture:
|
||||
Build ejabberd Community Server base image from ejabberd master on Github:
|
||||
|
||||
```bash
|
||||
docker buildx build \
|
||||
--build-arg METHOD=package \
|
||||
--platform linux/arm64 \
|
||||
-t personal/ejabberd:$VERSION \
|
||||
-f .github/container/Dockerfile \
|
||||
.
|
||||
docker build -t personal/ejabberd .
|
||||
```
|
||||
|
||||
Build ejabberd Community Server base image for a given ejabberd version:
|
||||
|
||||
```bash
|
||||
./build.sh 18.03
|
||||
```
|
||||
|
||||
Composer Examples
|
||||
-----------------
|
||||
|
@ -430,26 +673,21 @@ stores the mnesia database in a local path,
|
|||
registers an account when it's created,
|
||||
and checks the number of registered accounts every time it's started.
|
||||
|
||||
Download or copy the ejabberd configuration file:
|
||||
Prepare an ejabberd configuration file:
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/processone/ejabberd/master/ejabberd.yml.example
|
||||
mv ejabberd.yml.example ejabberd.yml
|
||||
```
|
||||
|
||||
Use a macro in `ejabberd.yml` to set the served vhost, with `localhost` as default value:
|
||||
```yaml
|
||||
define_macro:
|
||||
XMPPHOST: localhost
|
||||
|
||||
hosts:
|
||||
- XMPPHOST
|
||||
mkdir conf && cp ejabberd.yml.example conf/ejabberd.yml
|
||||
```
|
||||
|
||||
Create the database directory and allow the container access to it:
|
||||
```bash
|
||||
mkdir database
|
||||
sudo chown 9000:9000 database
|
||||
```
|
||||
|
||||
- Docker:
|
||||
```bash
|
||||
mkdir database && sudo chown 9000:9000 database
|
||||
```
|
||||
- Podman:
|
||||
```bash
|
||||
mkdir database && podman unshare chown 9000:9000 database
|
||||
```
|
||||
|
||||
If using Docker, write this `docker-compose.yml` file
|
||||
and start it with `docker-compose up`:
|
||||
|
@ -463,8 +701,9 @@ services:
|
|||
image: ghcr.io/processone/ejabberd
|
||||
container_name: ejabberd
|
||||
environment:
|
||||
- EJABBERD_MACRO_XMPPHOST=example.com
|
||||
- CTL_ON_CREATE=register admin example.com asd
|
||||
- EJABBERD_MACRO_HOST=example.com
|
||||
- EJABBERD_MACRO_ADMIN=admin@example.com
|
||||
- REGISTER_ADMIN_PASSWORD=somePassw0rd
|
||||
- CTL_ON_START=registered_users example.com ;
|
||||
status
|
||||
ports:
|
||||
|
@ -473,7 +712,7 @@ services:
|
|||
- "5280:5280"
|
||||
- "5443:5443"
|
||||
volumes:
|
||||
- ./ejabberd.yml:/opt/ejabberd/conf/ejabberd.yml:ro
|
||||
- ./conf/ejabberd.yml:/opt/ejabberd/conf/ejabberd.yml:ro
|
||||
- ./database:/opt/ejabberd/database
|
||||
```
|
||||
|
||||
|
@ -494,8 +733,12 @@ spec:
|
|||
- name: ejabberd
|
||||
image: ghcr.io/processone/ejabberd
|
||||
env:
|
||||
- name: CTL_ON_CREATE
|
||||
value: register admin example.com asd
|
||||
- name: EJABBERD_MACRO_HOST
|
||||
value: example.com
|
||||
- name: EJABBERD_MACRO_ADMIN
|
||||
value: admin@example.com
|
||||
- name: REGISTER_ADMIN_PASSWORD
|
||||
value: somePassw0rd
|
||||
- name: CTL_ON_START
|
||||
value: registered_users example.com ;
|
||||
status
|
||||
|
@ -518,7 +761,7 @@ spec:
|
|||
volumes:
|
||||
- name: config
|
||||
hostPath:
|
||||
path: ./ejabberd.yml
|
||||
path: ./conf/ejabberd.yml
|
||||
type: File
|
||||
- name: db
|
||||
hostPath:
|
||||
|
@ -551,11 +794,17 @@ services:
|
|||
|
||||
main:
|
||||
image: ghcr.io/processone/ejabberd
|
||||
container_name: ejabberd
|
||||
container_name: main
|
||||
environment:
|
||||
- ERLANG_NODE_ARG=ejabberd@main
|
||||
- ERLANG_COOKIE=dummycookie123
|
||||
- CTL_ON_CREATE=! register admin localhost asd
|
||||
healthcheck:
|
||||
test: netstat -nl | grep -q 5222
|
||||
start_period: 5s
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 120
|
||||
|
||||
replica:
|
||||
image: ghcr.io/processone/ejabberd
|
||||
|
@ -654,92 +903,41 @@ spec:
|
|||
|
||||
```
|
||||
|
||||
Your configuration file should use those macros to allow each ejabberd node
|
||||
use different listening port numbers:
|
||||
|
||||
```diff
|
||||
diff --git a/ejabberd.yml.example b/ejabberd.yml.example
|
||||
index 39e423a64..6e875b48f 100644
|
||||
--- a/ejabberd.yml.example
|
||||
+++ b/ejabberd.yml.example
|
||||
@@ -24,9 +24,19 @@ loglevel: info
|
||||
# - /etc/letsencrypt/live/domain.tld/fullchain.pem
|
||||
# - /etc/letsencrypt/live/domain.tld/privkey.pem
|
||||
|
||||
+define_macro:
|
||||
+ PORT_C2S: 5222
|
||||
+ PORT_C2S_TLS: 5223
|
||||
+ PORT_S2S: 5269
|
||||
+ PORT_HTTP_TLS: 5443
|
||||
+ PORT_HTTP: 5280
|
||||
+ PORT_STUN: 5478
|
||||
+ PORT_MQTT: 1883
|
||||
+ PORT_PROXY65: 7777
|
||||
+
|
||||
listen:
|
||||
-
|
||||
- port: 5222
|
||||
+ port: PORT_C2S
|
||||
ip: "::"
|
||||
module: ejabberd_c2s
|
||||
max_stanza_size: 262144
|
||||
@@ -34,7 +44,7 @@ listen:
|
||||
access: c2s
|
||||
starttls_required: true
|
||||
-
|
||||
- port: 5223
|
||||
+ port: PORT_C2S_TLS
|
||||
ip: "::"
|
||||
module: ejabberd_c2s
|
||||
max_stanza_size: 262144
|
||||
@@ -42,13 +52,13 @@ listen:
|
||||
access: c2s
|
||||
tls: true
|
||||
-
|
||||
- port: 5269
|
||||
+ port: PORT_S2S
|
||||
ip: "::"
|
||||
module: ejabberd_s2s_in
|
||||
max_stanza_size: 524288
|
||||
shaper: s2s_shaper
|
||||
-
|
||||
- port: 5443
|
||||
+ port: PORT_HTTP_TLS
|
||||
ip: "::"
|
||||
module: ejabberd_http
|
||||
tls: true
|
||||
@@ -60,14 +70,14 @@ listen:
|
||||
/upload: mod_http_upload
|
||||
/ws: ejabberd_http_ws
|
||||
-
|
||||
- port: 5280
|
||||
+ port: PORT_HTTP
|
||||
ip: "::"
|
||||
module: ejabberd_http
|
||||
request_handlers:
|
||||
/admin: ejabberd_web_admin
|
||||
/.well-known/acme-challenge: ejabberd_acme
|
||||
-
|
||||
- port: 5478
|
||||
+ port: PORT_STUN
|
||||
ip: "::"
|
||||
transport: udp
|
||||
module: ejabberd_stun
|
||||
@@ -77,7 +87,7 @@ listen:
|
||||
## The server's public IPv6 address:
|
||||
# turn_ipv6_address: "2001:db8::3"
|
||||
-
|
||||
- port: 1883
|
||||
+ port: PORT_MQTT
|
||||
ip: "::"
|
||||
module: mod_mqtt
|
||||
backlog: 1000
|
||||
@@ -207,6 +217,7 @@ modules:
|
||||
mod_proxy65:
|
||||
access: local
|
||||
max_connections: 5
|
||||
+ port: PORT_PROXY65
|
||||
mod_pubsub:
|
||||
access_createnode: pubsub_createnode
|
||||
plugins:
|
||||
```
|
||||
Images Comparison
|
||||
-----------------
|
||||
|
||||
Let's summarize the differences between both container images. Legend:
|
||||
|
||||
- :sparkle: is the recommended alternative
|
||||
- :orange_circle: added in the latest release (ejabberd 25.xx)
|
||||
- :high_brightness: added in the previous release (ejabberd 24.12)
|
||||
- :low_brightness: added in the pre-previous release (ejabberd 24.10)
|
||||
|
||||
| | [](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [](https://hub.docker.com/r/ejabberd/ecs/) |
|
||||
|:----------------------|:------------------|:-----------------------|
|
||||
| Source code | [ejabberd/.github/container](https://github.com/processone/ejabberd/tree/master/.github/container) | [docker-ejabberd/ecs](https://github.com/processone/docker-ejabberd/tree/master/ecs) |
|
||||
| Generated by | [container.yml](https://github.com/processone/ejabberd/blob/master/.github/workflows/container.yml) | [tests.yml](https://github.com/processone/docker-ejabberd/blob/master/.github/workflows/tests.yml) |
|
||||
| Built for | stable releases <br /> `master` branch | stable releases <br /> [`master` branch zip](https://github.com/processone/docker-ejabberd/actions/workflows/tests.yml) |
|
||||
| Architectures | `linux/amd64` <br /> `linux/arm64` | `linux/amd64` |
|
||||
| Software | Erlang/OTP 27.2-alpine <br /> Elixir 1.18.1 | Alpine 3.19 <br /> Erlang/OTP 26.2 <br /> Elixir 1.15.7 |
|
||||
| Published in | [ghcr.io/processone/ejabberd](https://github.com/processone/ejabberd/pkgs/container/ejabberd) | [docker.io/ejabberd/ecs](https://hub.docker.com/r/ejabberd/ecs/) <br /> [ghcr.io/processone/ecs](https://github.com/processone/docker-ejabberd/pkgs/container/ecs) |
|
||||
| :black_square_button: **Additional content** |
|
||||
| [ejabberd-contrib](https://docs.ejabberd.im/admin/guide/modules/#ejabberd-contrib) | included | not included |
|
||||
| [ejabberdapi](#ejabberdapi) | included :orange_circle: | included |
|
||||
| :black_square_button: **Ports** |
|
||||
| [1880](#ports) for WebAdmin | yes :orange_circle: | yes :orange_circle: |
|
||||
| [5210](#ports) for `ERL_DIST_PORT` | supported | supported :orange_circle: |
|
||||
| :black_square_button: **Paths** |
|
||||
| `$HOME` | `/opt/ejabberd/` | `/home/ejabberd/` |
|
||||
| User data | `$HOME` :sparkle: <br /> `/home/ejabberd/` :orange_circle: | `$HOME` <br /> `/opt/ejabberd/` :sparkle: :low_brightness: |
|
||||
| `ejabberdctl` | `ejabberdctl` :sparkle: <br /> `bin/ejabberdctl` :orange_circle: | `bin/ejabberdctl` <br /> `ejabberdctl` :sparkle: :low_brightness: |
|
||||
| [`captcha.sh`](#captcha) | `$HOME/bin/captcha.sh` :orange_circle: | `$HOME/bin/captcha.sh` :orange_circle: |
|
||||
| `*.sql` files | `$HOME/sql/*.sql` :sparkle: :orange_circle: <br /> `$HOME/database/*.sql` :orange_circle: | `$HOME/database/*.sql` <br /> `$HOME/sql/*.sql` :sparkle: :orange_circle: |
|
||||
| Mnesia spool files | `$HOME/database/` :sparkle: <br /> `$HOME/database/NODENAME/` :orange_circle: | `$HOME/database/NODENAME/` <br /> `$HOME/database/` :sparkle: :orange_circle: |
|
||||
| :black_square_button: **Variables** |
|
||||
| [`EJABBERD_MACRO_*`](#macros-in-environment) | supported :high_brightness: | supported :high_brightness: |
|
||||
| Macros used in `ejabberd.yml` | yes :orange_circle: | yes :orange_circle: |
|
||||
| [`EJABBERD_MACRO_ADMIN`](#register-admin-account) | Grant admin rights :orange_circle: <br /> (default `admin@localhost`) <br /> | Hardcoded `admin@localhost` |
|
||||
| [`REGISTER_ADMIN_PASSWORD`](#register-admin-account) | Register admin account :orange_circle: | unsupported |
|
||||
| `CTL_OVER_HTTP` | enabled :orange_circle: | unsupported |
|
||||
|
|
|
@ -198,6 +198,17 @@
|
|||
#
|
||||
#CONTRIB_MODULES_CONF_DIR=/etc/ejabberd/modules
|
||||
|
||||
#.
|
||||
#' CTL_OVER_HTTP: Path to ejabberdctl HTTP listener socket
|
||||
#
|
||||
# To speedup ejabberdctl execution time for ejabberd commands,
|
||||
# you can setup an ejabberd_http listener with ejabberd_ctl handling requests,
|
||||
# listening in a unix domain socket.
|
||||
#
|
||||
# Default: disabled
|
||||
#
|
||||
#CTL_OVER_HTTP=sockets/ctl_over_http.sock
|
||||
|
||||
#.
|
||||
#'
|
||||
# vim: foldmarker=#',#. foldmethod=marker:
|
||||
|
|
|
@ -179,7 +179,9 @@ livewarning()
|
|||
echo "Please be extremely cautious with your actions,"
|
||||
echo "and exit immediately if you are not completely sure."
|
||||
echo ""
|
||||
echo "To exit and detach this shell from ejabberd, press:"
|
||||
echo "To stop ejabberd gracefully:"
|
||||
echo " ejabberd:stop()."
|
||||
echo "To quit erlang immediately, press:"
|
||||
echo " control+g and then q"
|
||||
echo ""
|
||||
echo "--------------------------------------------------------------------"
|
||||
|
@ -332,6 +334,54 @@ wait_status()
|
|||
[ $timeout -gt 0 ]
|
||||
}
|
||||
|
||||
exec_other_command()
|
||||
{
|
||||
if [ -z "$CTL_OVER_HTTP" ] || [ ! -S "$CTL_OVER_HTTP" ] \
|
||||
|| [ ! -x "$(command -v curl)" ] || [ -z "$1" ] || [ "$1" = "help" ] \
|
||||
|| [ "$1" = "mnesia_info_ctl" ]|| [ "$1" = "print_sql_schema" ] ; then
|
||||
exec_erl "$(uid ctl)" -hidden -noinput \
|
||||
-eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \
|
||||
-s ejabberd_ctl \
|
||||
-extra "$ERLANG_NODE" $NO_TIMEOUT "$@"
|
||||
result=$?
|
||||
case $result in
|
||||
3) help;;
|
||||
*) :;;
|
||||
esac
|
||||
exit $result
|
||||
else
|
||||
exec_ctl_over_http_socket "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
exec_ctl_over_http_socket()
|
||||
{
|
||||
COMMAND=${1}
|
||||
CARGS=""
|
||||
while [ $# -gt 0 ]; do
|
||||
[ -z "$CARGS" ] && CARGS="[" || CARGS="${CARGS}, "
|
||||
CARGS="${CARGS}\"$1\""
|
||||
shift
|
||||
done
|
||||
CARGS="${CARGS}]"
|
||||
TEMPHEADERS=temp-headers.log
|
||||
curl \
|
||||
--unix-socket ${CTL_OVER_HTTP} \
|
||||
--header "Content-Type: application/json" \
|
||||
--header "Accept: application/json" \
|
||||
--data "${CARGS}" \
|
||||
--dump-header ${TEMPHEADERS} \
|
||||
--no-progress-meter \
|
||||
"http://localhost/ctl/${COMMAND}"
|
||||
result=$(sed -n 's/.*status-code: \([0-9]*\).*/\1/p' < $TEMPHEADERS)
|
||||
rm ${TEMPHEADERS}
|
||||
case $result in
|
||||
2|3) exec_other_command help ${COMMAND};;
|
||||
*) :;;
|
||||
esac
|
||||
exit $result
|
||||
}
|
||||
|
||||
# ensure we can change current directory to SPOOL_DIR
|
||||
[ -d "$SPOOL_DIR" ] || exec_cmd mkdir -p "$SPOOL_DIR"
|
||||
cd "$SPOOL_DIR" || {
|
||||
|
@ -400,15 +450,6 @@ case $1 in
|
|||
;;
|
||||
*)
|
||||
set_dist_client
|
||||
exec_erl "$(uid ctl)" -hidden -noinput \
|
||||
-eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \
|
||||
-s ejabberd_ctl \
|
||||
-extra "$ERLANG_NODE" $NO_TIMEOUT "$@"
|
||||
result=$?
|
||||
case $result in
|
||||
2|3) help;;
|
||||
*) :;;
|
||||
esac
|
||||
exit $result
|
||||
exec_other_command "$@"
|
||||
;;
|
||||
esac
|
||||
|
|
|
@ -663,7 +663,7 @@ status() ->
|
|||
{value, {_, _, Version}} ->
|
||||
{ok, io_lib:format("ejabberd ~s is running in that node", [Version])}
|
||||
end,
|
||||
{Is_running, String1 ++ " " ++String2}.
|
||||
{Is_running, String1 ++ "\n" ++String2}.
|
||||
|
||||
stop() ->
|
||||
_ = supervisor:terminate_child(ejabberd_sup, ejabberd_sm),
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
-behaviour(gen_server).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start/0, start_link/0, process/1, process2/2]).
|
||||
-export([start/0, start_link/0, process/1, process/2, process2/2]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
@ -37,6 +37,7 @@
|
|||
|
||||
-include("ejabberd_ctl.hrl").
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_stacktrace.hrl").
|
||||
|
||||
|
@ -115,15 +116,44 @@ code_change(_OldVsn, State, _Extra) ->
|
|||
{ok, State}.
|
||||
|
||||
%%-----------------------------
|
||||
%% Process
|
||||
%% Process http
|
||||
%%-----------------------------
|
||||
|
||||
-spec process_http([binary()], tuple()) -> {non_neg_integer(), [{binary(), binary()}], string()}.
|
||||
|
||||
process_http([_Call], #request{data = Data, path = [<<"ctl">> | _]}) ->
|
||||
Args = [binary_to_list(E) || E <- misc:json_decode(Data)],
|
||||
process_http2(Args, ?DEFAULT_VERSION).
|
||||
|
||||
process_http2(["--version", Arg | Args], _) ->
|
||||
Version =
|
||||
try
|
||||
list_to_integer(Arg)
|
||||
catch _:_ ->
|
||||
throw({invalid_version, Arg})
|
||||
end,
|
||||
process_http2(Args, Version);
|
||||
|
||||
process_http2(Args, Version) ->
|
||||
{String, Code} = process2(Args, [], Version),
|
||||
String2 = case String of
|
||||
[] -> String;
|
||||
_ -> [String, "\n"]
|
||||
end,
|
||||
{200, [{<<"status-code">>, integer_to_binary(Code)}], String2}.
|
||||
|
||||
%%-----------------------------
|
||||
%% Process command line
|
||||
%%-----------------------------
|
||||
|
||||
-spec process([string()]) -> non_neg_integer().
|
||||
process(Args) ->
|
||||
process(Args, ?DEFAULT_VERSION).
|
||||
|
||||
-spec process([string() | binary()], non_neg_integer() | tuple()) -> non_neg_integer().
|
||||
|
||||
-spec process([string()], non_neg_integer()) -> non_neg_integer().
|
||||
process([Call], Request) when is_binary(Call) and is_record(Request, request) ->
|
||||
process_http([Call], Request);
|
||||
|
||||
%% The commands status, stop and restart are defined here to ensure
|
||||
%% they are usable even if ejabberd is completely stopped.
|
||||
|
@ -232,7 +262,7 @@ process2(Args, AccessCommands, Auth, Version) ->
|
|||
io:format(lists:flatten(["\n" | String]++["\n"])),
|
||||
[CommandString | _] = Args,
|
||||
process(["help" | [CommandString]], Version),
|
||||
{lists:flatten(String), ?STATUS_ERROR};
|
||||
{lists:flatten(String), ?STATUS_USAGE};
|
||||
{String, Code}
|
||||
when is_list(String) and is_integer(Code) ->
|
||||
{lists:flatten(String), Code};
|
||||
|
@ -271,7 +301,7 @@ try_run_ctp(Args, Auth, AccessCommands, Version) ->
|
|||
try_call_command(Args, Auth, AccessCommands, Version);
|
||||
false ->
|
||||
print_usage(Version),
|
||||
{"", ?STATUS_USAGE};
|
||||
{"", ?STATUS_BADRPC};
|
||||
Status ->
|
||||
{"", Status}
|
||||
catch
|
||||
|
@ -288,7 +318,7 @@ try_run_ctp(Args, Auth, AccessCommands, Version) ->
|
|||
try_call_command(Args, Auth, AccessCommands, Version) ->
|
||||
try call_command(Args, Auth, AccessCommands, Version) of
|
||||
{Reason, wrong_command_arguments} ->
|
||||
{Reason, ?STATUS_ERROR};
|
||||
{Reason, ?STATUS_USAGE};
|
||||
Res ->
|
||||
Res
|
||||
catch
|
||||
|
@ -550,7 +580,8 @@ print_usage(HelpMode, MaxC, ShCode, Version) ->
|
|||
AllCommands = get_list_commands(Version),
|
||||
|
||||
print(
|
||||
["Usage: ", "ejabberdctl", " [--no-timeout] [--node ", ?A("nodename"), "] [--version ", ?A("api_version"), "] ",
|
||||
["Usage: ", "ejabberdctl", " [--no-timeout] [--node ", ?A("name"), "] [--version ", ?A("apiv"), "] ",
|
||||
"[--auth ", ?A("user host pass"), "] ",
|
||||
?C("command"), " [", ?A("arguments"), "]\n"
|
||||
"\n"
|
||||
"Available commands in this ejabberd node:\n"], []),
|
||||
|
|
|
@ -216,18 +216,13 @@ listen_tcp(Port, SockOpts) ->
|
|||
|
||||
setup_provisional_udsocket_dir(DefinitivePath) ->
|
||||
ProvisionalPath = get_provisional_udsocket_path(DefinitivePath),
|
||||
SocketDir = filename:dirname(ProvisionalPath),
|
||||
file:make_dir(SocketDir),
|
||||
file:change_mode(SocketDir, 8#00700),
|
||||
?DEBUG("Creating a Unix Domain Socket provisional file at ~ts for the definitive path ~s",
|
||||
[ProvisionalPath, DefinitivePath]),
|
||||
ProvisionalPath.
|
||||
|
||||
get_provisional_udsocket_path(Path) ->
|
||||
MnesiaDir = mnesia:system_info(directory),
|
||||
SocketDir = filename:join(MnesiaDir, "socket"),
|
||||
PathBase64 = misc:term_to_base64(Path),
|
||||
PathBuild = filename:join(SocketDir, PathBase64),
|
||||
PathBuild = filename:join(os:getenv("HOME"), PathBase64),
|
||||
%% Shorthen the path, a long path produces a crash when opening the socket.
|
||||
binary:part(PathBuild, {0, erlang:min(107, byte_size(PathBuild))}).
|
||||
|
||||
|
@ -236,11 +231,10 @@ get_definitive_udsocket_path(<<"unix", _>> = Unix) ->
|
|||
get_definitive_udsocket_path(ProvisionalPath) ->
|
||||
PathBase64 = filename:basename(ProvisionalPath),
|
||||
{term, Path} = misc:base64_to_term(PathBase64),
|
||||
Path.
|
||||
relative_socket_to_mnesia(Path).
|
||||
|
||||
set_definitive_udsocket(<<"unix:", Path/binary>>, Opts) ->
|
||||
Prov = get_provisional_udsocket_path(Path),
|
||||
timer:sleep(5000),
|
||||
Usd = maps:get(unix_socket, Opts),
|
||||
case maps:get(mode, Usd, undefined) of
|
||||
undefined -> ok;
|
||||
|
@ -268,10 +262,33 @@ set_definitive_udsocket(<<"unix:", Path/binary>>, Opts) ->
|
|||
throw({error_setting_socket_group, Group, Prov})
|
||||
end
|
||||
end,
|
||||
file:rename(Prov, Path);
|
||||
FinalPath = relative_socket_to_mnesia(Path),
|
||||
FinalPathDir = filename:dirname(FinalPath),
|
||||
case file:make_dir(FinalPathDir) of
|
||||
ok ->
|
||||
file:change_mode(FinalPathDir, 8#00700);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
file:rename(Prov, FinalPath);
|
||||
set_definitive_udsocket(_Port, _Opts) ->
|
||||
ok.
|
||||
|
||||
relative_socket_to_mnesia(Path1) ->
|
||||
case filename:pathtype(Path1) of
|
||||
absolute ->
|
||||
Path1;
|
||||
relative ->
|
||||
MnesiaDir = mnesia:system_info(directory),
|
||||
filename:join(MnesiaDir, Path1)
|
||||
end.
|
||||
|
||||
maybe_delete_udsocket_file(<<"unix:", Path/binary>>) ->
|
||||
PathAbsolute = relative_socket_to_mnesia(Path),
|
||||
file:delete(PathAbsolute);
|
||||
maybe_delete_udsocket_file(_Port) ->
|
||||
ok.
|
||||
|
||||
%%%
|
||||
%%%
|
||||
%%%
|
||||
|
@ -341,11 +358,20 @@ accept(ListenSocket, Module, State, Sup, Interval, Proxy, Arity) ->
|
|||
gen_tcp:close(Socket),
|
||||
none
|
||||
end,
|
||||
?INFO_MSG("(~p) Accepted connection ~ts -> ~ts",
|
||||
[Receiver,
|
||||
ejabberd_config:may_hide_data(
|
||||
format_endpoint({PPort, PAddr, tcp})),
|
||||
format_endpoint({Port, Addr, tcp})]);
|
||||
case is_ctl_over_http(State) of
|
||||
false ->
|
||||
?INFO_MSG("(~p) Accepted connection ~ts -> ~ts",
|
||||
[Receiver,
|
||||
ejabberd_config:may_hide_data(
|
||||
format_endpoint({PPort, PAddr, tcp})),
|
||||
format_endpoint({Port, Addr, tcp})]);
|
||||
true ->
|
||||
?DEBUG("(~p) Accepted connection ~ts -> ~ts",
|
||||
[Receiver,
|
||||
ejabberd_config:may_hide_data(
|
||||
format_endpoint({PPort, PAddr, tcp})),
|
||||
format_endpoint({Port, Addr, tcp})])
|
||||
end;
|
||||
_ ->
|
||||
gen_tcp:close(Socket)
|
||||
end,
|
||||
|
@ -356,6 +382,16 @@ accept(ListenSocket, Module, State, Sup, Interval, Proxy, Arity) ->
|
|||
accept(ListenSocket, Module, State, Sup, NewInterval, Proxy, Arity)
|
||||
end.
|
||||
|
||||
is_ctl_over_http(State) ->
|
||||
case lists:keyfind(request_handlers, 1, State) of
|
||||
{request_handlers, Handlers} ->
|
||||
case lists:keyfind(ejabberd_ctl, 2, Handlers) of
|
||||
{_, ejabberd_ctl} -> true;
|
||||
_ -> false
|
||||
end;
|
||||
_ -> false
|
||||
end.
|
||||
|
||||
-spec udp_recv(inet:socket(), module(), state()) -> no_return().
|
||||
udp_recv(Socket, Module, State) ->
|
||||
case gen_udp:recv(Socket, 0) of
|
||||
|
@ -470,12 +506,13 @@ stop_listeners() ->
|
|||
Ports).
|
||||
|
||||
-spec stop_listener(endpoint(), module(), opts()) -> ok | {error, any()}.
|
||||
stop_listener({_, _, Transport} = EndPoint, Module, Opts) ->
|
||||
stop_listener({Port, _, Transport} = EndPoint, Module, Opts) ->
|
||||
case supervisor:terminate_child(?MODULE, EndPoint) of
|
||||
ok ->
|
||||
?INFO_MSG("Stop accepting ~ts connections at ~ts for ~p",
|
||||
[format_transport(Transport, Opts),
|
||||
format_endpoint(EndPoint), Module]),
|
||||
maybe_delete_udsocket_file(Port),
|
||||
ets:delete(?MODULE, EndPoint),
|
||||
supervisor:delete_child(?MODULE, EndPoint);
|
||||
Err ->
|
||||
|
@ -562,10 +599,12 @@ format_error(Reason) ->
|
|||
end.
|
||||
|
||||
-spec format_endpoint(endpoint()) -> string().
|
||||
format_endpoint({Port, IP, _Transport}) ->
|
||||
format_endpoint({Port, IP, Transport}) ->
|
||||
case Port of
|
||||
<<"unix:", _/binary>> ->
|
||||
Port;
|
||||
<<>> when (IP == local) and (Transport == tcp) ->
|
||||
"local-unix-socket-domain";
|
||||
Unix when is_binary(Unix) ->
|
||||
Def = get_definitive_udsocket_path(Unix),
|
||||
<<"unix:", Def/binary>>;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue