-- Leo's gemini proxy
-- Connecting to gmi.noulin.net:1965...
-- Connected
-- Sending request
-- Meta line: 20 text/gemini
date: 2022-12-23 14:19:31
categories: tools
firstPublishDate: 2022-12-23 14:19:31
Tor is network that preserves the anonymity of the users and services.
On the
, you can download the tor browser and browse the web anonymously.
The list of active tor relays is available at
.
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.
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
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
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
, 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
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
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
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
For gemini over tor experiment, I used the `gmnisrv` gemini server and the `amfora` gemini client.
Install `gmnisvr` as described in the my article
.
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
-- Response ended
-- Page fetched on Wed May 22 02:22:33 2024