mirror of
https://github.com/Cameri/nostream.git
synced 2025-05-12 02:49:57 +02:00
feat: automatic onion services
This commit is contained in:
parent
7d9ef0e216
commit
3731d03230
@ -1,6 +1,7 @@
|
|||||||
FROM node:18-alpine3.16 as build
|
FROM node:18-alpine3.16 as build
|
||||||
|
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
RUN apk add --no-cache --update git
|
||||||
|
|
||||||
COPY ["package.json", "package-lock.json", "./"]
|
COPY ["package.json", "package-lock.json", "./"]
|
||||||
|
|
||||||
@ -26,6 +27,8 @@ ENV DB_USER=nostr-ts-relay
|
|||||||
ENV DB_PASSWORD=nostr-ts-relay
|
ENV DB_PASSWORD=nostr-ts-relay
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
RUN apk add --no-cache --update git
|
||||||
|
RUN mkdir /app/.nostr && chown 1000:1000 /app/.nostr
|
||||||
|
|
||||||
COPY --from=build /build/dist .
|
COPY --from=build /build/dist .
|
||||||
|
|
||||||
|
@ -16,6 +16,10 @@ services:
|
|||||||
REDIS_PORT: 6379
|
REDIS_PORT: 6379
|
||||||
REDIS_USER: default
|
REDIS_USER: default
|
||||||
REDIS_PASSWORD: nostr_ts_relay
|
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:
|
# Enable DEBUG for troubleshooting. Examples:
|
||||||
# DEBUG: "worker:*"
|
# DEBUG: "worker:*"
|
||||||
# DEBUG: "knex:query"
|
# DEBUG: "knex:query"
|
||||||
@ -31,13 +35,14 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
migrations:
|
migrations:
|
||||||
condition: service_completed_successfully
|
condition: service_completed_successfully
|
||||||
|
tor_proxy:
|
||||||
|
condition: service_healthy
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
networks:
|
networks:
|
||||||
default:
|
default:
|
||||||
ipv4_address: 10.10.10.2
|
ipv4_address: 10.10.10.2
|
||||||
db:
|
db:
|
||||||
image: postgres
|
image: postgres
|
||||||
container_name: db
|
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: nostr_ts_relay
|
POSTGRES_DB: nostr_ts_relay
|
||||||
POSTGRES_USER: nostr_ts_relay
|
POSTGRES_USER: nostr_ts_relay
|
||||||
@ -62,7 +67,6 @@ services:
|
|||||||
start_period: 360s
|
start_period: 360s
|
||||||
cache:
|
cache:
|
||||||
image: redis:7.0.5-alpine3.16
|
image: redis:7.0.5-alpine3.16
|
||||||
container_name: cache
|
|
||||||
volumes:
|
volumes:
|
||||||
- cache:/data
|
- cache:/data
|
||||||
command: redis-server --save 20 1 --loglevel warning --requirepass nostr_ts_relay
|
command: redis-server --save 20 1 --loglevel warning --requirepass nostr_ts_relay
|
||||||
@ -77,7 +81,6 @@ services:
|
|||||||
retries: 5
|
retries: 5
|
||||||
migrations:
|
migrations:
|
||||||
image: node:18-alpine3.16
|
image: node:18-alpine3.16
|
||||||
container_name: migrations
|
|
||||||
environment:
|
environment:
|
||||||
DB_HOST: db
|
DB_HOST: db
|
||||||
DB_PORT: 5432
|
DB_PORT: 5432
|
||||||
@ -97,7 +100,19 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
default:
|
default:
|
||||||
ipv4_address: 10.10.10.254
|
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:
|
networks:
|
||||||
default:
|
default:
|
||||||
name: nostr-ts-relay
|
name: nostr-ts-relay
|
||||||
|
11
package-lock.json
generated
11
package-lock.json
generated
@ -18,6 +18,7 @@
|
|||||||
"pg-query-stream": "4.2.4",
|
"pg-query-stream": "4.2.4",
|
||||||
"ramda": "0.28.0",
|
"ramda": "0.28.0",
|
||||||
"redis": "4.5.1",
|
"redis": "4.5.1",
|
||||||
|
"tor-control-ts": "^1.0.0",
|
||||||
"ws": "8.11.0"
|
"ws": "8.11.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -11806,6 +11807,11 @@
|
|||||||
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==",
|
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==",
|
||||||
"dev": true
|
"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": {
|
"node_modules/tr46": {
|
||||||
"version": "0.0.3",
|
"version": "0.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
@ -21408,6 +21414,11 @@
|
|||||||
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==",
|
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==",
|
||||||
"dev": true
|
"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": {
|
"tr46": {
|
||||||
"version": "0.0.3",
|
"version": "0.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
@ -117,6 +117,7 @@
|
|||||||
"pg-query-stream": "4.2.4",
|
"pg-query-stream": "4.2.4",
|
||||||
"ramda": "0.28.0",
|
"ramda": "0.28.0",
|
||||||
"redis": "4.5.1",
|
"redis": "4.5.1",
|
||||||
|
"tor-control-ts": "^1.0.0",
|
||||||
"ws": "8.11.0"
|
"ws": "8.11.0"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Cluster, Worker } from 'cluster'
|
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 { createLogger } from '../factories/logger-factory'
|
||||||
import { IRunnable } from '../@types/base'
|
import { IRunnable } from '../@types/base'
|
||||||
import { ISettings } from '../@types/settings'
|
import { ISettings } from '../@types/settings'
|
||||||
@ -39,6 +40,9 @@ export class App implements IRunnable {
|
|||||||
░ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ░ ░░ ░ ░ ░ ▒ ░ ░
|
░ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ░ ░░ ░ ░ ░ ▒ ░ ░
|
||||||
░ ░ ░ ░ ░ ░ ░ ░ ░ ░`)
|
░ ░ ░ ░ ░ ░ ░ ░ ░ ░`)
|
||||||
const width = 74
|
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 logCentered = (input: string, width: number) => {
|
||||||
const start = (width >> 1) - (input.length >> 1)
|
const start = (width >> 1) - (input.length >> 1)
|
||||||
console.log(' '.repeat(start), input)
|
console.log(' '.repeat(start), input)
|
||||||
@ -58,6 +62,13 @@ export class App implements IRunnable {
|
|||||||
logCentered(`${workerCount} workers started`, width)
|
logCentered(`${workerCount} workers started`, width)
|
||||||
|
|
||||||
debug('settings: %O', this.settingsFactory())
|
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) {
|
private onClusterMessage(source: Worker, message: Serializable) {
|
||||||
|
83
src/tor/client.ts
Normal file
83
src/tor/client.ts
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user