Skip to content

Commit

Permalink
Add S3 webresource handler
Browse files Browse the repository at this point in the history
Change-type: minor
  • Loading branch information
otaviojacobi committed Sep 29, 2023
1 parent d632e07 commit a27f4b5
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 28 deletions.
2 changes: 2 additions & 0 deletions config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ConfigLoader } from '@balena/pinejs';
import * as balenaModel from './src/balena';
import { getFileUploadHandler } from './src/fileupload-handler';

export = {
models: [balenaModel],
Expand All @@ -25,4 +26,5 @@ export = {
],
},
],
webResourceHandler: getFileUploadHandler(),
} as ConfigLoader.Config;
7 changes: 7 additions & 0 deletions config/confd/conf.d/cloudront-pk.pem.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[template]
src = "cloudfront-pk.pem.tmpl"
dest = "/etc/ssl/private/cloudfront-pk.pem"
keys = [
"WEBRESOURCES_CLOUDFRONT_PRIVATEKEY",
]
mode = "0400"
11 changes: 10 additions & 1 deletion config/confd/conf.d/env.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,14 @@ keys = [
"VPN_PORT",
"VPN_SERVICE_API_KEY",
"VPN_GUEST_API_KEY",
"VPN_CONNECT_PROXY_PORT"
"VPN_CONNECT_PROXY_PORT",
"WEBRESOURCES_S3_ACCESS_KEY",
"WEBRESOURCES_S3_SECRET_KEY",
"WEBRESOURCES_S3_REGION",
"WEBRESOURCES_S3_HOST",
"WEBRESOURCES_S3_BUCKET",
"WEBRESOURCES_S3_MAX_FILESIZE",
"WEBRESOURCES_CLOUDFRONT_PUBLICKEY",
"WEBRESOURCES_CLOUDFRONT_HOST",
"WEBRESOURCES_CLOUDFRONT_PRIVATEKEY_PATH"
]
1 change: 1 addition & 0 deletions config/confd/templates/cloudfront-pk.pem.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{base64Decode (getenv "WEBRESOURCES_CLOUDFRONT_PRIVATEKEY" "")}}
9 changes: 9 additions & 0 deletions config/confd/templates/env.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,12 @@ VPN_SERVICE_API_KEY={{getenv "VPN_SERVICE_API_KEY"}}
{{if getenv "VPN_GUEST_API_KEY"}}VPN_GUEST_API_KEY={{getenv "VPN_GUEST_API_KEY"}}{{end}}
{{if getenv "AUTH_RESINOS_REGISTRY_CODE"}}AUTH_RESINOS_REGISTRY_CODE={{getenv "AUTH_RESINOS_REGISTRY_CODE"}}{{end}}
{{if getenv "BROTLI_COMPRESSION_QUALITY"}}BROTLI_COMPRESSION_QUALITY={{getenv "BROTLI_COMPRESSION_QUALITY"}}{{end}}
{{if getenv "WEBRESOURCES_S3_ACCESS_KEY"}}WEBRESOURCES_S3_ACCESS_KEY={{getenv "WEBRESOURCES_S3_ACCESS_KEY"}}{{end}}
{{if getenv "WEBRESOURCES_S3_SECRET_KEY"}}WEBRESOURCES_S3_SECRET_KEY={{getenv "WEBRESOURCES_S3_SECRET_KEY"}}{{end}}
{{if getenv "WEBRESOURCES_S3_REGION"}}WEBRESOURCES_S3_REGION={{getenv "WEBRESOURCES_S3_REGION"}}{{end}}
{{if getenv "WEBRESOURCES_S3_HOST"}}WEBRESOURCES_S3_HOST={{getenv "WEBRESOURCES_S3_HOST"}}{{end}}
{{if getenv "WEBRESOURCES_S3_BUCKET"}}WEBRESOURCES_S3_BUCKET={{getenv "WEBRESOURCES_S3_BUCKET"}}{{end}}
{{if getenv "WEBRESOURCES_S3_MAX_FILESIZE"}}WEBRESOURCES_S3_MAX_FILESIZE={{getenv "WEBRESOURCES_S3_MAX_FILESIZE"}}{{end}}
{{if getenv "WEBRESOURCES_CLOUDFRONT_PUBLICKEY"}}WEBRESOURCES_CLOUDFRONT_PUBLICKEY={{getenv "WEBRESOURCES_CLOUDFRONT_PUBLICKEY"}}{{end}}
{{if getenv "WEBRESOURCES_CLOUDFRONT_HOST"}}WEBRESOURCES_CLOUDFRONT_HOST={{getenv "WEBRESOURCES_CLOUDFRONT_HOST"}}{{end}}
WEBRESOURCES_CLOUDFRONT_PRIVATEKEY_PATH=/etc/ssl/private/cloudfront-pk.pem
68 changes: 42 additions & 26 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
"@balena/env-parsing": "^1.1.5",
"@balena/es-version": "^1.0.2",
"@balena/node-metrics-gatherer": "^6.0.3",
"@balena/pinejs": "^15.3.4",
"@balena/pinejs": "^15.3.8",
"@balena/pinejs-webresource-cloudfront": "^0.0.4",
"@sentry/node": "^7.49.0",
"@types/basic-auth": "^1.1.3",
"@types/bluebird": "^3.5.38",
Expand Down
102 changes: 102 additions & 0 deletions src/fileupload-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { webResourceHandler } from '@balena/pinejs';
import {
CloudFrontHandler,
CloudFrontHandlerProps,
} from '@balena/pinejs-webresource-cloudfront';
import * as fs from 'fs';

import {
WEBRESOURCES_S3_ACCESS_KEY,
WEBRESOURCES_S3_SECRET_KEY,
WEBRESOURCES_S3_REGION,
WEBRESOURCES_S3_HOST,
WEBRESOURCES_S3_BUCKET,
WEBRESOURCES_S3_MAX_FILESIZE,
WEBRESOURCES_CLOUDFRONT_PRIVATEKEY_PATH,
WEBRESOURCES_CLOUDFRONT_PUBLICKEY,
WEBRESOURCES_CLOUDFRONT_HOST,
} from './lib/config';

const getEndpointFromHost = (host: string): string => {
return host.startsWith('http') ? host : `https://${host}`;
};

const getS3Config = (): webResourceHandler.S3HandlerProps | undefined => {
if (
WEBRESOURCES_S3_ACCESS_KEY != null &&
WEBRESOURCES_S3_SECRET_KEY != null &&
WEBRESOURCES_S3_REGION != null &&
WEBRESOURCES_S3_HOST != null &&
WEBRESOURCES_S3_BUCKET != null
) {
return {
endpoint: getEndpointFromHost(WEBRESOURCES_S3_HOST),
accessKey: WEBRESOURCES_S3_ACCESS_KEY,
secretKey: WEBRESOURCES_S3_SECRET_KEY,
region: WEBRESOURCES_S3_REGION,
bucket: WEBRESOURCES_S3_BUCKET,
maxSize: WEBRESOURCES_S3_MAX_FILESIZE,
};
}
};

const getCloudfrontConfig = (): CloudFrontHandlerProps | undefined => {
const s3Config = getS3Config();
if (
s3Config != null &&
WEBRESOURCES_CLOUDFRONT_PRIVATEKEY_PATH != null &&
WEBRESOURCES_CLOUDFRONT_PUBLICKEY != null &&
WEBRESOURCES_CLOUDFRONT_HOST != null
) {
let cfSecretKey: string;
try {
cfSecretKey = fs.readFileSync(
WEBRESOURCES_CLOUDFRONT_PRIVATEKEY_PATH,
'utf-8',
);
} catch (e) {
console.error('Failed to start cloudfront with error', e);
return;
}

return {
cfDistributionDomain: getEndpointFromHost(WEBRESOURCES_CLOUDFRONT_HOST),
cfPublicKeyId: WEBRESOURCES_CLOUDFRONT_PUBLICKEY,
cfSecretKey,
...s3Config,
};
}
};

let handler: webResourceHandler.WebResourceHandler | undefined;
export const getFileUploadHandler = () => {
if (handler == null) {
const cfConfig = getCloudfrontConfig();
if (cfConfig != null) {
handler = new CloudFrontHandler(cfConfig);
console.log('Successfully initialised webresource CloudFront handler.');
console.log({
region: cfConfig.region,
endpoint: cfConfig.endpoint,
bucket: cfConfig.bucket,
cfHost: cfConfig.cfDistributionDomain,
});
return handler;
}

const s3Config = getS3Config();
if (s3Config != null) {
handler = new webResourceHandler.S3Handler(s3Config);
console.log('Successfully initialised webresource S3 handler.');
console.log({
region: s3Config.region,
endpoint: s3Config.endpoint,
bucket: s3Config.bucket,
});
return handler;
}

console.log('No webresource handler loaded.');
}
return handler;
};
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ export * as scheduler from './infra/scheduler';
export * as cache from './infra/cache';
export * as config from './lib/config';
export * as abstractSql from './abstract-sql-utils';
export { getFileUploadHandler } from './fileupload-handler';

export * as deviceState from './features/device-state';
export const errors = {
Expand Down
23 changes: 23 additions & 0 deletions src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,29 @@ export const IGNORE_FROZEN_DEVICE_PERMISSIONS = boolVar(
false,
);

export const WEBRESOURCES_S3_HOST = optionalVar('WEBRESOURCES_S3_HOST');
export const WEBRESOURCES_S3_REGION = optionalVar('WEBRESOURCES_S3_REGION');
export const WEBRESOURCES_S3_ACCESS_KEY = optionalVar(
'WEBRESOURCES_S3_ACCESS_KEY',
);
export const WEBRESOURCES_S3_SECRET_KEY = optionalVar(
'WEBRESOURCES_S3_SECRET_KEY',
);
export const WEBRESOURCES_S3_BUCKET = optionalVar('WEBRESOURCES_S3_BUCKET');
export const WEBRESOURCES_S3_MAX_FILESIZE = intVar(
'WEBRESOURCES_S3_MAX_FILESIZE',
10000000,
);
export const WEBRESOURCES_CLOUDFRONT_PRIVATEKEY_PATH = optionalVar(
'WEBRESOURCES_CLOUDFRONT_PRIVATEKEY_PATH',
);
export const WEBRESOURCES_CLOUDFRONT_PUBLICKEY = optionalVar(
'WEBRESOURCES_CLOUDFRONT_PUBLICKEY',
);
export const WEBRESOURCES_CLOUDFRONT_HOST = optionalVar(
'WEBRESOURCES_CLOUDFRONT_HOST',
);

/**
* Splits an env var in the format of `${username}:${password}`
* into a RedisAuth object. Auth is optional, so this can return
Expand Down

0 comments on commit a27f4b5

Please sign in to comment.