It covers configuration and deployment of simple Phoenix application as a tor hidden service.
Important topics like Phoenix auth, and properly securing a hidden service are not covered.
security/tor needs to be installed.
Phoenix, Elixir and PostgreSQL also need to be installed.
Software Versions
Creating a Sample Project
Create a new Phoenix project.
The sample project will be a simple memo JSON API service.
A real service will almost certainly need some sort of authentication,
but that is not covered in this post.
Revise the web/router.ex file.
The “/api” scope needs to be uncommented and the “/memos” route needs to be added.
web/router.ex
Run the migration.
Configure the app to only serve localhost (127.0.0.1) in config/prod.exs.
Also make sure server is set to true and a dynamic port configuration is used.
config/prod.exs partial listing
Optionally, configure the IP address and add a dynamic port configuration
with a default value to config/dev.exs.
The above dynamic port solution is suitable for releases.
The interpreted solution below is suitable for development.
config/dev.exs partial listing
Tests should pass
Start the server.
POST and GET a memo to make sure the server works.
A prior post covers a shell script for conveniently interacting with this sample app.
Generating a Release
Now that the Phoenix app is working, it is time to build a release.
Add the elixir release manager (exrm) to mix.exs as a project dependency.
mix.exs partial listing
Install exrm and build a release.
This will create the rel/ directory.
The rc script will use environment variable knobs to configure the app.
Note that the RELX_REPLACE_OS_VARS=true environment variable needs
to be defined to use environment variables for dynamic configuration.
The vm.args file is primarily used to configure the erlang VM.
It can also be used to define application configure parameters.
Application configuration parameters defined in this file can be
passed into the program as atoms or integers.
Note that the location of this file can be configured
with the RELEASE_CONFIG_DIR environment variable.
Add the following to rel/vm.args.
rel/vm.args
Alternatively, sys.config can be used to pass in
application configuration parameters.
In this file, application configuration parameters
defined with environment variables must be strings.
Pass the port setting in as above or add the following
to rel/sys.config.
The app module should work with either solution.
Adding both files will not break anything.
Note that rel/sys.config is written in Erlang.
Content needs to be served to a port on localhost.
This post will use port 8080.
Read the tor configuration instructions.
Open /usr/local/etc/tor/torrc (see torrc instructions).
Add the following lines to the section titled
“This section is just for location-hidden services”.
/usr/local/etc/tor/torrc partial listing
Enable tor in /etc/rc.conf
/etc/rc.conf partial listing
Start tor.
Get the hostname for your hidden service with the following command.
Do not share the private_key, found in the same directory.
Test your hidden service with curl by supplying the tor proxy with the -x option.
The -v flag gives verbose output.
You can also test your hidden service with Tor2web.
For example, if your hidden service has a hostname of ABCDEFGHIJKLMNOP.onion,
go to https://ABCDEFGHIJKLMNOP.onion.to to view it in a web browser.
The author of this post could not get Tor2web curl commands to work with a Phoenix app.
Something like the following commands should theoretically work.
Note that Tor2web blocks the curl user agent, so the user agent is set to test instead.
To disable tor when you no longer need to use it, stop it with the service command.
Then disable it in /etc/rc.conf.
A Sample Shell Script for Working With Hidden Phoenix JSON APIs
Support for flags has been added.
The flags default to using the tor proxy.
The default host has been changed to automatically
pull in the hidden service hostname.
tor_memo_api.sh
As written, the above script can only be run as root or the tor
user because /usr/home/tor does not have global read permissions.
Changing the permissions is a bad idea.
Instead, hard code the default host if you want to
be able to use the script with unprivileged users.