Skip to content

Commit

Permalink
More sentinel stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
mikenomitch authored and philrenaud committed May 7, 2024
1 parent 2066e6c commit 7cf5ad8
Show file tree
Hide file tree
Showing 11 changed files with 226 additions and 103 deletions.
6 changes: 3 additions & 3 deletions ui/app/adapters/sentinel-policy.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ export default class SentinelPolicyAdapter extends ApplicationAdapter {
return '/v1/sentinel/policy/' + id;
}

testAgainstJob(sentinelPolicy, jobspec) {
testAgainstJob(sentinelPolicy, job) {
const url = '/v1/sentinel/test-policy';

return this.ajax(url, 'POST', {
data: {
policy: sentinelPolicy.policy,
jobspec,
Job: job.get('_newDefinitionJSON'),
Policy: sentinelPolicy.policy,
},
});
}
Expand Down
107 changes: 87 additions & 20 deletions ui/app/components/sentinel-policy-editor.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,50 @@

{{#if this.devMode}}
{{#if selectedJobspec}}
<div class="section">
<button
class="button is-light is-compact"
type="button"
{{on "click" (perform this.testIt)}}
data-test-edit-job
>
Run Policy
</button>
<button
class="button is-light is-compact"
type="button"
{{on "click" this.exitDevMode}}
data-test-edit-job
>
Finished Developing
</button>
</div>

{{#if (eq testResult "Passed")}}
<div>
<Hds::Alert @type="inline" @color="success" @icon="info" class="related-entities notification" as |A|>
<A.Title>Policy Check Passed</A.Title>
<A.Description>
This policy check successfully passed.
</A.Description>
</Hds::Alert>
</div>
{{/if}}

{{#if (eq testResult "Failed")}}
<div>
<Hds::Alert @type="inline" @color="critical" @icon="info" class="related-entities notification" as |A|>
<A.Title>Policy Check failed</A.Title>
<A.Description>
This Policy check failed.
</A.Description>
</Hds::Alert>
</div>
{{/if}}

<div class="boxed-section">
<div class="boxed-section-head">
Edit Job Spec
Job to test against
</div>
<div class="boxed-section-body is-full-bleed">
<div
Expand All @@ -19,7 +60,7 @@
theme="hashi"
mode="ruby"
content=this.selectedJobspec
onUpdate=this.updatedSelectedJobspec
onUpdate=this.updateSelectedJobspec
autofocus=false
extraKeys=(hash Cmd-Enter=this.save)
}} />
Expand All @@ -30,6 +71,7 @@
Select a Job Spec
</h2>

<Hds::ButtonSet class="button-group">
{{#each this.jobs as |job|}}
<Hds::Button
@text={{job.name}}
Expand All @@ -38,11 +80,12 @@
{{on "click" (perform this.selectJob job.name)}}
/>
{{/each}}
</Hds::ButtonSet>
{{/if}}

<div class="boxed-section">
<div class="boxed-section-head">
Edit Policy
Policy Definition
</div>
<div class="boxed-section-body is-full-bleed">
<div
Expand All @@ -61,23 +104,47 @@
</div>
</div>

<button
class="button is-light is-compact"
type="button"
{{on "click" this.testIt}}
data-test-edit-job
>
Test it!
</button>
<div class="section">
<button
class="button is-light is-compact"
type="button"
{{on "click" (perform this.testIt)}}
data-test-edit-job
>
Run Policy
</button>

<button
class="button is-light is-compact"
type="button"
{{on "click" this.exitDevMode}}
data-test-edit-job
>
Finished Developing
</button>
<button
class="button is-light is-compact"
type="button"
{{on "click" this.exitDevMode}}
data-test-edit-job
>
Finished Developing
</button>
</div>

{{#if (eq testResult "Passed")}}
<div>
<Hds::Alert @type="inline" @color="success" @icon="info" class="related-entities notification" as |A|>
<A.Title>Policy Check Passed</A.Title>
<A.Description>
This policy check successfully passed.
</A.Description>
</Hds::Alert>
</div>
{{/if}}

{{#if (eq testResult "Failed")}}
<div>
<Hds::Alert @type="inline" @color="critical" @icon="info" class="related-entities notification" as |A|>
<A.Title>Policy Check failed</A.Title>
<A.Description>
This Policy check failed.
</A.Description>
</Hds::Alert>
</div>
{{/if}}
{{else}}
<form class="acl-form" autocomplete="off" {{on "submit" this.save}}>
{{#if @policy.isNew }}
Expand All @@ -102,7 +169,7 @@
{{on "click" this.enterDevMode}}
data-test-edit-job
>
Test against a job spec
Develop with a job spec
</button>
</div>
</div>
Expand Down
117 changes: 70 additions & 47 deletions ui/app/components/sentinel-policy-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,47 +17,41 @@ export default class SentinelPolicyEditorComponent extends Component {
@service store;
@tracked devMode = null;
@tracked jobs = null;
@tracked selectedJobspec = `job "hello-world" {
meta {
foo = "bar"
}
group "servers" {
count = 1
network {
port "www" {
to = 8001
}
}
# Tasks are individual units of work that are run by Nomad.
task "web" {
# This particular task starts a simple web server within a Docker container
driver = "docker"
config {
image = "busybox:1"
command = "httpd"
args = ["-v", "-f", "-p", "\${NOMAD_PORT_www}", "-h", "/local"]
ports = ["www"]
}
template {
data = <<-EOF
<h1>Hello, Nomad!</h1>
EOF
destination = "local/index.html"
}
# Specify the maximum resources required to run the task
resources {
cpu = 50
memory = 64
}
}
}
}
`;
@tracked testResult = null;
@tracked selectedJobspec = '';
// @tracked selectedJobspec = `job "hello-world" {
// group "servers" {
// count = 1
// network {
// port "www" {
// to = 8001
// }
// }
// task "web" {
// config {
// image = "busybox:1"
// command = "httpd"
// args = ["-v", "-f", "-p", "\${NOMAD_PORT_www}", "-h", "/local"]
// ports = ["www"]
// }
// template {
// data = <<-EOF
// <h1>Hello, Nomad!</h1>
// EOF
// destination = "local/index.html"
// }
// resources {
// cpu = 50
// memory = 64
// }
// }
// }
// }
// `;
@alias('args.policy') policy;

Expand All @@ -73,7 +67,7 @@ export default class SentinelPolicyEditorComponent extends Component {
this.policy.set('enforcementLevel', id);
}

@action updatedSelectedJobspec({ target: { value } }) {
@action updateSelectedJobspec(value) {
this.selectedJobspec = value;
}

Expand All @@ -84,21 +78,54 @@ export default class SentinelPolicyEditorComponent extends Component {
}

@action exitDevMode() {
this.testResult = null;
this.selectedJobspec = '';
this.devMode = false;
}

@action async getJobspecOptions() {
return this.store.peekAll('submission');
}

/**
* A task that performs the job parsing and planning.
* On error, it calls the onError method.
*/
@(task(function* () {
this.testResult = null;

let job = this.store.createRecord('job', {
_newDefinition: this.selectedJobspec,
});

try {
yield job.parse();
} catch (err) {
this.onError(err, 'parse', 'parse jobs');
return;
}

let res = yield this.policy.testAgainstJob(job);

if (res.Passed) {
this.testResult = 'Passed';
} else {
this.testResult = 'Failed';
this.testMessage = res.Message;
}

console.log('res: ', res);
}).drop())
testIt;

@task(function* (arg) {
// TODO: This only works on default
const fullId = JSON.stringify([arg, 'default']);
let job = yield this.store.findRecord('job', fullId, { reload: true });
console.log('job name', job.name);
const spec = yield job.fetchRawSpecification();
console.log('spec', spec);
this.selectedJobspec = spec;
this.selectedJobspec = spec.Source;
yield true;
})
selectJob;
Expand All @@ -110,10 +137,6 @@ export default class SentinelPolicyEditorComponent extends Component {
});
}

@action async testIt() {
await this.policy.testAgainstJob(this.selectedJobspec);
}

@action async save(e) {
if (e instanceof Event) {
e.preventDefault(); // code-mirror "command+enter" submits the form, but doesnt have a preventDefault()
Expand Down
2 changes: 1 addition & 1 deletion ui/app/components/variable-form/related-entities.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
job <Hds::Link::Inline @route="jobs.job" @model={{concat @job "@" @namespace}} @icon="external-link">{{@job}}</Hds::Link::Inline>
{{else}}
all nomad jobs in this namespace
{{/if}}
{{/if}}
</A.Description>
</Hds::Alert>
6 changes: 2 additions & 4 deletions ui/app/models/sentinel-policy.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ export default class SentinelPolicy extends Model {
@attr('number') createIndex;
@attr('number') modifyIndex;

testAgainstJob(specification) {
return this.store
.adapterFor('sentinel-policy')
.testAgainstJob(this, specification);
testAgainstJob(job) {
return this.store.adapterFor('sentinel-policy').testAgainstJob(this, job);
}
}
10 changes: 8 additions & 2 deletions ui/app/routes/sentinel-policies/new.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,23 @@ export default class NewRoute extends Route {
};

model({ template }) {
let policy = 'main = rule { false }\n';
let policy = '#I always pass\nmain = rule { true }\n';
let name = '';
let description = '';

if (template) {
let matchingTemplate = TEMPLATES.find((t) => t.name == template);
if (matchingTemplate) {
policy = matchingTemplate.policy;
name = matchingTemplate.name;
description = matchingTemplate.description;
}
}

return this.store.createRecord('sentinel-policy', {
name: '',
name,
policy,
description,
enforcementLevel: 'advisory',
scope: 'submit-job',
});
Expand Down
7 changes: 7 additions & 0 deletions ui/app/utils/default-sentinel-policy-templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import vaultSecretsPolicy from './sentinel_policy_templates/vault-secrets-only';
import dyanmicPortsPolicy from './sentinel_policy_templates/dynamic-ports-only';
import resourceLimitsPolicy from './sentinel_policy_templates/resource-limits';
import constraintEnforcmentPolicy from './sentinel_policy_templates/constraint-enforcement';
import restictImagesPolicy from './sentinel_policy_templates/restrict-images';

export default [
{
Expand Down Expand Up @@ -91,4 +92,10 @@ export default [
description: 'Requires a constraint on client metadata',
policy: constraintEnforcmentPolicy,
},
{
displayName: 'Restrict Images',
name: 'restrict-images',
description: 'Allows only certain Docker images and disables "latest" tags',
policy: restictImagesPolicy,
},
];
Loading

0 comments on commit 7cf5ad8

Please sign in to comment.