Skip to content

Commit

Permalink
Impemented trusted.extensions support
Browse files Browse the repository at this point in the history
Also, Hiera Editor now submits CSR with pp_project = "hiera_editor" so it could be easily authenticated by extensions.pp_project field in the certificate
Fixed hierarchy resolving
  • Loading branch information
desertkun committed Jan 23, 2019
1 parent 23535a6 commit 0744cc6
Show file tree
Hide file tree
Showing 11 changed files with 305 additions and 173 deletions.
185 changes: 83 additions & 102 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hiera-editor",
"version": "0.1.3",
"version": "0.1.4",
"description": "A GUI tool to manage your Puppet/Hiera for you",
"main": "dist/main.js",
"scripts": {
Expand Down Expand Up @@ -86,6 +86,7 @@
},
"dependencies": {
"@fortawesome/fontawesome-free": "^5.3.1",
"@types/request": "^2.48.1",
"app-root-path": "^2.0.1",
"dialogs": "^1.1.20",
"domain-name-parser": "^2.3.0",
Expand All @@ -99,7 +100,9 @@
"ini": "^1.3.5",
"install": "^0.10.2",
"jquery": "^3.2.1",
"node-forge": "^0.7.6",
"npm": "^5.10.0",
"request": "^2.88.0",
"slash": "^2.0.0",
"static-eval": ">=2.0.0",
"text-ellipsis": "^1.0.3",
Expand Down
12 changes: 12 additions & 0 deletions src/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,18 @@ export async function mostRecentFileTime(start: string): Promise<number>
return lastModifiedTime;
}

export function makeDirectories(path: string): Promise<boolean>
{
return new Promise<boolean>((resolve, reject) =>
{
fs.mkdirs(path, (err) =>
{
resolve(err == null);
})
});
}


export function makeDirectory(path: string): Promise<boolean>
{
return new Promise<boolean>((resolve, reject) =>
Expand Down
31 changes: 31 additions & 0 deletions src/puppet/cert.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

export const CERTIFICATE_EXTENSIONS: {[key: string]: string} = {
"1.3.6.1.4.1.34380.1.1.1": "pp_uuid",
"1.3.6.1.4.1.34380.1.1.2": "pp_instance_id",
"1.3.6.1.4.1.34380.1.1.3": "pp_image_name",
"1.3.6.1.4.1.34380.1.1.4": "pp_preshared_key",
"1.3.6.1.4.1.34380.1.1.5": "pp_cost_center",
"1.3.6.1.4.1.34380.1.1.6": "pp_product",
"1.3.6.1.4.1.34380.1.1.7": "pp_project",
"1.3.6.1.4.1.34380.1.1.8": "pp_application",
"1.3.6.1.4.1.34380.1.1.9": "pp_service",
"1.3.6.1.4.1.34380.1.1.10": "pp_employee",
"1.3.6.1.4.1.34380.1.1.11": "pp_created_by",
"1.3.6.1.4.1.34380.1.1.12": "pp_environment",
"1.3.6.1.4.1.34380.1.1.13": "pp_role",
"1.3.6.1.4.1.34380.1.1.14": "pp_software_version",
"1.3.6.1.4.1.34380.1.1.15": "pp_department",
"1.3.6.1.4.1.34380.1.1.16": "pp_cluster",
"1.3.6.1.4.1.34380.1.1.17": "pp_provisioner",
"1.3.6.1.4.1.34380.1.1.18": "pp_region",
"1.3.6.1.4.1.34380.1.1.19": "pp_datacenter",
"1.3.6.1.4.1.34380.1.1.20": "pp_zone",
"1.3.6.1.4.1.34380.1.1.21": "pp_network",
"1.3.6.1.4.1.34380.1.1.22": "pp_securitypolicy",
"1.3.6.1.4.1.34380.1.1.23": "pp_cloudplatform",
"1.3.6.1.4.1.34380.1.1.24": "pp_apptier",
"1.3.6.1.4.1.34380.1.1.25": "pp_hostname"
};

export const HIERA_EDITOR_FIELD = "1.3.6.1.4.1.34380.1.1.7";
export const HIERA_EDITOR_VALUE = "hiera_editor";
97 changes: 96 additions & 1 deletion src/puppet/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import { PuppetHTTP } from "./http"
import { isArray } from "util";
import { WorkspaceSettings } from "./workspace_settings";
import { EnvironmentTreeDump, NodeDump } from "../ipc/objects"
import { HIERA_EDITOR_FIELD, HIERA_EDITOR_VALUE } from "./cert"

const forge = require("node-forge");
const PromisePool = require('es6-promise-pool');

export class Environment
Expand All @@ -33,6 +35,7 @@ export class Environment
private readonly _warnings: WorkspaceError[];
private readonly _offline: boolean;
private readonly _nodeFacts: Dictionary<string, any>;
private readonly _certificates: Dictionary<string, any>;
private _modulesInfo: PuppetModulesInfo;

constructor(workspace: Workspace, name: string, _path: string, cachePath: string, offline: boolean = false)
Expand All @@ -48,6 +51,7 @@ export class Environment
this._global.put("environment", name);
this._nodes = new Dictionary();
this._nodeFacts = new Dictionary();
this._certificates = new Dictionary();
this._offline = offline;
}

Expand Down Expand Up @@ -96,6 +100,11 @@ export class Environment
return path.join(this._cachePath, "facts", certName + ".json")
}

public get certificateCachePath(): string
{
return path.join(this._cachePath, "certs")
}

public get certListCachePath(): string
{
return path.join(this._cachePath, "certnames.json")
Expand Down Expand Up @@ -436,6 +445,69 @@ export class Environment

}

public getCertificate(certname: string): any
{
return this._certificates.get(certname);
}

private async loadCertificates(updateProgressCategory: any, certList: string[], settings: WorkspaceSettings)
{

if (!await async.isDirectory(this.certificateCachePath))
{
await async.makeDirectory(this.certificateCachePath);
}

const certsExist = [];

for (const certname of certList)
{
certsExist.push(async.isFile(path.join(this.certificateCachePath, certname + ".txt")));
}

const ex = await Promise.all(certsExist.map(p => p.catch(() => undefined)));
const loadCerts = [];

for (let i = 0, t = certList.length; i < t; i++)
{
const certname = certList[i];
const exists = ex[i];

if (exists)
{
loadCerts.push(async.readFile(path.join(this.certificateCachePath, certname + ".txt")));
}
else
{
loadCerts.push(Promise.resolve(null));
}
}

const certs = await Promise.all(loadCerts.map(p => p.catch((e: any): any => undefined)));

for (let i = 0, t = certList.length; i < t; i++)
{
const certname = certList[i];
let certificate = certs[i];

if (certificate == null)
{
if (updateProgressCategory) updateProgressCategory(
"[" + this.name + "] Downloading certificate for node " + certname + "...", false);

certificate = await PuppetHTTP.GetCertificate(certname, this.name, settings);
await async.writeFile(path.join(this.certificateCachePath, certname + ".txt"), certificate);
}

const cert = forge.pki.certificateFromPem(certificate);

if (cert == null)
continue;

this._certificates.put(certname, cert);
}
}

private async updateCertList(updateProgressCategory: any, settings: WorkspaceSettings): Promise<string[]>
{
if (updateProgressCategory) updateProgressCategory("[" + this.name + "] Updating certificate list...", false);
Expand All @@ -451,12 +523,16 @@ export class Environment
throw new WorkspaceError("Failed to obtain certificate list", e.toString());
}

await this.loadCertificates(updateProgressCategory, certList, settings);

await async.writeJSON(this.certListCachePath, certList);
return certList;
}

public async init(progressCallback: any = null, updateProgressCategory: any = null): Promise<any>
{
const zis = this;

this._warnings.length = 0;

if (!await async.isDirectory(this.cachePath))
Expand Down Expand Up @@ -519,7 +595,26 @@ export class Environment
}

const nodeIgnoreList = this.workspace.getNodeIgnoreList();
nodeList = nodeList.filter((value: string) => nodeIgnoreList.indexOf(value) < 0);
nodeList = nodeList.filter((certname: string) =>
{
if (nodeIgnoreList.indexOf(certname) >= 0)
return false;

const cert = zis._certificates.get(certname);
if (cert != null)
{
const ext = cert.getExtension({id: HIERA_EDITOR_FIELD});
if (ext != null)
{
// cut the fist two characters
// https://puppet.com/docs/puppet/6.0/ssl_attributes_extensions.html#manually-checking-for-extensions-in-csrs-and-certificates
if (ext.value.substr(2) == HIERA_EDITOR_VALUE)
return false;
}
}

return true;
});
this._nodeFacts.clear();

if (!this._offline)
Expand Down
34 changes: 8 additions & 26 deletions src/puppet/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class Folder
return await dir.createFile(localPath);
}

const entryPath = path.join(this._path, File.FilePath(name));
const entryPath = path.join(this._path, name);
const node = await this.acquireFile(this._env, name, entryPath, slash(path.join(this._localPath, name)));

if (node == null)
Expand Down Expand Up @@ -120,7 +120,7 @@ export class Folder
if (node == null)
return false;

const entryPath = path.join(this._path, File.FilePath(name));
const entryPath = path.join(this._path, name);

if (!await async.remove(entryPath))
{
Expand Down Expand Up @@ -154,7 +154,7 @@ export class Folder

const nodes: any = [];

for (const node of await this.getNodes())
for (const node of await this.getFiles())
{
nodes.push({
"name": node.name,
Expand Down Expand Up @@ -197,7 +197,7 @@ export class Folder

public async getFile(name: string): Promise<File>
{
const entryPath = path.join(this._path, File.FilePath(name));
const entryPath = path.join(this._path, name);

if (!await async.isFile(entryPath))
{
Expand Down Expand Up @@ -247,7 +247,7 @@ export class Folder
return result;
}

public async getNodes(): Promise<Array<File>>
public async getFiles(): Promise<Array<File>>
{
if (!await async.fileExists(this._path))
{
Expand All @@ -261,19 +261,14 @@ export class Folder

const result:Array<File> = [];

for (const entry of await async.listFiles(this._path))
for (const fileName of await async.listFiles(this._path))
{
const nodeName = File.ValidatePath(entry);

if (nodeName == null)
continue;

const entryPath = path.join(this._path, entry);
const entryPath = path.join(this._path, fileName);

if (await async.isFile(entryPath))
{
result.push(await this.acquireFile(
this._env, nodeName, entryPath, slash(path.join(this._localPath, nodeName))));
this._env, fileName, entryPath, slash(path.join(this._localPath, fileName))));
}
}

Expand Down Expand Up @@ -315,19 +310,6 @@ export class File
this._parent = parent;
}

static FilePath(name: string): string
{
return name + ".yaml";
}

static ValidatePath(pathName: string): string
{
if (!pathName.endsWith(".yaml"))
return null;

return pathName.substr(0, pathName.length - 5);
}

public async remove(): Promise<boolean>
{
if (this._parent == null)
Expand Down
9 changes: 5 additions & 4 deletions src/puppet/hiera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ export class Hierarchy

this._datadir = "data";
this._hierarchy = [
new HierarchyEntry("nodes/%{::trusted.certname}"),
new HierarchyEntry("common")
new HierarchyEntry("nodes/%{::trusted.certname}.yaml"),
new HierarchyEntry("common.yaml")
];
}

Expand All @@ -188,7 +188,7 @@ export class Hierarchy
{
if (value == null)
return "";
const key = path.pop();
const key = path.shift();
value = value[key];
}

Expand All @@ -212,7 +212,8 @@ export class Hierarchy

try
{
data = await async.readYAML(this._path);
const document = await async.readYAML(this._path);
data = document.toJSON();
}
catch (e)
{
Expand Down
Loading

0 comments on commit 0744cc6

Please sign in to comment.