feat: automatic onion services

This commit is contained in:
Juan Angel 2022-12-24 19:35:00 +01:00 committed by juanAngel
parent 7d9ef0e216
commit 3731d03230
6 changed files with 129 additions and 5 deletions

View File

@ -1,6 +1,7 @@
FROM node:18-alpine3.16 as build
WORKDIR /build
RUN apk add --no-cache --update git
COPY ["package.json", "package-lock.json", "./"]
@ -26,6 +27,8 @@ ENV DB_USER=nostr-ts-relay
ENV DB_PASSWORD=nostr-ts-relay
WORKDIR /app
RUN apk add --no-cache --update git
RUN mkdir /app/.nostr && chown 1000:1000 /app/.nostr
COPY --from=build /build/dist .

View File

@ -16,6 +16,10 @@ services:
REDIS_PORT: 6379
REDIS_USER: default
REDIS_PASSWORD: nostr_ts_relay
TOR_HOST: tor_proxy
TOR_CONTROL_PORT: 9051
TOR_PASSWORD: nostr_ts_relay
HIDDEN_SERVICE_PORT: 80
# Enable DEBUG for troubleshooting. Examples:
# DEBUG: "worker:*"
# DEBUG: "knex:query"
@ -31,13 +35,14 @@ services:
condition: service_healthy
migrations:
condition: service_completed_successfully
tor_proxy:
condition: service_healthy
restart: on-failure
networks:
default:
ipv4_address: 10.10.10.2
db:
image: postgres
container_name: db
environment:
POSTGRES_DB: nostr_ts_relay
POSTGRES_USER: nostr_ts_relay
@ -62,7 +67,6 @@ services:
start_period: 360s
cache:
image: redis:7.0.5-alpine3.16
container_name: cache
volumes:
- cache:/data
command: redis-server --save 20 1 --loglevel warning --requirepass nostr_ts_relay
@ -77,7 +81,6 @@ services:
retries: 5
migrations:
image: node:18-alpine3.16
container_name: migrations
environment:
DB_HOST: db
DB_PORT: 5432
@ -97,7 +100,19 @@ services:
networks:
default:
ipv4_address: 10.10.10.254
tor_proxy:
image: dperson/torproxy
restart: on-failure
environment:
PASSWORD: nostr_ts_relay
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
default:
ipv4_address: 10.10.10.5
networks:
default:
name: nostr-ts-relay

11
package-lock.json generated
View File

@ -18,6 +18,7 @@
"pg-query-stream": "4.2.4",
"ramda": "0.28.0",
"redis": "4.5.1",
"tor-control-ts": "^1.0.0",
"ws": "8.11.0"
},
"devDependencies": {
@ -11806,6 +11807,11 @@
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==",
"dev": true
},
"node_modules/tor-control-ts": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/tor-control-ts/-/tor-control-ts-1.0.0.tgz",
"integrity": "sha512-uV+swAIQuH0QP+SJcQwlj2xrv0XqKa9V1HQOkU+NONR7/8+JM/4uIxzDJDsVtiK6cNq+x5Nt6deh3nx16XK1Yg=="
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
@ -21408,6 +21414,11 @@
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==",
"dev": true
},
"tor-control-ts": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/tor-control-ts/-/tor-control-ts-1.0.0.tgz",
"integrity": "sha512-uV+swAIQuH0QP+SJcQwlj2xrv0XqKa9V1HQOkU+NONR7/8+JM/4uIxzDJDsVtiK6cNq+x5Nt6deh3nx16XK1Yg=="
},
"tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",

View File

@ -117,6 +117,7 @@
"pg-query-stream": "4.2.4",
"ramda": "0.28.0",
"redis": "4.5.1",
"tor-control-ts": "^1.0.0",
"ws": "8.11.0"
},
"config": {

View File

@ -1,6 +1,7 @@
import { Cluster, Worker } from 'cluster'
import { cpus } from 'os'
import { cpus, hostname } from 'os'
import { addOnion } from '../tor/client'
import { createLogger } from '../factories/logger-factory'
import { IRunnable } from '../@types/base'
import { ISettings } from '../@types/settings'
@ -39,6 +40,9 @@ export class App implements IRunnable {
`)
const width = 74
const torHiddenServicePort = process.env.HIDDEN_SERVICE_PORT ? Number(process.env.HIDDEN_SERVICE_PORT) : 80
const port = process.env.RELAY_PORT ? Number(process.env.RELAY_PORT) : 8008
const logCentered = (input: string, width: number) => {
const start = (width >> 1) - (input.length >> 1)
console.log(' '.repeat(start), input)
@ -58,6 +62,13 @@ export class App implements IRunnable {
logCentered(`${workerCount} workers started`, width)
debug('settings: %O', this.settingsFactory())
const host = `${hostname()}:${port}}`
addOnion(torHiddenServicePort, host).then(value=>{
debug('tor hidden service address: %s:%d', value, torHiddenServicePort)
}, (error) => {
console.error('Unable to add Tor hidden service. Skipping.', error)
})
}
private onClusterMessage(source: Worker, message: Serializable) {

83
src/tor/client.ts Normal file
View File

@ -0,0 +1,83 @@
import {Tor} from "tor-control-ts"
import { createLogger } from '../factories/logger-factory'
import {readFile,writeFile} from 'fs/promises';
import { homedir } from "os";
import { join } from "path";
interface torParams{
host:string;
port:number;
password:string;
}
const debug = createLogger('tor-client')
const getPrivKeyFile = ()=>{
return join(process.env.NOSTR_CONFIG_DIR ?? join(homedir(), '.nostr'),"v3_onion_private_key");
}
const createTorConfig = ():torParams => {
return {
host:process.env.TOR_HOST,
port:Number(process.env.TOR_CONTROL_PORT),
password:process.env.TOR_PASSWORD
};
}
let client:any = null;
export const getTorClient = async () => {
if (!client) {
const config = createTorConfig();
debug('config: %o', config);
//client = knex(config)
if(config.port){
debug('connecting');
client = new Tor(config);
await client.connect();
debug('connected to tor');
}
}
return client
}
export const addOnion = async (port:number,host?:string):Promise<string>=>{
let privateKey = null;
try {
let data = await readFile(getPrivKeyFile(),{
encoding:"utf-8"
});
if(data && data.length){
privateKey = data;
}
debug('privateKey: %o', privateKey);
} catch (error) {
debug('addOnion catch: %o', error);
}
try {
await getTorClient();
if(client){
let hs = await client.addOnion(port,host,privateKey);
if(hs && hs.PrivateKey){
await writeFile(getPrivKeyFile(),hs.PrivateKey,{
encoding:"utf-8"
});
}
debug('hs: %o', hs);
debug('hidden service: ', hs.ServiceID+":"+port);
return hs.ServiceID;
}else{
return null;
}
} catch (error) {
debug('addOnion catch: %o', error);
return null;
}
}