mirror of
https://github.com/harish2704/ipv6-global-ip-demo.git
synced 2026-09-10 07:11:08 +00:00
* Refactoring and documentation
This commit is contained in:
@@ -1,2 +1,10 @@
|
||||
# ipv6-global-ip-demo
|
||||
A simple web application to test whether your ISP provides you 2^64 ipv6 global IP or not
|
||||
|
||||
## Usage
|
||||
|
||||
* clone the repo
|
||||
* run `npm install` or `yarn install`
|
||||
* export environment variable `ETH_DEV` which points to ether net interface with public IPv6 address.
|
||||
* Run server . Eg: `env ETH_DEV=em1 npm run server`, where em1 is the ether net interface with global IPv6 IP.
|
||||
* Open [http://127.0.0.1:3553/] in the browser to access the application
|
||||
|
||||
@@ -6,16 +6,17 @@
|
||||
const Koa = require('koa');
|
||||
const Router = require('koa-joi-router');
|
||||
const util = require('util');
|
||||
const {RateLimiterMemory} = require('rate-limiter-flexible');
|
||||
|
||||
|
||||
const app = new Koa();
|
||||
const router = new Router();
|
||||
const Joi = Router.Joi;
|
||||
const _exec = util.promisify( require('child_process').exec );
|
||||
const ethDev = process.env.ETH_DEV
|
||||
const {RateLimiterMemory} = require('rate-limiter-flexible');
|
||||
const rateLimitOpts = {
|
||||
points: 1, // 1 request for ctx.ip
|
||||
duration: 1, // per 1 second
|
||||
points: 5, // 1 request for ctx.ip
|
||||
duration: 5, // per 1 second
|
||||
};
|
||||
const rateLimiter = new RateLimiterMemory( rateLimitOpts );
|
||||
const MyJoi = {
|
||||
@@ -23,7 +24,8 @@ const MyJoi = {
|
||||
return Joi.string().ip({
|
||||
version: [
|
||||
'ipv6'
|
||||
]
|
||||
],
|
||||
cidr: 'forbidden'
|
||||
})
|
||||
},
|
||||
ipv6Array: function(){
|
||||
@@ -41,6 +43,64 @@ async function exec( cmd ){
|
||||
}
|
||||
|
||||
|
||||
function setRateLimitHeader( ctx, rateLimitInfo ){
|
||||
ctx.response.set({
|
||||
"X-RateLimit-Limit": rateLimitOpts.points,
|
||||
"X-RateLimit-Remaining": rateLimitInfo.remainingPoints,
|
||||
"X-RateLimit-Reset": new Date(Date.now() + rateLimitInfo.msBeforeNext).toUTCString()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function rateLimiterMiddleWare( ctx, next ){
|
||||
try {
|
||||
let rlInfo = await rateLimiter.consume(ctx.ip)
|
||||
setRateLimitHeader( ctx, rlInfo );
|
||||
await next().catch();
|
||||
} catch ( rlInfo ) {
|
||||
ctx.status = 429
|
||||
ctx.response.set({
|
||||
"Retry-After": rlInfo.msBeforeNext / 1000,
|
||||
});
|
||||
setRateLimitHeader( ctx, rlInfo );
|
||||
ctx.body = { error: 'Too Many Requests. Please wait for some time' };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function getIpList( ctx ){
|
||||
const ips = await exec(`ip -6 a show dev ${ethDev} scope global | grep inet6 | awk '{print $2}'`)
|
||||
ctx.body = { ips : ips.split('\n').filter(v => v.length ).map( v => v.slice(0, -3)) };
|
||||
}
|
||||
|
||||
|
||||
async function addIp( ctx ){
|
||||
const ip = ctx.request.body.ip;
|
||||
try {
|
||||
await exec(`sudo ip -6 a add ${ip}/64 dev ${ethDev}`);
|
||||
} catch (e) {
|
||||
ctx.status = 409;
|
||||
ctx.body = { error: 'Failed to assign ip'};
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove ip after some time
|
||||
setTimeout(function(){
|
||||
exec(`sudo ip -6 a del dev ${ethDev} ${ip}/64`);
|
||||
},60*1000 );
|
||||
|
||||
const ips = await exec(`ip -6 a show dev ${ethDev}| grep inet6.*global | awk '{print $2}'`);
|
||||
ctx.body = { ips : ips.split('\n').filter(v => v.length ).map( v => v.slice(0, -3)) };
|
||||
}
|
||||
|
||||
|
||||
async function logger( ctx, next ){
|
||||
const startTime = Date.now();
|
||||
await next();
|
||||
const requestTime = Date.now() - startTime;
|
||||
console.log(`${ctx.method} ${ctx.response.status} ${ctx.ip} "${ctx.request.headers['user-agent']}" - ${requestTime}`);
|
||||
}
|
||||
|
||||
|
||||
router.route({
|
||||
method: 'get',
|
||||
@@ -54,10 +114,7 @@ router.route({
|
||||
}
|
||||
}
|
||||
},
|
||||
handler: async (ctx) => {
|
||||
const ips = await exec(`ip -6 a show dev ${ethDev} scope global | grep inet6 | awk '{print $2}'`)
|
||||
ctx.body = { ips : ips.split('\n').filter(v => v.length ).map( v => v.slice(0, -3)) };
|
||||
}
|
||||
handler: getIpList
|
||||
});
|
||||
|
||||
|
||||
@@ -77,43 +134,13 @@ router.route({
|
||||
}
|
||||
}
|
||||
},
|
||||
handler: async (ctx) => {
|
||||
const ip = ctx.request.body.ip;
|
||||
try {
|
||||
await exec(`sudo ip -6 a add ${ip}/64 dev ${ethDev}`);
|
||||
} catch (e) {
|
||||
ctx.status = 409;
|
||||
ctx.body = { error: 'Failed to assign ip'};
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove ip after some time
|
||||
setTimeout(function(){
|
||||
exec(`sudo ip -6 a del dev ${ethDev} ${ip}/64`);
|
||||
},60*1000 );
|
||||
|
||||
const ips = await exec(`ip -6 a show dev ${ethDev}| grep inet6.*global | awk '{print $2}'`);
|
||||
ctx.body = { ips : ips.split('\n').filter(v => v.length ).map( v => v.slice(0, -3)) };
|
||||
}
|
||||
handler: addIp
|
||||
});
|
||||
|
||||
|
||||
app.use(require('koa-static')('./static'));
|
||||
app.use( async ( ctx, next ) => {
|
||||
try {
|
||||
await rateLimiter.consume(ctx.ip)
|
||||
await next();
|
||||
} catch (rejRes) {
|
||||
ctx.status = 429
|
||||
ctx.response.set({
|
||||
"Retry-After": rejRes.msBeforeNext / 1000,
|
||||
"X-RateLimit-Limit": rateLimitOpts.points,
|
||||
"X-RateLimit-Remaining": rejRes.remainingPoints,
|
||||
"X-RateLimit-Reset": new Date(Date.now() + rejRes.msBeforeNext)
|
||||
});
|
||||
ctx.body = { error: 'Too Many Requests. Please wait for some time' };
|
||||
}
|
||||
});
|
||||
app.use(router.middleware());
|
||||
app.use( logger );
|
||||
app.use( require('koa-static')('./static') );
|
||||
app.use( rateLimiterMiddleWare );
|
||||
app.use( router.middleware() );
|
||||
app.listen( 3553 );
|
||||
|
||||
|
||||
+1
-2
@@ -46,8 +46,7 @@ var app = new Vue({
|
||||
this.ips = res.ips;
|
||||
})
|
||||
.catch( res =>{
|
||||
console.log( res );
|
||||
// alert( res.error )
|
||||
alert( res.error )
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -5,11 +5,19 @@
|
||||
<title>IPV6 global ip test</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>What is this?</h2>
|
||||
<pre>
|
||||
* List of public IPv6 IP assigned to the currently running server is shown below.
|
||||
* We can click on each link and verify whether that IP is globally accessible or not by visiting the link
|
||||
* Optionally, we can add new public IPv6 address using the form displayed in the bottom.
|
||||
* Newly added IP will only persist for one minute. It will be removed after that.
|
||||
* Please note that, we are only allowed change last 64 bits of existing global ip. Otherwise it won't work
|
||||
</pre>
|
||||
<div id="app">
|
||||
<h3>List of global IPv6 address assigned to this server</h3>
|
||||
<ul id="cont-ip-list">
|
||||
<li v-for="ip in ips">
|
||||
<a :href="'http://[' + ip + ']:3553'">
|
||||
<a target="_blank" :href="'http://[' + ip + ']:3553'">
|
||||
{{ ip }}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user