Handshake integration (#302)

This commit is contained in:
Karol Wypchło 2020-07-27 11:30:55 +02:00 committed by GitHub
parent f9f580cf64
commit a7c57b3c5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 171 additions and 2 deletions

View File

@ -15,7 +15,7 @@
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 2018,
"ecmaVersion": 2020,
"sourceType": "module"
},
"plugins": ["react", "cypress"]

1
.gitignore vendored
View File

@ -77,3 +77,4 @@ docker/data
# Cache files
__pycache__
/.idea/

View File

@ -51,6 +51,47 @@ services:
depends_on:
- docker-host
handshake:
build:
context: ./docker/handshake
dockerfile: Dockerfile
container_name: handshake
restart: on-failure
environment:
- HSD_HTTP_HOST=0.0.0.0
- HSD_NETWORK=main
- HSD_PORT=12037
env_file:
- .env
volumes:
- ./docker/data/handshake/.hsd:/root/.hsd
networks:
- shared
expose:
- 12037
handshake-api:
build:
context: .
dockerfile: ./docker/handshake-api/Dockerfile
container_name: handshake-api
restart: on-failure
networks:
- shared
environment:
- HSD_HOST=handshake
- HSD_NETWORK=main
- HSD_PORT=12037
- HOST=0.0.0.0
- NODE_TLS_REJECT_UNAUTHORIZED=0
env_file:
- .env
expose:
- 3100
depends_on:
- handshake
- caddy
health-check:
build:
context: .

View File

@ -37,6 +37,8 @@
}
reverse_proxy /health-check health-check:3100
reverse_proxy /hns/* handshake-api:3100
reverse_proxy /hnsres/* handshake-api:3100
reverse_proxy @blacklist nginx:80 {
header_up User-Agent Sia-Agent

View File

@ -0,0 +1,20 @@
FROM node:14.5
WORKDIR /usr/app
RUN yarn init -y && \
yarn add express express-http-proxy hs-client
COPY handshake-api/index.js ./
ENV HOST="localhost"
ENV PORT=3100
ENV HSD_NETWORK="main"
ENV HSD_HOST="0.0.0.0"
ENV HSD_PORT=12037
ENV HSD_API_KEY="foo"
EXPOSE $PORT
ENTRYPOINT ["node", "index.js"]

View File

@ -0,0 +1,10 @@
FROM node:14.5
WORKDIR /opt/hsd
RUN git clone https://github.com/handshake-org/hsd.git /opt/hsd && \
npm install --production
ENV PATH="${PATH}:/opt/hsd/bin:/opt/hsd/node_modules/.bin"
ENTRYPOINT ["hsd"]

View File

@ -41,6 +41,9 @@ server {
client_body_buffer_size 128k;
client_max_body_size 128k;
rewrite "^(/([a-zA-Z0-9-_]{46})[^/]{0})$" $1/ permanent;
rewrite "^(/hns/[^/]+)[^/]{0}$" $1/ permanent;
location /blacklist {
proxy_cache skynet;
proxy_cache_valid any 1m; # cache blacklist for 1 minute

90
handshake-api/index.js Normal file
View File

@ -0,0 +1,90 @@
const express = require("express");
const proxy = require("express-http-proxy");
const { NodeClient } = require("hs-client");
const host = process.env.HOST || "localhost";
const port = Number(process.env.PORT) || 3100;
const hsdNetworkType = process.env.HSD_NETWORK || "regtest";
const hsdHost = process.env.HSD_HOST || "localhost";
const hsdPort = Number(process.env.HSD_PORT) || 12037;
const hsdApiKey = process.env.HSD_API_KEY || "foo";
const clientOptions = {
network: hsdNetworkType,
host: hsdHost,
port: hsdPort,
apiKey: hsdApiKey,
};
const client = new NodeClient(clientOptions);
const resolveDomain = async (name) => {
const response = await client.execute("getnameresource", [name]);
if (!response) throw new Error("API not responding");
console.log(`${name} => ${JSON.stringify(response.records)}`);
return response;
};
const findSkylinkRecord = (records) => {
return records?.find(({ txt }) => txt?.some((entry) => isValidSkylink(entry)));
};
const getSkylinkFromRecord = (record) => {
return record?.txt?.find((entry) => isValidSkylink(entry));
};
const resolveDomainHandler = async (req, res) => {
try {
const response = await resolveDomain(req.params.name);
const record = findSkylinkRecord(response.records);
if (!record) return res.status(404).send(`No skylink found for ${req.params.name}`);
const skylink = getSkylinkFromRecord(record);
return res.json({ skylink });
} catch (error) {
res.status(500).send(`Handshake error: ${error.message}`);
}
};
const SIA_LINK_RE = /^([a-zA-Z0-9-_]{46}.*)$/;
// Checks if the given string is a valid Sia link.
function isValidSkylink(link) {
if (!link || link.length === 0) {
return false;
}
return Boolean(link.match(SIA_LINK_RE));
}
const server = express();
server.use(
"/hns/:name",
proxy("nginx", {
proxyReqPathResolver: async (req) => {
const response = await resolveDomain(req.params.name);
const record = findSkylinkRecord(response.records);
if (!record) throw new Error(`No skylink found for ${req.params.name}`);
const skylink = getSkylinkFromRecord(record);
// if this is exact domain call, do not append anything to skylink entry
if (req.url === "" || req.url === "/") return `/${skylink}`;
// drop any index.html or trailing slash from the skylink entry
const path = skylink.split("/").slice(0, -1).join("/");
return `/${path}${req.url}`;
},
})
);
server.get("/hnsres/:name", resolveDomainHandler);
server.listen(port, host, (error) => {
if (error) throw error;
console.info(`API will look for HSD Server at ${hsdHost}:${hsdPort}, "${hsdNetworkType}" network.`);
console.info(`Server listening at http://${host}:${port}`);
});

View File

@ -98,6 +98,7 @@ At this point we have almost everything set up. We have 2 siad instances running
- `EMAIL_ADDRESS` (required) is your email address used for communication regarding SSL certification (required)
- `SIA_API_AUTHORIZATION` (required) is token you just generated in the previous point
- `CLOUDFLARE_AUTH_TOKEN` (optional) if using cloudflare as dns loadbalancer (it's just for siasky.net configuration)
- `HSD_API_KEY` (optional) this is a random security key for an optional handshake integration that gets generated automatically
1. if you have a custom domain and you configured it in `DOMAIN_NAME`, edit `/home/user/skynet-webportal/docker/caddy/Caddyfile` and uncomment `import custom.domain`
1. only for siasky.net domain instances: edit `/home/user/skynet-webportal/docker/caddy/Caddyfile`, uncomment `import siasky.net`
1. `sudo docker-compose up -d` to restart the services so they pick up new env variables

View File

@ -23,7 +23,8 @@ docker-compose --version # sanity check
# SIA_API_AUTHORIZATION - the base64 encoded :apipassword string
# CLOUDFLARE_AUTH_TOKEN - cloudflare auth token for ssl generation (just for siasky.net)
if ! [ -f /home/user/skynet-webportal/.env ]; then
printf "DOMAIN_NAME=example.com\nEMAIL_ADDRESS=email@example.com\nSIA_API_AUTHORIZATION=\nCLOUDFLARE_AUTH_TOKEN=\n" > /home/user/skynet-webportal/.env
HSD_API_KEY=$(openssl rand -base64 32) # generate safe random key for handshake
printf "DOMAIN_NAME=example.com\nEMAIL_ADDRESS=email@example.com\nSIA_API_AUTHORIZATION=\nCLOUDFLARE_AUTH_TOKEN=\nHSD_API_KEY=${HSD_API_KEY}\n" > /home/user/skynet-webportal/.env
fi
# Start docker container with nginx and client