Skip to content

Commit

Permalink
update encryption to use AES
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeTWC1984 committed May 2, 2021
1 parent 6186811 commit cef752d
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 17 deletions.
9 changes: 5 additions & 4 deletions Docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# build: docker build -t cronicle:edge -f Dockerfile --build-arg branch=main --build-arg echo=1 --build-arg bldonly=1 .
# build: docker build -t cronicle:edge2 -f Dockerfile --build-arg branch=main --build-arg echo=1 --build-arg bldonly=1 .
# test run: docker run -it -v $HOME/data:/opt/cronicle/data -p 3012:3012 cronicle:edge manager

FROM node:14-alpine3.12
Expand Down Expand Up @@ -52,9 +52,10 @@ ARG branch=main
RUN git clone https://github.com/cronicle-edge/cronicle-edge.git /opt/cronicle
RUN git checkout ${branch}
RUN npm audit fix --force; npm install
ARG bldonly
RUN echo $bldonly
RUN git pull && node bin/build dist
RUN node bin/build dist
# ARG bldonly
# RUN echo $bldonly
# RUN git pull && node bin/build dist

# protect sensitive folders
RUN mkdir -p /opt/cronicle/data /opt/cronicle/conf && chmod 0700 /opt/cronicle/data /opt/cronicle/conf
Expand Down
34 changes: 34 additions & 0 deletions DockerfileDev
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# build: docker build -t cronicle:dev -f DockerfileDev --build-arg echo=1 .
# test run: docker run --rm -it -p 3019:3012 cronicle:dev bash

FROM node:14-alpine3.12
RUN apk add --no-cache git tini util-linux bash openssl procps coreutils curl acl jq
# required: all: tini; alpine: util-linux procps coreutils

# optional lolcat for tty/color debugging
RUN apk add lolcat --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing


ENV CRONICLE_foreground=1
ENV CRONICLE_echo=1
ENV TZ=America/New_York
ENV EDITOR=nvim

ENV PATH "/opt/cronicle/bin:${PATH}"

# non root user for shell plugin
ARG CRONICLE_UID=1007
ARG CRONICLE_GID=1099
RUN addgroup cronicle --gid $CRONICLE_GID && adduser -D -h /opt/cronicle -u $CRONICLE_UID -G cronicle cronicle

COPY . /opt/cronicle
WORKDIR /opt/cronicle
ARG echo
RUN echo $echo
RUN npm audit fix --force; npm install
RUN node bin/build dist

# protect sensitive folders
RUN mkdir -p /opt/cronicle/data /opt/cronicle/conf && chmod 0700 /opt/cronicle/data /opt/cronicle/conf

ENTRYPOINT ["/sbin/tini", "--"]
10 changes: 6 additions & 4 deletions lib/api/secret.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ module.exports = Class.create({

if (secret.encrypted && secret.data) {
try {
secret.data = await self.decrypt(secret.data)
if (secret.data) secret.data = secret.data.toString();
//secret.data = await self.decrypt(secret.data)
//if (secret.data) secret.data = secret.data.toString();
secret.data = self.decryptObject(secret.data)
}
catch (err) {
secret.data = "Failed to decrypt secret:\n" + err;
Expand Down Expand Up @@ -124,8 +125,9 @@ module.exports = Class.create({

if (params.encrypted) {
try {
params.data = await self.encrypt(params.data)
if (params.data) params.data = params.data.toString();
// params.data = await self.encrypt(params.data)
params.data = self.encryptObject(params.data)
//if (params.data) params.data = params.data.toString();
}
catch (err) {
return self.doError('secret', "Failed to encrypt secret (missing key file?)", callback);
Expand Down
29 changes: 27 additions & 2 deletions lib/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ const cp = require('child_process');
const dotenv = require('dotenv');
const openssl = util.promisify(require('openssl-wrapper').exec);

const crypto = require("crypto");
const algorithm = "aes-256-ctr";
const inputEncoding = "utf8";
const outputEncoding = "base64";

module.exports = Class.create({

__name: 'Cronicle',
Expand Down Expand Up @@ -220,6 +225,24 @@ module.exports = Class.create({
return openssl('cms.encrypt', Buffer.from(cipher), encOpts);
},

encryptObject: function (obj) {
const IV = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(algorithm, this.server.config.get('secret_key'), IV);
let crypted = cipher.update(JSON.stringify(obj), inputEncoding, outputEncoding );
crypted += cipher.final(outputEncoding);
return `${IV.toString(outputEncoding)}:${crypted}`;
},

decryptObject: function ( encObj ) {
const encParts = encObj.split(":");
const IV = Buffer.from(encParts[0], outputEncoding);
const encryptedText = Buffer.from(encParts[1], outputEncoding);
const decipher = crypto.createDecipheriv(algorithm, this.server.config.get('secret_key'), IV);
let decrypted = decipher.update(encryptedText, outputEncoding, inputEncoding);
decrypted += decipher.final(inputEncoding);
return JSON.parse(decrypted);
},

updateSecrets: function() {
const self = this;
this.storage.listGet('global/secrets', 0, 0, async function (err, items, list) {
Expand All @@ -231,9 +254,10 @@ module.exports = Class.create({
for (i = 0; i < items.length; i++) {
let secret = JSON.parse(JSON.stringify(items[i]));
try {
if(secret.encrypted) secret.data = await self.decrypt(secret.data)
//if(secret.encrypted) secret.data = await self.decrypt(secret.data)
if(secret.encrypted) secret.data = self.decryptObject(secret.data)
if(secret.form == 'props') secret.data = dotenv.parse(secret.data);
if(secret.form == 'json') secret.data = JSON.parse(secret.data);
//if(secret.form == 'json') secret.data = JSON.parse(secret.data);

}
catch (err) {
Expand All @@ -248,6 +272,7 @@ module.exports = Class.create({
}
});
},


checkmanagerEligibility: function (callback) {
// determine manager server eligibility
Expand Down
21 changes: 14 additions & 7 deletions lib/job.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,14 +214,14 @@ module.exports = Class.create({

// pull in properties from plugin
let globalenv = (self.secrets['globalenv'] || {}).data
if(typeof globalenv != 'object') globalenv = {}
if(globalenv) job.secret = self.encryptObject(globalenv) ;

job.command = plugin.command;
if (plugin.cwd) job.cwd = plugin.cwd;
if (plugin.uid) job.uid = plugin.uid;
if (job.debug_sudo) job.uid = process.getuid();
if (plugin.gid) job.gid = plugin.gid;
job.env = Tools.mergeHashes(plugin.env || {}, globalenv)
job.env = Tools.mergeHashes(plugin.env || {}, self.server.config.get('job_env') || {});
if (plugin.secret) job.env['PLUGIN_SECRET'] = plugin.secret;
if (plugin.id == 'workflow') { // todo - change to flag
let temp_key = Tools.digestHex(job.id + (new Date).toDateString())
Expand Down Expand Up @@ -489,19 +489,26 @@ module.exports = Class.create({
});
}

if(job.secret) {
try {
job.env = Tools.mergeHashes(job.env, self.decryptObject(job.secret));
}
catch (e) {
self.logDebug(6, 'failed to decrypt job secret:', e);
}
}

// setup environment for child
var child_opts = {
cwd: job.cwd || process.cwd(),
uid: job.uid || process.getuid(),
gid: process.getgid(),
env: Tools.mergeHashes(
this.server.config.get('job_env') || {},
Tools.mergeHashes(process.env, job.env || {})
)
env: Tools.mergeHashes(process.env, job.env || {})
};

// drop job.env to prevent from logging (it may include sensitive data)
// drop job.env and secret to prevent logging (it may include sensitive data)
delete job.env;
delete job.secret;

child_opts.env['CRONICLE'] = this.server.__version;
child_opts.env['JOB_ID'] = job.id;
Expand Down

0 comments on commit cef752d

Please sign in to comment.