Skip to content
This repository has been archived by the owner on Apr 2, 2024. It is now read-only.

Commit

Permalink
Don't insert secrets into a nonexistent config (#55)
Browse files Browse the repository at this point in the history
If the config fetch fails, it is important that we don't try to
substitute in secret values.

We want to validate after substitution, just in case.
  • Loading branch information
amrc-benmorrow authored Feb 9, 2024
2 parents ea5df40 + 1a6609b commit 6932809
Showing 1 changed file with 48 additions and 27 deletions.
75 changes: 48 additions & 27 deletions lib/translator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,44 +264,65 @@ export class Translator extends EventEmitter {
async fetchConfig(uuid: string): Promise<translatorConf> {
const cdb = this.fplus.ConfigDB;

let [config, etag] = await this.retry("config", () => cdb.get_config_with_etag(UUIDs.App.AgentConfig, uuid));
/* XXX This is a mess. We want an Error monad, I think, or
* recast everything with try/catch/custom exceptions */

log(`Fetched config with etag [${etag}]`);

let valid = config && validateConfig(config);

// Replace all occurrences of __FPSI_<v4UUID> with the actual secret of the same name from /etc/secrets
let [config, etag] = await this.retry("config", () =>
cdb.get_config_with_etag(UUIDs.App.AgentConfig, uuid));

// First, convert it to a string
const configString = JSON.stringify(config);
log(`Fetched config with etag [${etag}]`);

// Then, replace all occurrences of __FPSI_<v4UUID> with the actual secret of the same name from /etc/secrets
const secretReplacedConfig = configString.replace(/__FPSI__[a-f0-9-]{36}/g, (match) => {
try {
// Attempt to get the secret contents from the file in /etc/secrets with the same name
const secretPath = `/etc/secrets/${match}`;
return fs.readFileSync(secretPath, 'utf8');
} catch (err: any) {
// Handle error (e.g., file not found) gracefully
console.error(`Error reading secret from ${match}: ${err.message}`);
valid = false;
return "SECRET_NOT_FOUND";
let valid = true;
if (config) {
// Replace all occurrences of __FPSI_<v4UUID> with the actual
// secret of the same name from /etc/secrets

// First, convert it to a string
const configString = JSON.stringify(config);

// Then, replace all occurrences of __FPSI_<v4UUID> with the
// actual secret of the same name from /etc/secrets
const secretReplacedConfig = configString.replace(
/__FPSI__[a-f0-9-]{36}/g,
(match) => {
try {
// Attempt to get the secret contents from the file in /etc/secrets with the same name
const secretPath = `/etc/secrets/${match}`;
return fs.readFileSync(secretPath, 'utf8');
} catch (err: any) {
// Handle error (e.g., file not found) gracefully
console.error(`Error reading secret from ${match}: ${err.message}`);
valid = false;
return "SECRET_NOT_FOUND";
}
});

// Then, convert it back to an object and validate
if (valid) {
try {
config = JSON.parse(secretReplacedConfig);
valid = validateConfig(config);
}
catch {
valid = false;
}
}
});

// Then, convert it back to an object
config = JSON.parse(secretReplacedConfig);
}

const conns = valid ? reHashConf(config).deviceConnections : [];
const conns = config && valid ? reHashConf(config).deviceConnections : [];

if (!valid) log("Config is invalid or nonexistent, ignoring");

const rv = {
sparkplug: {
nodeControl: config?.sparkplug, configRevision: etag, alerts: {
configFetchFailed: !config, configInvalid: !!config && !valid,
nodeControl: config?.sparkplug,
configRevision: etag,
alerts: {
configFetchFailed: !config,
configInvalid: !valid,
},
}, deviceConnections: conns,
},
deviceConnections: conns,
};

log(`Mapped config: ${JSON.stringify(config)}\n->\n${JSON.stringify(rv)}`);
Expand Down

0 comments on commit 6932809

Please sign in to comment.