Using Tor
Tor is network that preserves the anonymity of the users and services.
On the tor project page, you can download the tor browser and browse the web anonymously.
The list of active tor relays is available at https://nusenu.github.io/OrNetStats/w/.
In this article, I show how to create onion services in Debian Bullseye:
- Web server onion service
- SSH onion service
- My own tcp server onion service
- IRC onion service
- Gemini failed experiment
Onion services are within the tor network and don't need tor exit nodes.
To create onion service, you need:
- to install
tor - configure the onion service in the .torrc file
- setup the service
After starting tor, it takes less than 30 seconds for the onion service to become accessible.
To create a regular internet service, you need:
- to buy a domain
- setup the dns for your server ip
- setup letsencrypt to get a tls certificate
- setup the service
- setup the service to use the tls certificate and restart when needed
From my experience, the tor network is quite slow, it's like using the internet with a 56kbauds modem.
How to create a simple http onion service
The web server I use is my simple web server: liveserver.
First, install sheepy(build system) as root:
apt-get install tor build-essential
git clone https://spartatek.se/git/sheepy.git
cd sheepy
./install.sh
Then, start the web server, it opens port 80:
git clone https://noulin.net/git/liveserver.git
cd liveserver
./liveserver.c
Create a tor configuration in your home directory (replace USER with your username):
vi ~/.torrc
HiddenServiceDir /home/USER/tor/my_website
HiddenServicePort 80 127.0.0.1:8080
The onion service configuration is stored in /home/USER/tor/my_website, create these directories with correct permissions and start tor (as username, not as root):
mkdir -p /home/USER/tor/my_website
chmod 700 /home/USER/tor/my_website
chmod 100 /home/USER/tor
# start tor
tor -f ~/.torrc
The onion address is generated by tor and stored in /home/USER/tor/my_website/hostname
cat /home/USER/tor/my_website/hostname
abc...onion
To connect to the server, open abc...onion in the tor browser.
It is possible to choose the start of the onion address with mkp224o, it is compiled like this:
sudo apt-get install autoconf libsodium-dev
git clone https://github.com/cathugger/mkp224o
cd mkp224o/
./autogen.sh
./configure
make
Run the following command to get onion addresses starting with name:
./mkp224o -d ./keys name
name2cv7s2t2cv4qbzem6bwjeub7gnifincy4lallp4fuk3whjiybgad.onion
name2hz7z5oaksecttowa3ewb4w3hxaosfz5uxknjf7jzidfwxe3xtid.onion
name33dio3amksrfqjowotnmdmosbqt4fs5jagxplsky3k5quez4vaqd.onion
^C
Choose an onion address, copy it the service configuration directory and restart tor:
cp keys/name2cv7s2t2cv4qbzem6bwjeub7gnifincy4lallp4fuk3whjiybgad.onion/* ~/tor/my_website/
# Restart tor
tor -f ~/.torrc
How to create an SSH onion service
Add an onion service to .torrc:
HiddenServiceDir /home/USER/tor/my_ssh
HiddenServicePort 22 127.0.0.1:22
Install torsocks and start the ssh client like this:
sudo apt-get install torsocks
torsocks ssh name2hz7z5oaksecttowa3ewb4w3hxaosfz5uxknjf7jzidfwxe3xtid.onion
How to create a newsgroups onion service
On the server, install tor and the nntp server inn2:
apt-get install tor inn2
apt issues an error for inn2 because inn.conf is invalid (on debian bullseye and more recent) and needs to be changed.
Configure inn2:
vi /etc/news/inn.conf
organization: example-organization
pathhost: news.example.com
domain: example.com
htmlstatus: false
# limit article size
maxartsize: 16384
Create a group:
ctlinnd newgroup tor.forum
Enable world access, add the lines below between auth localhost and access localhost:
vi /etc/news/readers.conf
auth "world" {
hosts: "*"
default: "<world>"
}
access "world" {
users: "<world>"
newsgroups: "tor.forum"
access: RPA
}
Restart inn2:
/etc/init.d/inn2 restart
In the Setting up a newsgroup server article, I configured TLS and with tor it is unnecessary, the server uses port 119.
Configure the onion service in .torrc, add these lines:
HiddenServiceDir /home/USER/tor/my_newsgroup
HiddenServicePort 119 127.0.0.1:119
Create the service directory and restart tor:
mkdir -p /home/USER/tor/my_newsgroup
chmod 700 /home/USER/tor/my_newsgroup
# Restart tor
tor -f ~/.torrc
On the client, install slrn newsgroup client:
apt-get install slrn
Configure slrn, ADDRESS_IN_HOSTNAME.onion is the string in /home/USER/tor/my_newsgroup/hostname:
zcat /usr/share/doc/slrn/examples/slrn.rc.gz > ~/.slrnrc
vi ~/.slrnrc
server "ADDRESS_IN_HOSTNAME.onion" ".jnewsrc-tor"
Execute srln to connect, first time:
torsocks slrn -f ~/.jnewsrc-tor --create -h ADDRESS_IN_HOSTNAME.onion
# press s to subscribe to the tor.forum newsgroup
# then
torsocks slrn -h ADDRESS_IN_HOSTNAME.onion
Creating my own client and server
Add an onion service to .torrc:
HiddenServiceDir /home/USER/tor/my_simple
HiddenServicePort 5000 127.0.0.1:5000
My server code is:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(int ac, char **av){
int sock;
struct sockaddr_in server;
int mysock;
char buf[1024];
int rval;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0){
perror("Failed to create socket");
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(5000);
if (bind(sock, (struct sockaddr *) &server, sizeof(server))){
perror("bind failed");
exit(1);
}
listen(sock, 5);
do {
mysock = accept(sock, (struct sockaddr *)0, 0);
if (mysock == -1)
perror("accept failed");
else {
memset(buf, 0, sizeof(buf));
if ((rval = recv(mysock, buf, sizeof(buf), 0)) < 0)
perror("reading message");
else if (rval == 0)
printf("Ending connection\n");
else
printf("MSG: %s\n", buf);
printf("Got the message (rval = %d)\n", rval);
write(mysock, "OK", sizeof("OK"));
close(mysock);
}
} while(1);
}
Compile with:
gcc -g3 server.c -o server
The client has to connect to tor socks5 proxy, the socks5 protocol is described in the SOCKS5 RFC 1928
My client code is:
#! /usr/bin/env sheepy
#include "libsheepyObject.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define socks5Server "127.0.0.1"
int main(int ac, char **av){
int sock;
struct sockaddr_in server;
struct hostent *hp;
int mysock;
char buf[1024*1024];
int rval;
server.sin_family = AF_INET;
if (!av[1]) {
puts("address missing.");
XFAILURE;
}
// connect to tor socks5 proxy 127.0.0.1:9050
hp = gethostbyname(socks5Server);
if (hp==0) {
perror("gethostbyname failed");
close(sock);
exit(1);
}
memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
server.sin_port = htons(9050);
stopwatchStart;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0){
perror("Failed to create socket");
}
if (connect(sock,(struct sockaddr *) &server, sizeof(server))){
perror("connect failed");
close(sock);
exit(1);
}
// Socks5 RFC gemini://gmi.noulin.net/rfc/rfc1928.gmi
// Authentication method negotiation
// NO AUTHENTICATION REQUIRED
buf[0] = 5; // version
buf[1] = 1; // 1 byte method field
buf[2] = 0; // no authentication required
if(send(sock, buf, 3, 0) < 0){
perror("send failed");
close(sock);
exit(1);
}
// response
// version 5
// method 0
// accepted
rval = recv(sock, buf, 2, 0);
stopwatchLogUs;
loghex(buf,2); put;
// request
buf[0] = 5; // version
buf[1] = 1; // command: connect
buf[2] = 0; // 0
buf[3] = 3; // address type: domain
if (lenG(av[1]) > 255) {
logE("address is too long");
ret 1;
}
buf[4] = lenG(av[1]);
memcpy(buf + 5, av[1], buf[4]);
buf[5+buf[4]] = 0x13; // port in network order 5000
buf[5+buf[4]+1] = 0x88; // port in network order 5000
logD("len %d", buf[4]);
logD("addr: %s", buf + 5);
loghex(buf, 5+buf[4]+2); put;
if(send(sock, buf, 5+buf[4]+2, 0) < 0){
perror("send failed");
close(sock);
exit(1);
}
rval = recv(sock, buf, 10, 0);
stopwatchLogUs;
// response must be
// 0x5
// 0x0 >> succeeded
loghex(buf, 10); put;
if(send(sock,"WERWER", sizeof("WERWER"), 0) < 0){
perror("send failed");
close(sock);
exit(1);
}
stopwatchLogUs;
rval = recv(sock, buf, 10, 0);
stopwatchLogUs;
// response from server: OK
puts(buf);
printf("Sent: WERWER");
close(sock);
}
Run the client with the onion address as argument:
./client.c address.onion
TOR/socks5 times:
- socks5 authentication: 1.8ms
- connect to server: 2 sec
- send/receive in ms
How to create an irc onion service and connect to it
Install an IRC server:
sudo apt-get install ngircd
Add an onion service configuration to .torrc:
HiddenServiceDir /home/USER/tor/my_irc
HiddenServicePort 6667 127.0.0.1:6667
Restart tor:
tor -f ~/.torrc
The irc client I use is tinyirc, with the torsocks command it can connect to the irc onion service:
torsocks tinyirc nick name4ttgny3n5y72n2crrmf53d4a6ujubtgagn5cdm56yae3jzc4vkyd.onion
I tried torsocks with irssi and weechat but couldn't connect the irc onion service, I could connect regular irc service with a tor exit node.
Then I added support for socks5 proxys directly in tinyirc:
sudo apt-get install libncurses5-dev
git clone https://github.com/nlaredo/tinyirc
Then apply the patch 0001-add-socks5-client-code.patch (keep the tabs otherwise it wont apply) to commit 83ee1dae701de8c968044248fb9e2a6257dd5b74 (latest):
From 3dcbc2defb47a13ff14a0b7319ef4349f15fd45f Mon Sep 17 00:00:00 2001
From: Your Name <you@example.com>
Date: Mon, 12 Dec 2022 19:36:14 +0100
Subject: [PATCH] add socks5 client code
tinyirc.c | 105 +++++++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 77 insertions(+), 28 deletions(-)
---
tinyirc.c | 105 +++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 77 insertions(+), 28 deletions(-)
diff --git a/tinyirc.c b/tinyirc.c
index cfe8998..a2d5974 100644
--- a/tinyirc.c
+++ b/tinyirc.c
@@ -880,37 +880,86 @@ char *hostname;
{
struct sockaddr_in sa;
struct hostent *hp;
- int s, t;
- if ((hp = gethostbyname(hostname)) == NULL)
+ int s;
+ #define socks5Server "127.0.0.1"
+ if ((hp = gethostbyname(socks5Server)) == NULL)
return -1;
- for (t = 0, s = -1; s < 0 && hp->h_addr_list[t] != NULL; t++) {
- bzero(&sa, sizeof(sa));
- bcopy(hp->h_addr_list[t], (char *) &sa.sin_addr, hp->h_length);
+ memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
sa.sin_family = hp->h_addrtype;
- sa.sin_port = htons((unsigned short) IRCPORT);
- s = socket(hp->h_addrtype, SOCK_STREAM, 0);
- if (s > 0) {
- if (connect(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
- close(s);
- s = -1;
- } else {
- fcntl(s, F_SETFL, O_NDELAY);
- my_tcp = s;
- sprintf(lineout, "USER %s * * :%s\n", IRCLOGIN, IRCGECOS);
- sendline();
- sprintf(lineout, "NICK :%s\n", IRCNAME);
- sendline();
+ sa.sin_port = htons(9050);
+ s = socket(hp->h_addrtype, SOCK_STREAM, 0);
+ if (s < 0){
+ perror("Failed to create socket");
+ return -1;
+ }
+ if (connect(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+ perror("connect failed");
+ close(s);
+ return -1;
+ }
+ char buf[1024] = {0};
+ // Socks5 RFC gemini://gmi.noulin.net/rfc/rfc1928.gmi
+ // Authentication method negotiation
+ // NO AUTHENTICATION REQUIRED
+ buf[0] = 5; // version
+ buf[1] = 1; // 1 byte method field
+ buf[2] = 0; // no authentication required
+ if(send(s, buf, 3, 0) < 0){
+ perror("send failed");
+ close(s);
+ return -1;
+ }
+ // response
+ // version 5
+ // method 0
+ // accepted
+ int rval = recv(s, buf, 2, 0);
+ if (rval != 2 || buf[0]!= 5 || buf[1] != 0) {
+ puts("Socks5 authentication method negotiation failed");
+ close(s);
+ return -1;
+ }
+ // request
+ buf[0] = 5; // version
+ buf[1] = 1; // command: connect
+ buf[2] = 0; // reserved 0
+ buf[3] = 3; // address type: domain
+ if (strlen(hostname) > 255) {
+ puts("address is too long");
+ return -1;
+ }
+ buf[4] = strlen(hostname);
+ memcpy(buf + 5, hostname, buf[4]);
+ uint16_t *port = (uint16_t*)&buf[5+buf[4]];
+ *port = htons((unsigned short) IRCPORT);
+ if(send(s, buf, 5+buf[4]+2, 0) < 0){
+ perror("send failed");
+ close(s);
+ return -1;
+ }
+ rval = recv(s, buf, 10, 0);
+ if (rval < 2 || buf[0]!= 5 || buf[1] != 0) {
+ puts("Failed to connect to server");
+ close(s);
+ return -1;
+ }
+ // response must be
+ // 0x5
+ // 0x0 >> succeeded
+ fcntl(s, F_SETFL, O_NDELAY);
+ my_tcp = s;
+ sprintf(lineout, "USER %s * * :%s\n", IRCLOGIN, IRCGECOS);
+ sendline();
+ sprintf(lineout, "NICK :%s\n", IRCNAME);
+ sendline();
#ifdef AUTOJOIN
- sprintf(lineout, AUTOJOIN);
- sendline();
+ sprintf(lineout, AUTOJOIN);
+ sendline();
#endif
- for (obj = olist; obj != NULL; obj = olist->next) {
- sprintf(lineout, "JOIN %s\n", OBJ);
- sendline();
- } /* error checking will be done later */
- }
- }
- }
+ for (obj = olist; obj != NULL; obj = olist->next) {
+ sprintf(lineout, "JOIN %s\n", OBJ);
+ sendline();
+ } /* error checking will be done later */
return s;
}
int main(argc, argv)
@@ -918,7 +967,7 @@ int argc;
char **argv;
{
struct utmp ut, *utmp;
- char hostname[64];
+ char hostname[1024];
int i = 0;
printf("%s Copyright (C) 1991-1996 Nathan Laredo\n\
This is free software with ABSOLUTELY NO WARRANTY.\n\
--
2.30.2
Build tinyirc:
make
Connect to server with these commands:
export IRCNICK=myircnickname
./tinyirc name4ttgny3n5y72n2crrmf53d4a6ujubtgagn5cdm56yae3jzc4vkyd.onion
Failed experiment: gemini client and server
For gemini over tor experiment, I used the gmnisrv gemini server and the amfora gemini client.
Install gmnisvr as described in the my article Building telescope gmni and gmnisvr.
Add a service in .torrc:
HiddenServiceDir /home/USER/tor/my_gem
HiddenServicePort 1965 127.0.0.1:1965
Amfora has a commit in which socks5 support is added:
Support SOCKS5 proxying
@makeworld-the-better-one
makeworld-the-better-one committed Dec 30, 2021
e3e1fc2
But there is no release with this commit:
- Latest release v1.9.2 on Dec 10, 2021
So I compile amfora from source (install go, then compile amfora):
# Download latest version on https://golang.org/dl/
wget https://go.dev/dl/go1.19.4.linux-amd64.tar.gz
tar xvf go1.19.4.linux-amd64.tar.gz
sudo chown -R root:root ./go
sudo mv go /usr/local
export GOPATH=$HOME/work
export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
git clone https://github.com/makeworld-the-better-one/amfora
cd amfora
make
sudo make install
export AMFORA_SOCKS5=127.0.0.1:9050
amfora gemini://address.onion
In gmnisrv, I get the error:
127.0.0.1 SSL accept error: error:14201076:SSL routines:tls_choose_sigalg:no suitable signature algorithm
Hashtag: #tor