make ip check in health checks more reliable

This commit is contained in:
Karol Wypchlo 2022-02-07 15:57:24 +01:00
parent f2258cf27e
commit 0292f57f3e
No known key found for this signature in database
GPG Key ID: B515DE9EEBE241E1
8 changed files with 2398 additions and 20 deletions

View File

@ -0,0 +1,23 @@
name: Test - packages/health-check
on:
pull_request:
paths:
- packages/health-check/**
defaults:
run:
working-directory: packages/health-check
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16.x
- run: yarn
- run: yarn jest

View File

@ -7,10 +7,10 @@ WORKDIR /usr/app
ENV PATH="/usr/app/bin:${PATH}" ENV PATH="/usr/app/bin:${PATH}"
# schedule critical checks to run every 5 minutes (any failures will disable server) # schedule critical checks to run every 5 minutes (any failures will disable server)
RUN echo '*/5 * * * * /usr/app/bin/cli run critical > /dev/stdout' >> /etc/crontabs/root RUN echo '*/5 * * * * source /etc/environment ; /usr/app/bin/cli run critical >> /proc/1/fd/1' >> /etc/crontabs/root
# schedule extended checks to run on every hour (optional checks, report only) # schedule extended checks to run on every hour (optional checks, report only)
RUN echo '0 * * * * /usr/app/bin/cli run extended > /dev/stdout' >> /etc/crontabs/root RUN echo '0 * * * * source /etc/environment ; /usr/app/bin/cli run extended >> /proc/1/fd/1' >> /etc/crontabs/root
COPY package.json yarn.lock ./ COPY package.json yarn.lock ./
@ -30,9 +30,10 @@ ENV NODE_ENV production
# 3. start crond in the background to schedule periodic health checks # 3. start crond in the background to schedule periodic health checks
# 4. start the health-check api service # 4. start the health-check api service
CMD [ "sh", "-c", \ CMD [ "sh", "-c", \
"serverip=$(node src/whatismyip.js) ; \ "serverip=$(node src/whatismyip.js) && \
dnsmasq --no-resolv --log-facility=/var/log/dnsmasq.log --address=/$PORTAL_DOMAIN/$serverip --server=127.0.0.11 ; \ echo export serverip=${serverip} >> /etc/environment && \
echo \"$(sed 's/127.0.0.11/127.0.0.1/' /etc/resolv.conf)\" > /etc/resolv.conf ; \ dnsmasq --no-resolv --log-facility=/var/log/dnsmasq.log --address=/$PORTAL_DOMAIN/$serverip --server=127.0.0.11 && \
crond ; \ echo \"$(sed 's/127.0.0.11/127.0.0.1/' /etc/resolv.conf)\" > /etc/resolv.conf && \
crond && \
node src/index.js" \ node src/index.js" \
] ]

View File

@ -18,6 +18,7 @@
"yargs": "^17.3.1" "yargs": "^17.3.1"
}, },
"devDependencies": { "devDependencies": {
"jest": "^27.5.0",
"prettier": "^2.5.1" "prettier": "^2.5.1"
} }
} }

View File

@ -1,13 +1,19 @@
const got = require("got"); const got = require("got");
const { ipCheckService, ipRegex } = require("../utils");
const getCurrentAddress = async () => { const getCurrentAddress = async () => {
// use serverip env variable when available (set via Dockerfile)
if (process.env.serverip) return process.env.serverip;
try { try {
const { body } = await got("http://whatismyip.akamai.com"); const { body } = await got(`http://${ipCheckService}`);
if (body) return body; if (ipRegex.test(body)) return body;
throw new Error("whatismyip.akamai.com responded with empty body");
throw new Error(`${ipCheckService} responded with invalid ip: "${body}"`);
} catch (error) { } catch (error) {
console.log(error.message); console.log(error.message); // log error to console for future reference
return "-- error fetching ip address from whatismyip.akamai.com --";
return null;
} }
}; };
@ -15,7 +21,8 @@ module.exports = async function middleware() {
const ip = await getCurrentAddress(); const ip = await getCurrentAddress();
return (check) => { return (check) => {
if (check.ip && check.ip !== ip) { // check only if current ip and check ip are provided
if (ip && check.ip && check.ip !== ip) {
check.up = false; check.up = false;
check.errors = check.errors ?? []; check.errors = check.errors ?? [];
check.errors.push({ check.errors.push({

View File

@ -1,4 +1,7 @@
const got = require("got"); const ipCheckService = "whatismyip.akamai.com";
const ipRegex = new RegExp(
`^(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}$`
);
/** /**
* Get the time between start and now in milliseconds * Get the time between start and now in milliseconds
@ -60,6 +63,8 @@ function getAuthCookie() {
if (!password) throw new Error("ACCOUNTS_TEST_USER_PASSWORD cannot be empty"); if (!password) throw new Error("ACCOUNTS_TEST_USER_PASSWORD cannot be empty");
async function authenticate() { async function authenticate() {
const got = require("got");
try { try {
// authenticate with given test user credentials // authenticate with given test user credentials
const response = await got.post(`${process.env.SKYNET_DASHBOARD_URL}/api/login`, { const response = await got.post(`${process.env.SKYNET_DASHBOARD_URL}/api/login`, {
@ -114,4 +119,6 @@ module.exports = {
ensureValidJSON, ensureValidJSON,
getAuthCookie, getAuthCookie,
isPortalModuleEnabled, isPortalModuleEnabled,
ipCheckService,
ipRegex,
}; };

View File

@ -0,0 +1,19 @@
describe("ipRegex", () => {
const { ipRegex } = require("./utils");
test("should test true for valid ip", () => {
expect(ipRegex.test("8.8.8.8")).toEqual(true);
expect(ipRegex.test("127.0.0.1")).toEqual(true);
expect(ipRegex.test("192.168.0.1")).toEqual(true);
expect(ipRegex.test("10.10.10.10")).toEqual(true);
expect(ipRegex.test("135.124.12.47")).toEqual(true);
});
test("should test false for invalid ip", () => {
expect(ipRegex.test("888.8.8.8")).toEqual(false);
expect(ipRegex.test("....")).toEqual(false);
expect(ipRegex.test(null)).toEqual(false);
expect(ipRegex.test("foo")).toEqual(false);
expect(ipRegex.test("")).toEqual(false);
});
});

View File

@ -1,13 +1,18 @@
const http = require("http"); const http = require("http");
const { ipCheckService, ipRegex } = require("./utils");
const request = http.request({ host: "whatismyip.akamai.com" }, (response) => { const request = http.request({ host: ipCheckService }, (response) => {
response.on("data", (data) => { response.on("data", (data) => {
process.stdout.write(data); if (ipRegex.test(data)) {
process.stdout.write(data);
} else {
throw new Error(`${ipCheckService} responded with invalid ip: "${data}"`);
}
}); });
}); });
request.on("error", (error) => { request.on("error", (error) => {
console.error(error); throw error; // throw error to exit with code 1
}); });
request.end(); request.end();

File diff suppressed because it is too large Load Diff