Commit 6e875b88 authored by nanahira's avatar nanahira

add ALLOWED_NETWORK

parent 79a6c84e
Pipeline #37645 passed with stages
in 2 minutes and 34 seconds
......@@ -6,4 +6,5 @@ S3_PATH_PREFIX: ''
S3_ACCESS_KEY_ID: ''
S3_SECRET_ACCESS_KEY: ''
S3_PATH_STYLE: ''
HTTP_TIMEOUT: 30000
\ No newline at end of file
HTTP_TIMEOUT: 30000
ALLOWED_NETWORK: ''
\ No newline at end of file
......@@ -18,6 +18,7 @@
"@nestjs/swagger": "^11.2.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.2",
"ipaddr.js": "^2.2.0",
"nesties": "^1.1.2",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
......@@ -9149,12 +9150,12 @@
}
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz",
"integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==",
"license": "MIT",
"engines": {
"node": ">= 0.10"
"node": ">= 10"
}
},
"node_modules/is-arrayish": {
......@@ -11312,6 +11313,15 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-addr/node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
......
......@@ -10,10 +10,11 @@ import {
PutObjectCommand,
S3Client,
} from '@aws-sdk/client-s3';
import { BlankReturnMessageDto } from 'nesties';
import { BlankReturnMessageDto, GenericReturnMessageDto } from 'nesties';
import { lastValueFrom } from 'rxjs';
import { teex } from './utility/tee-stream';
import { createHash } from 'node:crypto';
import ipaddr from 'ipaddr.js';
@Injectable()
export class AppService extends ConsoleLogger {
......@@ -27,6 +28,20 @@ export class AppService extends ConsoleLogger {
bucket = this.config.get<string>('S3_BUCKET');
endpoint = this.config.get<string>('S3_ENDPOINT');
pathPrefix = this.config.get<string>('S3_PATH_PREFIX') || '';
allowedNetwork = this.config.get<string>('ALLOWED_NETWORK') || '';
allowedNetworks: [ipaddr.IPv4 | ipaddr.IPv6, number][] =
this.allowedNetwork === ''
? []
: this.allowedNetwork.split(',').map((_n) => {
const n = _n.trim();
if (n.includes('/')) {
return ipaddr.parseCIDR(n);
} else {
const parsed = ipaddr.parse(n);
const prefixLength = parsed.kind() === 'ipv6' ? 128 : 32;
return [parsed, prefixLength];
}
});
s3 = new S3Client({
credentials: {
......@@ -41,13 +56,36 @@ export class AppService extends ConsoleLogger {
const url = req.originalUrl.startsWith('/')
? req.originalUrl.slice(1)
: req.originalUrl;
const clientIp = req.ip.startsWith('::ffff:') ? req.ip.slice(7) : req.ip;
if (
this.allowedNetworks.length &&
!this.allowedNetworks.some((network) => {
const [addr, prefixLength] = network;
const ip = ipaddr.parse(clientIp);
return ip.kind() === addr.kind() && ip.match(addr, prefixLength);
})
) {
this.log(
`Client ${clientIp} is not allowed to access this service, redirecting to upstream ${url}`,
);
res.header('location', url);
throw new BlankReturnMessageDto(
301,
'Redirecting to upstream',
).toException();
}
const urlObj = new URL(url);
const filename = urlObj.pathname.split('/').pop();
if (!filename) {
throw new BlankReturnMessageDto(400, 'Bad filename').toException();
}
this.log(`Client ${req.ip} requested file ${filename} from ${url}`);
this.log(
`Client ${clientIp} (${req.headers['user-agent'] || 'Unknown'}) requested file ${filename} from ${url}`,
);
const s3Key = `${this.pathPrefix}${filename}`;
const runExisting = async () => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment