This project sets up a Tor hidden service running inside a Docker container with Nginx for web hosting and SSH access.
It supports two modes:
- Persistent mode: Keeps the same
.onion
address across restarts. - Non-persistent mode: Generates a new
.onion
address each time.
- Docker
- Docker Compose
- Make
etc
git clone https://github.com/daisvke/ft_onion.git
cd ft_onion
Create a .env
file from the example:
cp .env.example .env
Then edit .env
as needed.
You will need this structure for log persistence:
├── logs
│ ├── auth.log
│ └── fail2ban.log
And this one for the persistence of the authorized SSH keys:
├── config
│ ├── ssh
│ │ └── authorized_keys
These files can be empty, just create them using touch <FILE>
.
Now that we have all the necessary empty files, we have two modes in which we can run this project:
You will need this structure (from /var/lib/tor/hidden_service/) to run it with hostname persistence:
├── tor_data
│ ├── authorized_clients
│ ├── hostname
│ ├── hs_ed25519_public_key
│ └── hs_ed25519_secret_key
# Run:
make up
This will keep the same .onion address across restarts.
make nonpersist
A new .onion address will be generated on every restart.
docker compose down
# Or
make clean
# Or do a full clean:
make fclean
These will remove the .onion
address across restarts.
After starting the container, check your Tor Hidden Service address:
docker exec -it tor_service cat /var/lib/tor/hidden_service/hostname
Use Tor Browser to visit the site.
-
When using
SSH (Secure Shell)
to connect to remote servers, there are two primary modes of operation you can consider: Direct SSH Connection and SSH over Tor (using torsocks). -
torsocks
is a wrapper for applications that need to connect to the internet through the Tor network. It allows these applications to route their traffic through Tor, providing anonymity and privacy for the user.
# Install torsocks
sudo apt install torsocks
# Set up a Tor connection
sudo tor
# Connect to the server via SSH through the Tor network
torsocks ssh <SSH_USER>@<your-hidden-service.onion> -p 4242
-
Pros:
- Anonymity: Connecting through Tor provides anonymity for both the client and the server. The server's IP address is not exposed to the client.
- Access from Anywhere: You can access the server from anywhere without needing to expose your server's IP address to the public internet.
-
Cons:
- Latency: Tor can introduce additional latency due to the multiple hops your connection makes through the Tor network.
# Execute bash from the container
docker exec -it tor_service /bin/bash
# Connect to the container via SSH from the host
ssh -p 4242 <SSH_USER>@localhost
# Ex.:
ssh -p 4242 user@localhost
# Connect from another device on the same network
ssh -p 4242 <SSH_USER>@<TOR_SERVICE_HOST_PRIVATE_IP>
# Ex.:
ssh -p 4242 user@192.168.43.67
-
Pros:
- Performance: A direct SSH connection to an IP address typically offers better performance and lower latency.
-
Cons:
- Exposure: The server's IP address is exposed, which can make it a target for attacks. If the server is on a public network, it may be vulnerable to scanning and unauthorized access.
- Limited Access: If you're trying to access the server from outside the local network, you may need to configure port forwarding on your router or use a VPN.
We secured the SSH service against attacks by adding to our sshd_config
file:
# Disable root login: Prevent attackers from trying to log in as root.
PermitRootLogin no
# Allow only specific users to log in via SSH
AllowUsers user
2. Enable verbose logging:
# Install syslog (from Dockerfile)
apt update && apt install -y inetutils-syslogd
# Run syslog (from script.sh)
syslogd
# Add in `sshd_config`:
SyslogFacility AUTH
LogLevel VERBOSE # With `INFO` we didn't get any SSH access logs
# Check logs
docker exec -it tor_service cat /var/log/auth.log
cat /var/run/utmp
3. Prevent brute-force attacks by banning IPs after failed login attempts (Fail2ban):
# Install Fail2ban
apt update && apt install -y fail2ban
# Edit config file
vim config/jail.conf
# We need to add a capability on the docker compose file
# to run the container with networking permissions.
# This is because `iptables` doesn't run without those permissions.
cap_add:
- NET_ADMIN
# In jail.conf we must have:
[sshd]
port = 4242
logpath = %(sshd_log)s
backend = %(sshd_backend)s
# Run Fail2ban
service fail2ban start
# Check status (total failed, total banned, etc)
fail2ban-client status sshd
# Display logs
cat /var/log/fail2ban.log
The address
192.168.16.1
is being banned.
4. Use Key-Based Authentication:
- If not done yet, generate an SSH key pair on your local machine:
ssh-keygen
- Copy the public key to the remote server:
ssh-copy-id user@remote_server -p 4242
# Ex.: ssh-copy-id user@localhost -p 4242
-
This will add an entry to
/home/user/.ssh/authorized_keys
in the container, and toconfig/ssh/authorized_keys
on the host machine. Now the client can connect automatically to the server without having to log in. -
Disable password authentication in the /etc/ssh/sshd_config file:
PasswordAuthentication no
Put back yes
if you want to register a new user.
# find the PID of the process managing the SSH session
ps aux | grep ssh
# Kill the process
kill <PID>
# Connection is then closed
In our case Fail2Ban
wasn't detecting SSH login failures over Tor socket. This is because of how Tor handles connections and how Fail2Ban reads logs:
- when using Tor, all connections appear to come from 127.0.0.1 (localhost) because Tor is forwarding the request.
- Therefore, we needed to ignore login failures coming from the localhost:
# In `jail.conf`:
ignoreip = 127.0.0.1/8 ::1
::1
is the IPv6 loopback address, equivalent to 127.0.0.1
in IPv4.
You do not need HTTPS
for a Tor .onion
website because Tor already encrypts all traffic end-to-end. Unlike the regular internet, where HTTPS is needed to prevent MITM (Man-in-the-Middle) attacks, Tor's network ensures that:
- End-to-end encryption is built into the protocol.
- No need for TLS/SSL certificates (Let's Encrypt does not issue .onion certs).
- Traffic is encrypted between the client and the hidden service.
- When you are hosting a .onion Hidden Service, nobody (including exit nodes) can see your traffic because it stays inside the Tor network.
# Check the used ports inside the container with the corresponding processes
docker exec -it tor_service ss -tulnp
# Check ports of the container that are open to the outside
docker ps
# or
docker port <CONTAINER ID>