Using Tor script to create a Tor network hidden service with vanity .onion address and export a service to the internet
Tor is not hidden
“In our experiments we collected 173667 unique .onion addresses in all using a single Amazon EC2 instances in 1 hour and found 4857 hidden web services online.”
Hidden Service Discovery
If you were to create a brand-new Tor v3 address, its address is not inherently public, but you might have little time before you’re discovered:
Bots & Crawlers: Hours to a day. Automated services scanning .onion addresses might identify the service pretty quickly.
Public Discovery (like human users or public listings): Days to weeks, depending on whether the address is actively shared or found.
This is just an estimate, and the actual time can vary based on scanning frequency, network traffic, and how indexed your .onion address becomes.
Tor to host an .onion service
Now, with the warnings in place, we can begin.
The purpose of this repo/post is to give someone a chance to test out hosting an .onion hidden service.
You can use this to quickly share a service to a friend, client, or even your future self.
A Tor hidden service does not need your server to have open ports or port forwarding - because it does not accept direct inbound connections from the public internet. Instead, both the client and the hidden service connect outbound to the Tor network, establishing circuits to special relays called introduction and rendezvous points. All communication is routed through these Tor relays, so as long as your server can make outbound connections to the Tor network, it can host a hidden service!
Tor Hidden Service Tutorial
1-up-tor-onion-address script
The 1-up-tor-onion-address.sh script sets up one service that will be available through a Tor .onion address.
This service is only available through the Tor network
This is intended as a demonstration. I hope you’re able to learn and enjoy using.
Download and run with:
1
2
3
wget https://github.com/MarcusHoltz/tor-hidden-service/archive/refs/heads/main.zip -O tor-hidden-service-repo.zip && unzip tor-hidden-service-repo.zip && rm tor-hidden-service-repo.zip && cd tor-hidden-service-main && chmod +x 1-up-tor-onion-address.sh && ./1-up-tor-onion-address.sh
Script Requirements
The 1-up-tor-onion-address.sh script will need sudo
.
Sudo is required to set all of the directory permissions correctly.
You will then need docker installed to generate a vanity address and run the docker-compose.yml
file that starts up Tor.
The 1-up-tor-onion-address.sh script is only intended to prepare the environment we’re using with Docker.
Changes the Script makes
The 1-up-tor-onion-address.sh script sets up two directories, a file, and optionally a vanity address.
Two Directories
You need sudo privs for:
- tor_config/vanity_keys/
and
- tor_data/
Those directories store the keys that are used for your .onion address. Kept safe from any normal user.
A file: torrc
A torrc file contains all the settings Tor uses.
You need sudo privs for:
- tor_config/torrc
By changing this file we can tell Tor what services we want to serve on a Tor Hidden Service and where to find the corresponding .onion address.
Vanity Name Creation
A vanity address is an onion address that starts with a pre-chosen number of characters, usually a meaningful name related to a specific Onion Service.
For instance, one might try to generate an onion address for the mysitename website and end up with something looking like this:
mysitenameyx4fi3l6x2gyzmtmgxjyqyorj9qsb5r543izcwymle.onion
This has some advantages:
It is easy for Onion Services users to know which site they are about to access.
It has some branding appeal to site owners.
It is easy for Onion Services operators to debug their logs and know which services have errors.
Anyone else is very unlikely to come up with the exact key from the example above, but they may be able to find a similar key - one beginng with the same few letters.
- The longer the vanity name length, the less likly it is to have a forgery made.
Vanity Name Length
You can only pick something, at max, 7 characters.
Why?
Let’s say you were running this on a Raspberry Pi 2B….
Take a look at the approximate generation time per character for a Raspberry Pi 2B below:
Approximate Generation Time per Character Count Chart
1
2
3
4
5
6
7
8
9
10
11
12
13
Vanity Characters : Approximate Generation Time
1 : <1 second
2 : <1 second
3 : 1 second
4 : 30 seconds
5 : 16 minutes
6 : 8.5 hours
7 : 11.5 days
8 : 1 year
9 : 32 years
10 : 1,024 years
11 : 32,768 years
12 : 1 million years
Example vanities
So now that we know our upper limit on the amount of letters we can have, take a look at some examples….
Click to expand and take a look at the 6 character example vanities below:
6 character example vanity .onion domains
- 123456 - nopers - online - system - search - office - forums - mobile - garden - nature - movies - photos - social - future - people - estate - energy - income - browse - create - report - global - agency - potato - attack - wisdom - stream - viewer - status - screen - sector - survey - secure - signal - source - remote - direct - little - jazzed - dazzle - danger - school - familyHow is the vanity generated
Thanks to the work on the cathugger/mkp224o repository, we’re able to generate vanity address for tor onion v3 (ed25519) hidden services.
Specifically, the 1-up-tor-onion-address.sh script will run:
docker run ghcr.io/cathugger/mkp224o:master -n 3 <your_vanity_name>
It will generate
3
.onion addresses that begin with your vanity name, allowing you to select a favorite.The .onion address will be in
tor_config/vanity_keys/
Can’t I just use my own .onion address
Yes! The 1-up-tor-onion-address.sh script will prompt you to use your own, you just have to provide the path.
Instructions for Using Bringing Your Own Vanity Tor Address:
Make sure you have all of your files for your .onion address in the same directory:
hostname
- Contains your .onion addresshs_ed25519_secret_key
- Your private keyhs_ed25519_public_key
- Your public key
After the 1-up-tor-onion-address.sh script completes, verify your hidden service is correct:
1
2
3
sudo cat tor_data/hidden_service/hostname
What Service to put on Tor
You will also need a service to provide to the .onion address.
This can be anything. It can be another docker container, a python web server on your laptop, your favorite IoT device, whatever!
You will just need to give The 1-up-tor-onion-address.sh script:
The
IP
orHostname
of the service you’re sending to the Tor network.The
Port
for the service to forward over the .onion address.ONLY ONE SERVICE!!! –> tor_data/hidden_service/
This script is designed for demonstration and as such, there’s only one service designed into the script. You can always make multiple services on the same .onion address with different ports, or a new .onion address for every service. But today, only one service.
Sample Service
If you really dont have anything to use as a service, you can create a quick HTTP server with bash:
Creates an HTTP server using
netcat
Server will respond on
port 5432
Exit the netcat command with:
ctrl + c
1
2
3
echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<p>Works for me</p><p>$(date)</p>" | nc -l -p 5432
Add an
&
on the end to the command - to let it run in the background.
Exit from the background with:
kill $(ps -ef | grep [5]432 | awk '{print $2}')
Torrc is important
The torrc
file lets you define HiddenServiceDir
and HiddenServicePort
directives, these tell Tor where to store your service you’re sending to the Tor network’s keys and what ports to forward, making your .onion site accessible.
The 1-up-tor-onion-address.sh script uses two directories
File permissions are critical for Tor hidden services:
- Directories need 700 permissions (drwx——)
- Key files need 600 permissions (-rw——-)
- The docker container will adjusts these permissions for you
The Tor user (not root) must own all these files inside the container
Browsers that find an onion service
Use Brave Browser
Use Tor Browser
Want to know more?
Want to know more about the 1-up-tor-onion-address.sh script? How about a breakdown of the script’s logic!
Take a look at the flow of the 1-up-tor-onion-address.sh script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
┌───────────────────────┐
│ check_sudo() │
│ - Verify privileges │
└──────────┬────────────┘
▼
┌───────────────────────┐
│ create_directories() │
│ - tor_config/ │
│ - tor_data/ │
└──────────┬────────────┘
▼
┌───────────────────────┐
│ set_permissions() │
│ - 755 config │
│ - 700 data │
└──────────┬────────────┘
▼
┌───────────────────────┐
│get_network_settings() │
│ - Collect: │
│ • HOST_IP │
│ • HOST_PORT │
│ • VIRTUAL_PORT │
└──────────┬────────────┘
▼
┌───────────────────────┐
│setup_vanity_address() │
└──────────┬────────────┘
├────────────────────────────┐
▼ ▼
┌───────────────────────┐ ┌───────────────────────┐
│ Generate New Address │ │ Use Existing Keys │
│ - mkp224o Docker │ │ - Validate dir │
│ - vanity name input │ │ - Verify key files │
└──────────┬────────────┘ └──────────┬────────────┘
▼ ▼
┌───────────────────────┐ ┌───────────────────────┐
│ Select From Generated│ │ Copy Existing Keys │
│ - Display options │ │ - hostname │
│ - Validate selection │ │ - secret_key │
└──────────┬────────────┘ └──────────┬────────────┘
└────────────┬─────────────────┘
▼
┌─────────────────────────────────────────┐
│ setup_hidden_service_dir() │
│ - Create hidden_service/ │
│ - Set 700 permissions │
└──────────┬──────────────────────────────┘
▼
┌───────────────────────┐
│ create_torrc() │
│ - HiddenServicePort │
│ - DataDirectory │
└──────────┬────────────┘
▼
┌───────────────────────┐
│ finalize_setup() │
│ - Set file perms │
│ - Display hostname │
│ - Run instructions │
└───────────────────────┘
Uninstall
How do you stop the Tor network now that you’ve let it onto your computer? You’ve let Skynet spread!!!
Go get a high-density polyethylene (HDPE) container. HDPE is a commonly used plastic for robust, leak-proof containers.
Fill this up to the brim with high octane gasoline.
Dowse your computer in as much gas as possible.
Now your computer will run faster, but the Tor network is still on it.
Should we:
uninstall the work we did
kill Tor with fire
To uninstall, delete the directory (tor-hidden-service-main) you created for this demonstration (you may have to use sudo) and run the following to remove the docker container:
1 2 3
docker stop $(docker ps -a | grep tor-hidden-service | awk '{print $1}') 2>/dev/null && docker rm $(docker ps -a | grep tor-hidden-service | awk '{print $1}') 2>/dev/null
Additional Ways to Use: Tor on OPNSense
OPNsense has a great tor plugin, os-tor
, that can do tor SOCKS proxy, hidden services, and authentication as well but with the OPNsense GUI and Terminal.
You can use it as a proxy for your browsers or setup a hidden onion service here as well, just using the GUI.
But, to get the authentication part working… we need to enter the terminal.
Tor Hidden Onion Service with Secret Authentication
Crypto Passwords
Because, let’s be honest - if you can send someone the long string that is an onion address, you could probably give them the private key for that service as well.
Tor browser is such a sweetheart that when you paste in the onion address, it’ll immediatly ask you for the private key as well.
Onion Service Authorized clients
If you followed the official documentation for OPNSense, os-tor above, you will have seen the Onion Service creation, and, possibly the Authorized clients section.
You can enter as many clients as you want, but nothing will happen. You still need to add the appropreate file for the client in the terminal.
Let’s go do that now.
Console in to a terminal
SSH, open a console, login with a keyboard, whatever. You need to be looking at a terminal to add files.
Navigate to:
/var/db/tor/<name-of-service>/authorized_clients/
We will make the
<authorized_client_name_you_picked_in_Onion_Service_above>.auth
file required for this to work. But first,Run the script below. Then you will have two keys, a public and a private key.
You need to take the public key
Make the .auth file referenced above.
Paste in:
descriptor:x25519:
and paste after,
descriptor:x25519:
, your public key from step 5.Save your .auth file
Make sure it is in your
/var/db/tor/<name-of-service>/authorized_clients/
Using the private key to access services
The onion address you can find under Services > Tor > Information
and that’s how you can access the Onion Service Routing
you set when you read the official documentation for OPNSense, os-tor, again that address will only let someone with the private key access it.
There is no username you need to enter. That’s mainly for organizational purposes. Just enter the private key
when prompted and you should see the onion service.
A Differernt Tor Proxy supplying the example below:
OPNSense cannot do client authentication. You can, however, run a different tor proxy that will allow client authentication:
This involves: .auth_private
That file is different.
You must put the tor hidden service address first, with the .onion removed.
Example
Example address:
wisdomhrc3g5en6lcmb7i7jte6nj5rc33ctzgfiq5lozhqo2uoaccvqd.onion
Will become:
wisdomhrc3g5en6lcmb7i7jte6nj5rc33ctzgfiq5lozhqo2uoaccvqd
Now that you have the tor hidden service you want to use in the file, you must include the descriptor and the private key.
The private key should come after,
wisdomhrc3g5en6lcmb7i7jte6nj5rc33ctzgfiq5lozhqo2uoaccvqd:descriptor:x25519:
5 Save your .auth_private with the key on the end
- Copy it to your
/var/lib/tor/onion_auth/
Script
FreeBSD-compatible X25519 key generator with built-in Base32 encoder
Sorry. I found it a big pain to try and generate these auth keys in FreeBSD, because they dont include base32 in packages.
This script takes care of:
X25519 key generation: private.key & public.key
Base32 encodes these keys to make them Tor compatible
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#!/bin/sh
# Self-contained FreeBSD-compatible X25519 key generator with Base32 encoder
base32_encode_file() {
file="$1"
alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
pad="="
bits=""
# Read file byte-by-byte and convert to bit string
for byte in $(od -An -v -t u1 "$file"); do
n=$byte
b=""
for i in 1 2 3 4 5 6 7 8; do
b="$(($n % 2))$b"
n=$(($n / 2))
done
bits="${bits}${b}"
done
# Pad bits to multiple of 5
mod=$((${#bits} % 5))
if [ "$mod" -ne 0 ]; then
padlen=$((5 - mod))
while [ "$padlen" -gt 0 ]; do
bits="${bits}0"
padlen=$((padlen - 1))
done
fi
# Encode 5 bits at a time
output=""
i=0
while [ $i -lt ${#bits} ]; do
chunk=$(printf "%s" "$bits" | cut -c $(($i + 1))-$(($i + 5)))
idx=0
for c in $(echo "$chunk" | sed 's/./& /g'); do
idx=$((idx * 2 + c))
done
output="${output}$(printf "%s" "$alphabet" | cut -c $(($idx + 1)))"
i=$(($i + 5))
done
# Pad output to multiple of 8
mod=$((${#output} % 8))
if [ "$mod" -ne 0 ]; then
padlen=$((8 - mod))
while [ "$padlen" -gt 0 ]; do
output="${output}${pad}"
padlen=$((padlen - 1))
done
fi
echo "$output"
}
pem_body_to_bin() {
# Strips PEM headers/footers and base64-decodes the body
sed '/-----BEGIN/,/-----END/!d;/-----BEGIN/d;/-----END/d' "$1" | openssl base64 -d
}
extract_and_encode_key() {
input_pem="$1"
output_file="$2"
# Extract last 32 bytes from decoded PEM and encode
pem_body_to_bin "$input_pem" | tail -c 32 > tmp.raw
base32_encode_file tmp.raw | sed 's/=//g' > "$output_file"
rm -f tmp.raw
}
generate_keys() {
echo "[*] Generating Public & Private Keys"
openssl genpkey -algorithm x25519 -out key.private.pem
extract_and_encode_key key.private.pem private.key
openssl pkey -in key.private.pem -pubout > key.public.pem
extract_and_encode_key key.public.pem public.key
echo "[*] Successfully Generated Keys"
echo -n "Public Key: "
cat public.key
echo
echo -n "Private Key: "
cat private.key
echo
}
# Main
generate_keys
Tor - share with a button
If this is all just too much, and you need a quick - almost one button press - look into OnionShare.
OnionShare works great on a phone, and is also available on flathub, MacOS, and Windows.
OnionShare’s main features are:
Sending and receiving large files peer-to-peer over tor network.
Chat ephemerally.
Host a website.
Sources for OPNsense Onion Hidden Service Tor Clients
https://web.archive.org/web/20211101210839/https://matt.traudt.xyz/posts/creating-private-v3-FgbdRTFr/
https://community.torproject.org/onion-services/advanced/client-auth/
https://github.com/torproject/torspec/blob/main/rend-spec-v3.txt#L2569