2018-01-29 15:47:26 -08:00
'use strict' ;
exports = module . exports = {
2020-12-21 22:36:10 -08:00
providerTokenAuth ,
2024-04-26 20:06:53 +02:00
verifyUnprovisioned ,
2020-12-21 22:36:10 -08:00
setup ,
activate ,
restore ,
getStatus ,
2023-08-08 21:00:13 +02:00
setupTokenAuth ,
2023-08-10 18:45:27 +05:30
getBlockDevices ,
2024-04-26 20:36:23 +02:00
detectIP
2018-01-29 15:47:26 -08:00
} ;
2021-06-29 14:26:34 -07:00
const assert = require ( 'assert' ) ,
2021-09-30 09:50:30 -07:00
AuditSource = require ( '../auditsource.js' ) ,
2019-10-23 15:45:09 -07:00
BoxError = require ( '../boxerror.js' ) ,
2018-01-29 15:47:26 -08:00
HttpError = require ( 'connect-lastmile' ) . HttpError ,
HttpSuccess = require ( 'connect-lastmile' ) . HttpSuccess ,
2024-04-26 20:36:23 +02:00
network = require ( '../network.js' ) ,
2020-12-21 22:36:10 -08:00
paths = require ( '../paths.js' ) ,
2018-12-11 15:29:47 -08:00
provision = require ( '../provision.js' ) ,
2020-12-21 22:36:10 -08:00
safe = require ( 'safetydance' ) ,
2025-02-14 17:26:54 +01:00
superagent = require ( '../superagent.js' ) ,
2023-08-08 21:00:13 +02:00
system = require ( '../system.js' ) ,
users = require ( '../users.js' ) ;
2018-01-29 15:47:26 -08:00
2020-12-21 22:36:10 -08:00
function setupTokenAuth ( req , res , next ) {
assert . strictEqual ( typeof req . body , 'object' ) ;
const setupToken = safe . fs . readFileSync ( paths . SETUP _TOKEN _FILE , 'utf8' ) ;
if ( ! setupToken ) return next ( ) ;
if ( ! req . body . setupToken ) return next ( new HttpError ( 400 , 'setup token required' ) ) ;
if ( setupToken . trim ( ) !== req . body . setupToken ) return next ( new HttpError ( 422 , 'setup token does not match' ) ) ;
return next ( ) ;
}
2024-04-26 20:06:53 +02:00
async function verifyUnprovisioned ( req , res , next ) {
const activated = await users . isActivated ( ) ;
if ( activated ) return next ( new HttpError ( 405 , 'route unavailable post activation' ) ) ;
next ( ) ;
}
2021-08-25 19:41:46 -07:00
async function providerTokenAuth ( req , res , next ) {
2018-01-29 15:47:26 -08:00
assert . strictEqual ( typeof req . body , 'object' ) ;
2023-08-10 18:45:27 +05:30
if ( system . getProvider ( ) === 'ami' ) {
2018-01-29 15:47:26 -08:00
if ( typeof req . body . providerToken !== 'string' || ! req . body . providerToken ) return next ( new HttpError ( 400 , 'providerToken must be a non empty string' ) ) ;
2024-07-26 18:59:32 +02:00
// IMDSv2 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-options.html
// https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/
const imdsIp = req . body . ipv4Config ? . provider === 'noop' ? '[fd00:ec2::254]' : '169.254.169.254' ; // use ipv4config carefully, it's not validated yet at this point
const [ tokenError , tokenResponse ] = await safe ( superagent . put ( ` http:// ${ imdsIp } /latest/api/token ` ) . set ( 'x-aws-ec2-metadata-token-ttl-seconds' , 600 ) . timeout ( 30 * 1000 ) . ok ( ( ) => true ) ) ;
if ( tokenError ) return next ( new HttpError ( 422 , ` Network error getting EC2 metadata session token: ${ tokenError . message } ` ) ) ;
2025-02-14 17:26:54 +01:00
if ( tokenResponse . status !== 200 ) return next ( new HttpError ( 422 , ` Unable to get EC2 meta data session token. status: ${ tokenResponse . status } ` ) ) ;
2024-07-26 18:59:32 +02:00
const imdsToken = tokenResponse . text ;
const [ error , response ] = await safe ( superagent . get ( ` http:// ${ imdsIp } /latest/meta-data/instance-id ` ) . set ( 'x-aws-ec2-metadata-token' , imdsToken ) . timeout ( 30 * 1000 ) . ok ( ( ) => true ) ) ;
2021-08-25 19:41:46 -07:00
if ( error ) return next ( new HttpError ( 422 , ` Network error getting EC2 metadata: ${ error . message } ` ) ) ;
2025-02-14 17:26:54 +01:00
if ( response . status !== 200 ) return next ( new HttpError ( 422 , ` Unable to get EC2 meta data. status: ${ response . status } ` ) ) ;
2021-11-17 19:05:26 -08:00
if ( response . text !== req . body . providerToken ) return next ( new HttpError ( 422 , 'Instance ID does not match' ) ) ;
2018-01-29 15:47:26 -08:00
2021-08-25 19:41:46 -07:00
next ( ) ;
2018-01-29 15:47:26 -08:00
} else {
next ( ) ;
}
}
2021-08-27 09:52:24 -07:00
async function setup ( req , res , next ) {
2018-01-29 15:47:26 -08:00
assert . strictEqual ( typeof req . body , 'object' ) ;
2022-01-05 22:41:41 -08:00
if ( ! req . body . domainConfig || typeof req . body . domainConfig !== 'object' ) return next ( new HttpError ( 400 , 'domainConfig is required' ) ) ;
2018-01-29 15:47:26 -08:00
2022-01-05 22:41:41 -08:00
const domainConfig = req . body . domainConfig ;
2018-01-29 15:47:26 -08:00
2022-01-05 22:41:41 -08:00
if ( typeof domainConfig . provider !== 'string' || ! domainConfig . provider ) return next ( new HttpError ( 400 , 'provider is required' ) ) ;
if ( typeof domainConfig . domain !== 'string' || ! domainConfig . domain ) return next ( new HttpError ( 400 , 'domain is required' ) ) ;
2018-01-31 17:42:26 +01:00
2022-01-05 22:41:41 -08:00
if ( 'zoneName' in domainConfig && typeof domainConfig . zoneName !== 'string' ) return next ( new HttpError ( 400 , 'zoneName must be a string' ) ) ;
if ( ! domainConfig . config || typeof domainConfig . config !== 'object' ) return next ( new HttpError ( 400 , 'config must be an object' ) ) ;
2018-10-30 13:36:00 -07:00
2022-01-05 22:41:41 -08:00
if ( 'tlsConfig' in domainConfig && typeof domainConfig . tlsConfig !== 'object' ) return next ( new HttpError ( 400 , 'tlsConfig must be an object' ) ) ;
if ( domainConfig . tlsConfig && ( ! domainConfig . tlsConfig . provider || typeof domainConfig . tlsConfig . provider !== 'string' ) ) return next ( new HttpError ( 400 , 'tlsConfig.provider must be a string' ) ) ;
2018-10-30 13:36:00 -07:00
2023-08-03 06:05:29 +05:30
if ( 'ipv4Config' in req . body && typeof req . body . ipv4Config !== 'object' ) return next ( new HttpError ( 400 , 'ipv4Config must be an object' ) ) ;
2024-04-25 19:33:04 +02:00
if ( 'ipv6Config' in req . body && typeof req . body . ipv6Config !== 'object' ) return next ( new HttpError ( 400 , 'ipv6Config must be an object' ) ) ;
2018-10-30 18:43:52 -07:00
2018-11-02 17:24:38 -07:00
// it can take sometime to setup DNS, register cloudron
req . clearTimeout ( ) ;
2024-04-25 19:33:04 +02:00
const [ error ] = await safe ( provision . setup ( domainConfig , req . body . ipv4Config || { provider : 'generic' } , req . body . ipv6Config || { provider : 'generic' } , AuditSource . fromRequest ( req ) ) ) ;
2021-08-27 09:52:24 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2018-01-29 15:47:26 -08:00
2021-08-27 09:52:24 -07:00
next ( new HttpSuccess ( 200 , { } ) ) ;
2018-01-29 15:47:26 -08:00
}
2021-07-15 09:50:11 -07:00
async function activate ( req , res , next ) {
2018-01-29 15:47:26 -08:00
assert . strictEqual ( typeof req . body , 'object' ) ;
if ( typeof req . body . username !== 'string' ) return next ( new HttpError ( 400 , 'username must be string' ) ) ;
if ( typeof req . body . password !== 'string' ) return next ( new HttpError ( 400 , 'password must be string' ) ) ;
if ( typeof req . body . email !== 'string' ) return next ( new HttpError ( 400 , 'email must be string' ) ) ;
if ( 'displayName' in req . body && typeof req . body . displayName !== 'string' ) return next ( new HttpError ( 400 , 'displayName must be string' ) ) ;
2021-07-15 09:50:11 -07:00
const { username , password , email } = req . body ;
const displayName = req . body . displayName || '' ;
2018-01-29 15:47:26 -08:00
2025-01-29 10:35:09 +01:00
const ip = req . headers [ 'x-forwarded-for' ] || req . socket . remoteAddress ;
2018-01-29 15:47:26 -08:00
2021-09-30 09:50:30 -07:00
const [ error , info ] = await safe ( provision . activate ( username , password , email , displayName , ip , AuditSource . fromRequest ( req ) ) ) ;
2021-07-15 09:50:11 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2018-01-29 15:47:26 -08:00
2021-07-15 09:50:11 -07:00
next ( new HttpSuccess ( 201 , info ) ) ;
2018-01-29 15:47:26 -08:00
}
2021-08-27 09:52:24 -07:00
async function restore ( req , res , next ) {
2018-01-29 15:47:26 -08:00
assert . strictEqual ( typeof req . body , 'object' ) ;
if ( ! req . body . backupConfig || typeof req . body . backupConfig !== 'object' ) return next ( new HttpError ( 400 , 'backupConfig is required' ) ) ;
2021-02-24 14:56:09 -08:00
const backupConfig = req . body . backupConfig ;
2018-01-29 15:47:26 -08:00
if ( typeof backupConfig . provider !== 'string' ) return next ( new HttpError ( 400 , 'provider is required' ) ) ;
2024-04-09 13:20:01 +02:00
if ( typeof backupConfig . format !== 'string' ) return next ( new HttpError ( 400 , 'format must be a string' ) ) ;
2020-05-12 10:31:51 -07:00
if ( 'password' in backupConfig && typeof backupConfig . password !== 'string' ) return next ( new HttpError ( 400 , 'password must be a string' ) ) ;
2022-06-27 09:17:01 -07:00
if ( 'encryptedFilenames' in req . body && typeof req . body . encryptedFilenames !== 'boolean' ) return next ( new HttpError ( 400 , 'encryptedFilenames must be a boolean' ) ) ;
2022-04-05 09:28:30 -07:00
if ( typeof req . body . remotePath !== 'string' ) return next ( new HttpError ( 400 , 'remotePath must be a string' ) ) ;
2018-01-29 15:47:26 -08:00
if ( typeof req . body . version !== 'string' ) return next ( new HttpError ( 400 , 'version must be a string' ) ) ;
2023-08-03 06:05:29 +05:30
if ( 'ipv4Config' in req . body && typeof req . body . ipv4Config !== 'object' ) return next ( new HttpError ( 400 , 'ipv4Config must be an object' ) ) ;
2024-04-25 19:33:04 +02:00
if ( 'ipv6Config' in req . body && typeof req . body . ipv6Config !== 'object' ) return next ( new HttpError ( 400 , 'ipv6Config must be an object' ) ) ;
2021-02-24 14:56:09 -08:00
if ( 'skipDnsSetup' in req . body && typeof req . body . skipDnsSetup !== 'boolean' ) return next ( new HttpError ( 400 , 'skipDnsSetup must be a boolean' ) ) ;
2019-11-11 09:49:18 -08:00
2021-02-24 14:56:09 -08:00
const options = {
skipDnsSetup : req . body . skipDnsSetup || false
} ;
2024-04-25 19:33:04 +02:00
const [ error ] = await safe ( provision . restore ( backupConfig , req . body . remotePath , req . body . version , req . body . ipv4Config || { provider : 'generic' } , req . body . ipv6Config || { provider : 'generic' } , options , AuditSource . fromRequest ( req ) ) ) ;
2021-08-27 09:52:24 -07:00
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2018-01-29 15:47:26 -08:00
2021-08-27 09:52:24 -07:00
next ( new HttpSuccess ( 200 , { } ) ) ;
2018-01-29 15:47:26 -08:00
}
2018-12-15 15:27:16 -08:00
2021-08-18 13:25:42 -07:00
async function getStatus ( req , res , next ) {
const [ error , status ] = await safe ( provision . getStatus ( ) ) ;
if ( error ) return next ( BoxError . toHttpError ( error ) ) ;
2018-12-15 15:27:16 -08:00
2021-08-18 13:25:42 -07:00
next ( new HttpSuccess ( 200 , status ) ) ;
2018-12-15 15:27:16 -08:00
}
2023-08-08 21:00:13 +02:00
async function getBlockDevices ( req , res , next ) {
const [ error , devices ] = await safe ( system . getBlockDevices ( ) ) ;
if ( error ) return next ( new HttpError ( 500 , error ) ) ;
next ( new HttpSuccess ( 200 , { devices } ) ) ;
}
2024-04-26 20:36:23 +02:00
async function detectIP ( req , res , next ) {
const result = await network . detectIP ( ) ;
next ( new HttpSuccess ( 200 , { ipv4 : result . ipv4 , ipv6 : result . ipv6 } ) ) ;
}