The recommended setup4 is to run both ZNC and signal-cli in networked2 containers. Your best bets are
- the official ZNC Docker image
- a signal-cli built from (or based on)3
the provided
/docker/Dockerfile
Existing signal-cli users should bind a copy of their config-data directory to the appropriate path inside the container.5 New users should instead bind an empty directory or volume.
After securing a dedicated number, new users should proceed with the usual signal-cli setup steps (typically just registration/verification).6
- Clone the repo somewhere convenient
>$ git clone --recurse-submodules \ https://github.com/poppyschmo/znc-signal.git
- Copy the
/Signal
directory to the/modules
subdirectory of the volume to be mounted inside the container - Load the ZNC module (remembering the capital "S")
Kubernetes users can do the first two steps in an init container, binding
config maps where necessary. Or, just prepare a dedicated volume ahead of time
and persist the whole /znc-data
tree.
Various options exist and are worth exploring, including landlines with voice-based verification. In the meantime, if you have access to a US mobile number, a Twilio trial7 can probably hold you over.8
The following aren't so much disclaimers as admissions of shortcomings.
- The author remains wholly ignorant as to the inner workings of the underlying technologies on which this module depends, namely ZNC, IRC, D-Bus, and Signal.
- This module is, for now, solely and fundamentally a learning tool and a personal experiment.
- This module will never be a self-contained, bootstrapped, one-click wonder; too many independently moving parts tenuously strung together, any of which could render the whole thing inoperable at any time, perhaps indefinitely.
- Like most experimental, alpha-quality projects, this module is high maintenance. Be prepared to manually merge your config, rebuild the container, reinstall the module, restart ZNC, etc., on every update.
- Because this module has only been tried in a few configurations, it should be assumed unstable. That means crashes may require restarting Docker itself, reloading firewall rules, bouncing network connections, and/or rebooting the host machine, all of which introduce security risks. For what it's worth, the signal-cli account and those of the Signal clients it forwards to are most likely immune from being permanently compromised, so long as you own those numbers.
- For the above reasons, it's not recommended to make this available in multi-user ZNC setups or mission-critical operations at this time. In other words, although this module's type is "User", for now, it should be considered admin-only and single-user only.
-
A mentions aggregator like ZNC Watch that automatically forwards messages based on match conditions.
-
This ZNC module talks to signal-cli using the D-Bus protocol over a "local" TCP connection. In simple setups, this means both containers should reside on the same "custom" network (i.e., not "bridge").
># docker network ls --filter type=custom --format '{{ .Name }}'
A human-pronounceable hostname should be explicitly assigned, for example, using
docker-run
's--hostname
option.For Kubernetes, disregard the above and just place the two containers in the same pod. Exposing (this instance of) signal-cli as a service is not recommended.
Note that the D-Bus TCP transport was deprecated on Unix in 2018 (see revision 0.33 of the spec).
-
Pulling some random signal-cli image from Docker Hub definitely won't work. Also note that various build args like
port_number
andinterface_name
may be necessary. If using the/docker/Makefile
to retrieve the required tarballs, be aware that PGP signatures are not checked. -
Other configurations. For example, if only signal-cli is containerized, you may have to fiddle with the D-Bus connection on the host box. Experimenting with the
dbus-send
tool is already described in the signal-cli wiki. There's no need for numeric IPs if you're able to resolve container names.># docker run --detach --env "SIGNAL_CLI_USERNAME=+18662904236" \ --name signal --hostname signal \ -v /tmp/config_data:/var/lib/signal-cli/.local/share/signal-cli \ custom/signal-cli:test >$ DBUS_SESSION_BUS_ADDRESS=tcp:host=172.17.0.2,port=47000 \ dbus-send --print-reply --type=method_call \ --dest=org.asamk.Signal /org/asamk/Signal \ org.asamk.Signal.sendMessage string:"Testing" array:string: \ string:+18883821222
If also running signal-cli on the host machine, the transport must still be TCP, so make certain the interface is local-only.
-
This example shows
docker-run
's--volume
option, which takes an argument ofHOST-DIR:CONTAINER-DIR
. But the same idea applies to other container-creation tools as well as Kubernetes pod definitions.># docker run ... \ --volume HOST-DIR:CONTAINER-DIR ... ↓ ↓ /some/path/ /var/lib/signal-cli/.local/share/signal-cli/ └── data/ └── data/ ├── +11111111111 ├── +11111111111 ├── ... ├── ... └── +99999999999.d/ └── +99999999999.d/
The container-side of this mapping can be changed at build time. There's nothing special about this container's volume requirements, but the usual considerations still apply. For example, when using symlinks, the target must be accessible within the container, etc. If
docker-ps
doesn't display the current value (because the container is stopped), trydocker-container-inspect
instead. These--format
args may help:"{{ index .HostConfig.Binds 0 }}" // or "{{ $m := index .Mounts 0 }}{{ $m.Source }}:{{ $m.Destination }}"
-
The whole Catch-22 of having to initialize the container with an existing account can be sidestepped by providing
--env SIGNAL_CLI_USERNAME=<new number>
on the first go-round, even though the number's still unregistered. That way, you can usedocker-exec
(orkubectl-exec
) as follows, and forgo having to remake the container:># docker exec -it my_container interact [my_container:~]$ id && pwd uid=99(signal-cli) gid=99(signal-cli) groups=99(signal-cli),99(signal-cli) /var/lib/signal-cli [my_container:~]$ signal-cli --help ...
Upon exiting, restart the container.
Note: to access the utility non-interactively, you must first stop the service manually:
># docker exec sigcli_service /entrypoint.sh true signal-cli: stopped ># docker exec --user signal-cli my_container signal-cli --help ... ># docker restart my_container my_container
-
Twilio trial accounts.a Requirements: a web browser, a working US-mobile number.
- Sign up for a free trial, add a phone number, and keep the web session alive.
- Register the number with
signal-cli,b and keep the console
session alive.
[my_container:~]$ signal-cli -u $USERNAME register
- Grab the verification codec from
the latest error report in Twilio's SMS log and enter
it.
[my_container:~]$ signal-cli -u $USERNAME verify $CODE [my_container:~]$ test -n "$(ls .local/share/signal-cli/data)" && echo success success
- Notes:
- The
/twilio_verify
helper module is an experimental tool unrelated to this process. - In signal-cli terminology,
USERNAME
means phone number. See above for info on usingdocker-exec
to access the container command prompt. - If you're not receiving messages and have
previously modified the "Messaging" area of the
"phone-numbers" console, ensure the value of the
Messaging > A message comes in > webhook
field is non-null (or just reset it to the original demo addresss of:https://demo.twilio.com/welcome/sms/
).
- The
-
(Or be prolonged abusively, if stretching dollars is in your current job description.) As a deterrent against protracted trials, numbers are currently revoked after a month or so of "inactivity."
Move these points to individual issues threads (and somehow translate current ordering/priority). Also collect and consolidate general questions involving fundamental ZNC and/or SWIG behavior. Possibly do the same for IRC/RFC related stuff. Add simple, reproducible examples.
-
Add end-to-end tests, with CI, if possible.
-
Investigate the signal-cli "startup delay" issue. Description: the process lies dormant for up to 30 minutes before connecting to the message bus; it's also delayed in recognizing the first message, incoming or outgoing. Search upstream for relevant discussions/activity; perhaps create a DSL or shell script demonstrating the issue in a reproducible way. Use a debugger or system-call inspector to hunt for clues.
-
Either implement or remove the various "placeholder" config options stolen early on from ZNC Push. These are all currently ignored:
/templates/*/length
/conditions/*/replied_only
/conditions/*/timeout_post
/conditions/*/timeout_push
/conditions/*/timeout_idle
-
Deal with unexpected disconnections from the message bus. Find a way to simulate this.
-
Integrate signal-cli 0.6.0 features
- Subscribe to and acknowledge receipts
- Queue undelivered messages
- Alert attached clients or save to context buffers when backlogs arise
- Add support for
getGroupIds()
-
Prepare a TCP/Unix-domain-socket shim for the signal-cli container in case some future release of D-Bus drops TCP support entirely.
-
Find some means of testing against real ZNC and signal-cli instances to stay abreast of recent developments. Perhaps a move to GitLab would make this easier. Although ZNC, Signal, and signal-cli are all on GitHub.
-
Drop support for ZNC 1.7.0 at some target date or with the next minor release.