Midsummer site refresh
@@ -1,12 +0,0 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>{% if page.title %}{{ page.title | escape }}{% else %}{{ site.title | escape }}{% endif %}</title>
|
||||
<meta name="description" content="{% if page.excerpt %}{{ page.excerpt | strip_html | strip_newlines | truncate: 160 }}{% else %}{{ site.description }}{% endif %}">
|
||||
|
||||
<link rel="stylesheet" href="{{ "/css/main.css" | prepend: site.baseurl }}">
|
||||
<link rel="canonical" href="{{ page.url | replace:'index.html','' | prepend: site.baseurl | prepend: site.url }}">
|
||||
<link rel="alternate" type="application/rss+xml" title="{{ site.title }}" href="{{ "/feed.xml" | prepend: site.baseurl | prepend: site.url }}">
|
||||
</head>
|
||||
@@ -1 +0,0 @@
|
||||
<a href="https://github.com/{{ include.username }}/{{ include.repo }}"><span class="icon icon--github">{% include icon-github.svg %}</span><span class="username">{{include.username}}/{{ include.repo }}</span></a>
|
||||
@@ -1 +0,0 @@
|
||||
<svg viewBox="0 0 16 16"><path fill="#828282" d="M7.999,0.431c-4.285,0-7.76,3.474-7.76,7.761 c0,3.428,2.223,6.337,5.307,7.363c0.388,0.071,0.53-0.168,0.53-0.374c0-0.184-0.007-0.672-0.01-1.32 c-2.159,0.469-2.614-1.04-2.614-1.04c-0.353-0.896-0.862-1.135-0.862-1.135c-0.705-0.481,0.053-0.472,0.053-0.472 c0.779,0.055,1.189,0.8,1.189,0.8c0.692,1.186,1.816,0.843,2.258,0.645c0.071-0.502,0.271-0.843,0.493-1.037 C4.86,11.425,3.049,10.76,3.049,7.786c0-0.847,0.302-1.54,0.799-2.082C3.768,5.507,3.501,4.718,3.924,3.65 c0,0,0.652-0.209,2.134,0.796C6.677,4.273,7.34,4.187,8,4.184c0.659,0.003,1.323,0.089,1.943,0.261 c1.482-1.004,2.132-0.796,2.132-0.796c0.423,1.068,0.157,1.857,0.077,2.054c0.497,0.542,0.798,1.235,0.798,2.082 c0,2.981-1.814,3.637-3.543,3.829c0.279,0.24,0.527,0.713,0.527,1.437c0,1.037-0.01,1.874-0.01,2.129 c0,0.208,0.14,0.449,0.534,0.373c3.081-1.028,5.302-3.935,5.302-7.362C15.76,3.906,12.285,0.431,7.999,0.431z"/></svg>
|
||||
|
Before Width: | Height: | Size: 926 B |
@@ -1,46 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
---
|
||||
<article class="docs">
|
||||
|
||||
<div class="docs-nav">
|
||||
<ul>
|
||||
{% assign pages = site.pages | sort:"sort_order" %}
|
||||
{% for page in pages %}
|
||||
{% if page.section == 'docs' %}
|
||||
<li>
|
||||
<a class="page-link" href="{{ page.url | prepend: site.baseurl }}">{{page.title}}</a>
|
||||
</li>
|
||||
<br>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<li>References</li>
|
||||
<ul>
|
||||
{% assign pages = site.pages | sort:"sort_order" %}
|
||||
{% for page in pages %}
|
||||
{% if page.section == 'references' %}
|
||||
<li>
|
||||
<a class="page-link" href="{{ page.url | prepend: site.baseurl }}">{{page.title}}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<li>Examples</li>
|
||||
<ul>
|
||||
{% assign pages = site.pages | sort:"sort_order" %}
|
||||
{% for page in pages %}
|
||||
{% if page.section == 'examples' %}
|
||||
<li>
|
||||
<a class="page-link" href="{{ page.url | prepend: site.baseurl }}">{{page.title}}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="docs-content">
|
||||
{{ content }}
|
||||
</div>
|
||||
|
||||
</article>
|
||||
@@ -1,206 +0,0 @@
|
||||
/**
|
||||
* Reset some basic elements
|
||||
*/
|
||||
body, h1, h2, h3, h4, h5, h6,
|
||||
p, blockquote, pre, hr,
|
||||
dl, dd, ol, ul, figure {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Basic styling
|
||||
*/
|
||||
body {
|
||||
font: $base-font-weight #{$base-font-size}/#{$base-line-height} $base-font-family;
|
||||
color: $text-color;
|
||||
background-color: $background-color;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-font-feature-settings: "kern" 1;
|
||||
-moz-font-feature-settings: "kern" 1;
|
||||
-o-font-feature-settings: "kern" 1;
|
||||
font-feature-settings: "kern" 1;
|
||||
font-kerning: normal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set `margin-bottom` to maintain vertical rhythm
|
||||
*/
|
||||
h1, h2, h3, h4, h5, h6,
|
||||
p, blockquote, pre,
|
||||
ul, ol, dl, figure,
|
||||
%vertical-rhythm {
|
||||
margin-bottom: $spacing-unit / 2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Images
|
||||
*/
|
||||
img {
|
||||
max-width: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Figures
|
||||
*/
|
||||
figure > img {
|
||||
display: block;
|
||||
}
|
||||
|
||||
figcaption {
|
||||
font-size: $small-font-size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Lists
|
||||
*/
|
||||
ul, ol {
|
||||
margin-left: $spacing-unit;
|
||||
}
|
||||
|
||||
li {
|
||||
> ul,
|
||||
> ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Headings
|
||||
*/
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-weight: $base-font-weight;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Links
|
||||
*/
|
||||
a {
|
||||
color: $brand-color;
|
||||
text-decoration: none;
|
||||
|
||||
&:visited {
|
||||
color: darken($brand-color, 15%);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $text-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Blockquotes
|
||||
*/
|
||||
blockquote {
|
||||
color: $grey-color;
|
||||
border-left: 4px solid $grey-color-light;
|
||||
padding-left: $spacing-unit / 2;
|
||||
font-size: 18px;
|
||||
letter-spacing: -1px;
|
||||
font-style: italic;
|
||||
|
||||
> :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Code formatting
|
||||
*/
|
||||
pre,
|
||||
code {
|
||||
font-size: 15px;
|
||||
border: 1px solid $grey-color-light;
|
||||
border-radius: 3px;
|
||||
background-color: #eef;
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 1px 5px;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 8px 12px;
|
||||
overflow-x: auto;
|
||||
|
||||
> code {
|
||||
border: 0;
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper
|
||||
*/
|
||||
.wrapper {
|
||||
max-width: -webkit-calc(#{$content-width} - (#{$spacing-unit} * 2));
|
||||
max-width: calc(#{$content-width} - (#{$spacing-unit} * 2));
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding-right: $spacing-unit;
|
||||
padding-left: $spacing-unit;
|
||||
@extend %clearfix;
|
||||
|
||||
@include media-query($on-laptop) {
|
||||
max-width: -webkit-calc(#{$content-width} - (#{$spacing-unit}));
|
||||
max-width: calc(#{$content-width} - (#{$spacing-unit}));
|
||||
padding-right: $spacing-unit / 2;
|
||||
padding-left: $spacing-unit / 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Clearfix
|
||||
*/
|
||||
%clearfix {
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Icons
|
||||
*/
|
||||
.icon {
|
||||
|
||||
> svg {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
|
||||
path {
|
||||
fill: $grey-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
/**
|
||||
* Site header
|
||||
*/
|
||||
.site-header {
|
||||
border-top: 5px solid $grey-color-dark;
|
||||
border-bottom: 1px solid $grey-color-light;
|
||||
min-height: 56px;
|
||||
|
||||
// Positioning context for the mobile navigation icon
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.site-title {
|
||||
font-size: 26px;
|
||||
font-weight: 300;
|
||||
line-height: 56px;
|
||||
letter-spacing: -1px;
|
||||
margin-bottom: 0;
|
||||
float: left;
|
||||
|
||||
&,
|
||||
&:visited {
|
||||
color: $grey-color-dark;
|
||||
}
|
||||
}
|
||||
|
||||
.site-nav {
|
||||
float: right;
|
||||
line-height: 56px;
|
||||
|
||||
.menu-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.page-link {
|
||||
color: $text-color;
|
||||
line-height: $base-line-height;
|
||||
|
||||
// Gaps between nav items, but not on the last one
|
||||
&:not(:last-child) {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-query($on-palm) {
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
right: $spacing-unit / 2;
|
||||
background-color: $background-color;
|
||||
border: 1px solid $grey-color-light;
|
||||
border-radius: 5px;
|
||||
text-align: right;
|
||||
|
||||
.menu-icon {
|
||||
display: block;
|
||||
float: right;
|
||||
width: 36px;
|
||||
height: 26px;
|
||||
line-height: 0;
|
||||
padding-top: 10px;
|
||||
text-align: center;
|
||||
|
||||
> svg {
|
||||
width: 18px;
|
||||
height: 15px;
|
||||
|
||||
path {
|
||||
fill: $grey-color-dark;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trigger {
|
||||
clear: both;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover .trigger {
|
||||
display: block;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.page-link {
|
||||
display: block;
|
||||
padding: 5px 10px;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 0;
|
||||
}
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Site footer
|
||||
*/
|
||||
.site-footer {
|
||||
border-top: 1px solid $grey-color-light;
|
||||
padding: $spacing-unit 0;
|
||||
}
|
||||
|
||||
.footer-heading {
|
||||
font-size: 18px;
|
||||
margin-bottom: $spacing-unit / 2;
|
||||
}
|
||||
|
||||
.contact-list,
|
||||
.social-media-list {
|
||||
list-style: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.footer-col-wrapper {
|
||||
font-size: 15px;
|
||||
color: $grey-color;
|
||||
margin-left: -$spacing-unit / 2;
|
||||
@extend %clearfix;
|
||||
}
|
||||
|
||||
.footer-col {
|
||||
float: left;
|
||||
margin-bottom: $spacing-unit / 2;
|
||||
padding-left: $spacing-unit / 2;
|
||||
}
|
||||
|
||||
.footer-col-1 {
|
||||
width: -webkit-calc(35% - (#{$spacing-unit} / 2));
|
||||
width: calc(35% - (#{$spacing-unit} / 2));
|
||||
}
|
||||
|
||||
.footer-col-2 {
|
||||
width: -webkit-calc(65% - (#{$spacing-unit} / 2));
|
||||
width: calc(65% - (#{$spacing-unit} / 2));
|
||||
}
|
||||
|
||||
// .footer-col-3 {
|
||||
// width: -webkit-calc(45% - (#{$spacing-unit} / 2));
|
||||
// width: calc(45% - (#{$spacing-unit} / 2));
|
||||
// }
|
||||
|
||||
@include media-query($on-laptop) {
|
||||
.footer-col-1,
|
||||
.footer-col-2 {
|
||||
width: -webkit-calc(100% - (#{$spacing-unit} / 2));
|
||||
width: calc(100% - (#{$spacing-unit} / 2));
|
||||
}
|
||||
|
||||
// .footer-col-3 {
|
||||
// width: -webkit-calc(100% - (#{$spacing-unit} / 2));
|
||||
// width: calc(100% - (#{$spacing-unit} / 2));
|
||||
// }
|
||||
}
|
||||
|
||||
@include media-query($on-palm) {
|
||||
.footer-col {
|
||||
float: none;
|
||||
width: -webkit-calc(100% - (#{$spacing-unit} / 2));
|
||||
width: calc(100% - (#{$spacing-unit} / 2));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Page content
|
||||
*/
|
||||
|
||||
.page-content {
|
||||
padding: $spacing-unit 0;
|
||||
}
|
||||
|
||||
.page-heading {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.post-list {
|
||||
margin-left: 0;
|
||||
list-style: none;
|
||||
|
||||
> li {
|
||||
margin-bottom: $spacing-unit;
|
||||
}
|
||||
}
|
||||
|
||||
.post-meta {
|
||||
font-size: $small-font-size;
|
||||
color: $grey-color;
|
||||
}
|
||||
|
||||
.post-link {
|
||||
display: block;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Features section in index.html.
|
||||
*/
|
||||
|
||||
.col {
|
||||
float: left;
|
||||
width: 30%;
|
||||
padding: 1%;
|
||||
}
|
||||
|
||||
.features-grid:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/**
|
||||
* Docs pages, i.e., everything under /docs.
|
||||
*/
|
||||
.docs-nav {
|
||||
float: left;
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.docs-content {
|
||||
float: left;
|
||||
width: 75%;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: About
|
||||
header_title: About
|
||||
show_in_header: true
|
||||
---
|
||||
|
||||
About
|
||||
=====
|
||||
|
||||
The Open Policy Agent (OPA) project is under active development.
|
||||
|
||||
For information on contributing to OPA, see the [CONTRIBUTING.md](https://github.com/open-policy-agent/opa/blob/master/CONTRIBUTING.md) file in the repository.
|
||||
|
||||
Contact
|
||||
-------
|
||||
|
||||
- Bugs: [Github Issues](https://github.com/open-policy-agent/opa/issues)
|
||||
- Features: [Github Issues](https://github.com/open-policy-agent/opa/issues)
|
||||
- Discussions: [Google Groups](https://groups.google.com/forum/?hl=en#!forum/open-policy-agent)
|
||||
|
||||
Project Information
|
||||
-------------------
|
||||
|
||||
- License: [Apache Version 2.0](https://raw.githubusercontent.com/open-policy-agent/opa/master/LICENSE)
|
||||
- Continuous Integration: [](https://travis-ci.org/open-policy-agent/opa)
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -1,53 +0,0 @@
|
||||
---
|
||||
# Only the main Sass file needs front matter (the dashes are enough)
|
||||
---
|
||||
@charset "utf-8";
|
||||
|
||||
|
||||
|
||||
// Our variables
|
||||
$base-font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
$base-font-size: 16px;
|
||||
$base-font-weight: 400;
|
||||
$small-font-size: $base-font-size * 0.875;
|
||||
$base-line-height: 1.5;
|
||||
|
||||
$spacing-unit: 30px;
|
||||
|
||||
$text-color: #111;
|
||||
$background-color: #fdfdfd;
|
||||
$brand-color: #2a7ae2;
|
||||
|
||||
$grey-color: #828282;
|
||||
$grey-color-light: lighten($grey-color, 40%);
|
||||
$grey-color-dark: darken($grey-color, 25%);
|
||||
|
||||
// Width of the content area
|
||||
$content-width: 800px;
|
||||
|
||||
$on-palm: 600px;
|
||||
$on-laptop: 800px;
|
||||
|
||||
|
||||
|
||||
// Use media queries like this:
|
||||
// @include media-query($on-palm) {
|
||||
// .wrapper {
|
||||
// padding-right: $spacing-unit / 2;
|
||||
// padding-left: $spacing-unit / 2;
|
||||
// }
|
||||
// }
|
||||
@mixin media-query($device) {
|
||||
@media screen and (max-width: $device) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Import partials from `sass_dir` (defaults to `_sass`)
|
||||
@import
|
||||
"base",
|
||||
"layout",
|
||||
"syntax-highlighting"
|
||||
;
|
||||
@@ -1,2 +0,0 @@
|
||||
This directory contains internal documentation that is not included
|
||||
on the site.
|
||||
@@ -1,297 +0,0 @@
|
||||
---
|
||||
layout: docs
|
||||
title: Architecture
|
||||
section: references
|
||||
sort_order: 2
|
||||
---
|
||||
|
||||
Architecture
|
||||
------------
|
||||
|
||||
OPA provides an open source policy engine that can be used to policy enable applications. Policy enabling applications decouples the policy that governs how applications behave from the implementation of those applications. This allows administrators to create and manage policy without requiring changes to the application. This eases some of the challenges of maintaining applications in complex modern deployment environments.
|
||||
|
||||
Because policies are defined declaratively and outside the application, administrators and other developers can more readily understand the policies that govern how the application must behave and are empowered to safely and efficiently make changes to policy.
|
||||
|
||||
OPA is purpose built for modern deployment environments and can be integrated with various types of applications to provide rich policy control over various functions such as:
|
||||
|
||||
- API authorization
|
||||
- VM and container placement
|
||||
- Auto-scaling and auto-healing clusters
|
||||
- CI/CD pipeline gating
|
||||
- Network ACLs
|
||||
- Feature flags
|
||||
|
||||
This document describes the main concepts in OPA. After reading this document you should have a better idea of how OPA is deployed and interacted with by applications and users.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Before diving into the concepts behind OPA, it is useful to review the requirements placed on a policy engine to successfully policy enable applications.
|
||||
|
||||
1. **The policy definitions must be decoupled from the application's implementation.**
|
||||
|
||||
Policy authors must be empowered to define and manage policy without requiring changes to the application which necessitate costly development, testing, and deployment cycles.
|
||||
|
||||
1. **The policy definitions must be accessible to administrators and other developers.**
|
||||
|
||||
Policy must be readily understood by administrators and other developers. In some cases, policy definitions also need to be understood by non-technical team members. Furthermore, administrators and other developers must be empowered to safely modify policy.
|
||||
|
||||
1. **The engine must be highly available.**
|
||||
|
||||
Applications must be able to treat policy engine downtime as an extremely rare event (which likely requires manual intervention). The decision about what to do when the policy engine is unavailable is itself a policy question. It would be counter productive to the goals of policy enablement to re-implement policy decisions in the application to deal with policy engine downtime.
|
||||
|
||||
1. **The engine must introduce minimal latency when evaluating policy.**
|
||||
|
||||
Applications must be able to assume that the performance cost of decoupling the policy implementation from the application is negligible. If the performance cost of doing so is too high, applications will not use the engine.
|
||||
|
||||
1. **The engine must integrate with applications written in any programming language.**
|
||||
|
||||
Applications must not have to be developed using a specific programming language in order to benefit from the engine. If the cost of integrating with the engine involves developing the application in a specific programming language, far fewer applications will be able to use the engine.
|
||||
|
||||
1. **The engine must easily ingest the state of world that is relevant to policy.**
|
||||
|
||||
Applications must be able to leverage their respective programming language ecosystems to push data into the policy engine. Furthermore, the process for reading and writing data stored in the policy engine must be as simple as possible.
|
||||
|
||||
These core requirements capture the guiding principles behind the concepts and design of OPA.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Conceptually, OPA is a collection of algorithms that answer questions about policy.
|
||||
<img src="https://cdn.rawgit.com/open-policy-agent/opa/9f5f1e6fa68fd0ee627122b9e5c8809519e5bba8/docs/overview.svg" />
|
||||
|
||||
At a high level, using OPA involves:
|
||||
|
||||
- Deploying OPA on servers alongside applications.
|
||||
- Writing policies a declarative language ("Rego") and loading them into OPA.
|
||||
- Pushing application state that is relevant to policy into OPA.
|
||||
- Executing queries via OPA's APIs to answer questions about operations governed by policy.
|
||||
- Reacting to notifications delivered by OPA when policy is violated.
|
||||
|
||||
Deployment
|
||||
----------
|
||||
|
||||
In order to support arbitrary languages, frameworks and runtimes, OPA is deployed as a host-local daemon ("opad") alongside applications. OPA can be deployed directly on hosts as an operating system daemon or in a container.
|
||||
|
||||
Deploying OPA as a host-local daemon helps ensure low latency transactions and high availability (through fate sharing) of the policy engine. If OPA is deployed on a separate server, significant consideration must be given to ensure that hardware failures and network partitions do not result in downtime of the policy engine.
|
||||
|
||||
When OPA is started for the first time it contains no policies or data. Applications or deployment automation may load policies into OPA at deploy time and update policies during upgrades. Administrators can manually add, remove, and change policies as needed.
|
||||
|
||||
Applications interact with OPA via HTTP.
|
||||
|
||||
<img src="https://cdn.rawgit.com/open-policy-agent/opa/9f5f1e6fa68fd0ee627122b9e5c8809519e5bba8/docs/deployment.svg" />
|
||||
|
||||
APIs
|
||||
----
|
||||
|
||||
OPA exposes its APIs over HTTP and uses JSON as the default interchange format. The APIs are RESTful with the exception of certain streaming operations. HTTP and JSON are used because they are well supported in most application development languages and provide a low barrier to integration.
|
||||
|
||||
### <a name="policy-api"></a>Policy API
|
||||
|
||||
OPA's Policy API exposes CRUD operations on policies within OPA. Applications and administrators can use the Policy API to manage policies.
|
||||
|
||||
For example, the request below creates a policy named "example-policy":
|
||||
|
||||
```
|
||||
PUT /v1/policies/example-policy
|
||||
Content-Type: text/plain
|
||||
|
||||
package opa.examples
|
||||
|
||||
violations[server] :-
|
||||
server = data.servers[_],
|
||||
server.protocols[_] = "http",
|
||||
server.ports[_] = data.ports[i].id,
|
||||
data.ports[i].networks[_] = data.networks[j].id,
|
||||
data.networks[j].public = true
|
||||
```
|
||||
|
||||
Policy names are only used to identify policies for management purposes. Policy names are not used in policy definitions or when issuing requests against the Data API.
|
||||
|
||||
### <a name="data-api"></a>Data API
|
||||
|
||||
In order to evaluate policy and compute violations, OPA needs to have access to the state of the world which is relevant to policy. OPA's Data API deals with data from applications or the environment that is relevant to policy.
|
||||
|
||||
Applications use the Data API to push state relevant to policy into OPA. Applications are responsible for updating the relevant data stored in OPA when state changes.
|
||||
|
||||
For example, an application can push a list of servers into OPA by issuing an HTTP PATCH on /v1/data/servers:
|
||||
|
||||
```
|
||||
PATCH /v1/data/servers HTTP/1.1
|
||||
Content-Type: application/json-patch+json
|
||||
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "-",
|
||||
"value": {"id": "s1", "name": "app", "protocols": ["http", "https", "ssh"], "ports": ["p1", "p2", "p3"]}
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"path": "-",
|
||||
"value": {"id": "s2", "name": "db", "protocols": ["mysql"], "ports": ["p3"]}
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"path": "-",
|
||||
"value": {"id": "s3", "name": "cache", "protocols": ["memcache"], "ports": ["p3"]}
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"path": "-",
|
||||
"value": {"id": "s4", "name": "dev", "protocols": ["http", "https", "ssh"], "ports": ["p1", "p2"]}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Conceptually, all data stored in OPA exists within a single document ("data").
|
||||
|
||||
Applications execute queries when handling operations that are governed by policy. The query response tells the application what to do with the operation, e.g., reject the API call or place the VM on a specific host. Queries are executed via the Data API by performing a GET on the appropriate path. See the [Policies](#policies) section for examples.
|
||||
|
||||
Queries can also be performed against data pushed into OPA. For example, the request below queries for the "servers" added above.
|
||||
|
||||
```
|
||||
GET /v1/data/servers HTTP/1.1
|
||||
```
|
||||
|
||||
The response:
|
||||
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{"id": "s1", "name": "app", "protocols": ["http", "https", "ssh"], "ports": ["p1", "p2", "p3"]},
|
||||
{"id": "s2", "name": "db", "protocols": ["mysql"], "ports": ["p3"]},
|
||||
{"id": "s3", "name": "cache", "protocols": ["memcache"], "ports": ["p3"]},
|
||||
{"id": "s4", "name": "dev", "protocols": ["http"], "ports": ["p1", "p2"]}
|
||||
]
|
||||
```
|
||||
|
||||
### Triggers
|
||||
|
||||
Applications can register for asynchronous notifications when policy is violated. This allows applications to take corrective action when the system exits the expected state.
|
||||
|
||||
For example, to receive notifications when services should be migrated to a new host, the application responsible for service deployment would create a rule which asserts that running services must be placed on a host which is not in the "terminated" state. If a host was shutdown unexpectedly, the system would notify OPA and the host state (stored in OPA) would be updated. OPA will detect that a rule has been violated and deliver a notification to the service deployment application (which can handle the event by redeploying the service on a running host.)
|
||||
|
||||
### Transactions
|
||||
|
||||
OPA exposes transactional APIs to operate on consistent snapshots of data stored in OPA. For example, when deploying a new VM, the application would open a transaction and perform the following steps:
|
||||
|
||||
1. Query OPA for a list of hosts where the VM can be deployed.
|
||||
2. Choose a host from the list and deploy the VM.
|
||||
3. Push new data to OPA to reflect that the VM has been deployed.
|
||||
4. Close the transaction.
|
||||
|
||||
Lastly, policy writers can use OPA's APIs to obtain detailed explanations of query processing to understand why queries are returning specific results. This is useful for debugging queries.
|
||||
|
||||
<a name="data-model"></a>Data Model
|
||||
----------
|
||||
|
||||
OPA is designed to support document-oriented models such as JSON. Documents consist of scalars (i.e., booleans, strings, numbers, and null) and collections (i.e., objects, arrays, and sets). The document model was selected for OPA because of its prevalence in modern application stacks. For example, most applications today expose APIs which produce and consume JSON and many modern applications rely on document-oriented databases or document support in existing relational databases.
|
||||
|
||||
Conceptually, there are two kinds of documents in OPA:
|
||||
|
||||
- **Base documents** representing application state relevant to policy.
|
||||
- **Virtual documents** representing higher level information defined by policies.
|
||||
|
||||
**Base documents** represent the raw information relevant to policy that is pushed by applications integrated with OPA. Base documents are stored in-memory and can be written to disk for resilience.
|
||||
|
||||
**Virtual documents** are defined by rules inside policies. OPA computes the contents of virtual documents when callers execute queries against rules or when dependant documents are modified. Virtual documents are defined in terms of base documents and other virtual documents. Policy authors can use virtual documents to define abstractions which are useful for expressing high level policy and insulating policy from schema changes in low level data.
|
||||
|
||||
<img src="https://cdn.rawgit.com/open-policy-agent/opa/364bc39f3b2ade09eb093c00ee91bc216f2a6342/docs/docs/data-model-dependencies.svg" />
|
||||
|
||||
When defining policies, rules are written which contain expressions that reference documents. The language that rules are written in ("Rego") lets you reference base documents and virtual documents in exactly the same way.
|
||||
|
||||
<img src="https://cdn.rawgit.com/open-policy-agent/opa/86672fab147c476cb8e8b0950b6c4fd48b5b2014/docs/data-model-logical.svg" />
|
||||
|
||||
<a name="policies"></a> Policies
|
||||
--------------------------------
|
||||
|
||||
Policies are defined in OPA's native query language: Rego.
|
||||
|
||||
Rego is a declarative language based on [Datalog](https://en.wikipedia.org/wiki/Datalog). Rego allows policy writers to define modules which contain rules. Rules contain expressions which assert facts about the expected state of documents stored in OPA. The documents referenced in rules may be base documents pushed by applications integrated with OPA or virtual documents defined by other rules.
|
||||
|
||||
To support document-oriented models such as JSON, Rego has rich support for referencing nested documents (i.e., documents inside arrays or objects). The syntax for referencing nested documents is based on dictionary and array access in languages like Python as well as JSON Path.
|
||||
|
||||
Let's look at an example.
|
||||
|
||||
Assume we have the following data stored in OPA:
|
||||
|
||||
```
|
||||
GET /v1/data HTTP/1.1
|
||||
```
|
||||
|
||||
The response:
|
||||
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"servers": [
|
||||
{"id": "s1", "name": "app", "protocols": ["https", "ssh"], "ports": ["p1", "p2", "p3"]},
|
||||
{"id": "s2", "name": "db", "protocols": ["mysql"], "ports": ["p3"]},
|
||||
{"id": "s3", "name": "cache", "protocols": ["memcache", "http"], "ports": ["p3"]},
|
||||
{"id": "s4", "name": "dev", "protocols": ["http"], "ports": ["p1", "p2"]}
|
||||
],
|
||||
"networks": [
|
||||
{"id": "n1", "public": false},
|
||||
{"id": "n2", "public": false},
|
||||
{"id": "n3", "public": true}
|
||||
],
|
||||
"ports": [
|
||||
{"id": "p1", "networks": ["n1"]},
|
||||
{"id": "p2", "networks": ["n3"]},
|
||||
{"id": "p3", "networks": ["n2"]}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
We can write a rule which enumerates servers that expose HTTP (but not HTTPS) and are connected to public networks. These represent violations of policy.
|
||||
|
||||
```rego
|
||||
package opa.example # this policy belongs the opa.example package
|
||||
|
||||
import data.servers # import the data.servers document to refer to it as "servers" instead of "data.servers"
|
||||
import data.networks # same but for data.networks
|
||||
import data.ports # same but for data.ports
|
||||
|
||||
violations[server] :- # a server exists in the violations set if:
|
||||
server = servers[_], # the server exists in the servers collection
|
||||
server.protocols[_] = "http", # and the server has http in its protocols collection
|
||||
public_servers[server] # and the server exists in the public_servers set
|
||||
|
||||
public_servers[server] :- # a server exists in the public_servers set if:
|
||||
server = servers[_], # the server exists in the servers collection
|
||||
server.ports[_] = ports[i].id, # and the server is connected to a port in the ports collection
|
||||
ports[i].networks[_] = networks[j].id, # and the port is connected to a network in the networks collection
|
||||
networks[j].public = true # and the network is public
|
||||
```
|
||||
|
||||
The key aspects of Rego are illustrated by this example:
|
||||
|
||||
- Rules define the content of virtual documents. In this case, we create two virtual documents: `violations` and `public_servers`.
|
||||
|
||||
- Rules consist of assertions against data stored in OPA. In this case the assertions are expressions which test for equality and membership of `servers`, `networks`, and `ports` documents.
|
||||
|
||||
- Expressions can referenced nested documents, e.g., `ports[i].networks[_]` references the network IDs stored in an array on each port document.
|
||||
|
||||
- Expressions can reference elements in a collection using the `[_]` and `[<variable>]` syntax. When this is done, OPA knows to iterate over elements of the collection when processing queries.
|
||||
|
||||
If we query for the document produced by this rule, we receive an array of servers that expose an HTTP server and are connected to a public network:
|
||||
|
||||
```
|
||||
GET /v1/data/opa/examples/violations HTTP/1.1
|
||||
```
|
||||
|
||||
The response:
|
||||
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{"id": "s4", "name": "dev", "protocols": ["http"], "ports": ["p2"]}
|
||||
]
|
||||
```
|
||||
@@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 368.50393 170.07874" width="368.50393pt" height="170.07874pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.5 <dc:date>2016-03-18 04:17:26 +0000</dc:date></metadata><defs><font-face font-family="Helvetica Neue" font-size="16" panose-1="2 0 5 3 0 0 0 2 0 4" units-per-em="1000" underline-position="-100" underline-thickness="50" slope="0" x-height="517" cap-height="714" ascent="951.99585" descent="-212.99744" font-weight="500"><font-face-src><font-face-name name="HelveticaNeue"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_2" viewBox="-9 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M -8 0 L 0 3 L 0 -3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Deployment</title><rect fill="white" width="368.50393" height="170.07874"/><g><title>Layer 1</title><rect x="11.3385826" y="11.3385826" width="348.66142" height="150.23622" fill="white"/><rect x="11.3385826" y="11.3385826" width="348.66142" height="150.23622" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(16.338583 16.338583)" fill="black"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="0" y="15" textLength="43.84">server</tspan></text><rect x="25.511811" y="48.897638" width="93.543307" height="75.11811" fill="white"/><rect x="25.511811" y="48.897638" width="93.543307" height="75.11811" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(30.51181 77.232693)" fill="black"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="23.395653" y="15" textLength="36.752">opad</tspan></text><rect x="215.43307" y="34.451847" width="113.385826" height="89.5639" fill="white"/><rect x="215.43307" y="34.451847" width="113.385826" height="89.5639" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(220.43307 70.0098)" fill="black"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="36.140913" y="15" textLength="31.104">APP</tspan></text><rect x="223.937" y="43.664446" width="113.385826" height="89.5639" fill="white"/><rect x="223.937" y="43.664446" width="113.385826" height="89.5639" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(228.937 79.222397)" fill="black"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="36.140913" y="15" textLength="31.104">APP</tspan></text><rect x="232.44094" y="51.786796" width="113.385826" height="89.5639" fill="white"/><rect x="232.44094" y="51.786796" width="113.385826" height="89.5639" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(237.44094 78.120748)" fill="black"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="37.908913" y="15" textLength="27.568">app</tspan><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="23.244913" y="33.447998" textLength="14.816">pr</tspan><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x="37.772913" y="33.447998" textLength="42.368">ocess</tspan></text><path d="M 128.95499 86.74464 L 193.26363 87.071393 L 214.03823 87.39835" marker-end="url(#FilledArrow_Marker)" marker-start="url(#FilledArrow_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(139.475885 63.629574)" fill="black"><tspan font-family="Helvetica Neue" font-size="16" font-weight="500" x=".368" y="15" textLength="67.264">IPC (http)</tspan></text></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 4.3 KiB |
@@ -1,287 +0,0 @@
|
||||
---
|
||||
layout: docs
|
||||
title: Authorization
|
||||
section: examples
|
||||
sort_order: 1000
|
||||
---
|
||||
|
||||
Authorization
|
||||
-------------
|
||||
|
||||
This example helps you get started with OPA and shows how you can start policy enabling an existing
|
||||
app or service. This example introduces core concepts in OPA including the language used to define policies.
|
||||
|
||||
> Policy enabling an application decouples the policy implementation from the business logic so that administrators can define policy without changing the application while still keeping up with the size, complexity, and dynamic nature of modern applications.
|
||||
|
||||
In this example, we will write policies to authorize access to Docker. There are many examples of policies around access control. For demonstration purposes, we want to prevent the following:
|
||||
|
||||
- Containers with insecure configurations.
|
||||
- Users modifying the system without sufficient read+write access.
|
||||
|
||||
There are two key ideas that this example illustrates:
|
||||
|
||||
1. The policy definition is decoupled from the implementation of the app (in this case Docker). The administrator is empowered to define and manage policies without requiring changes to any of the apps.
|
||||
|
||||
1. Both the data relevant to policy and the policy definitions themselves can change rapidly.
|
||||
|
||||
Once you finish this example, you will be familiar with:
|
||||
|
||||
- Running OPA as a server/daemon.
|
||||
- Loading policy definitions and data via the REST APIs.
|
||||
- Querying data via the REST APIs.
|
||||
|
||||
You will also be introduced to the language used to write policies in OPA: [Rego](/docs/lang.html).
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
The example has been tested on the following platforms:
|
||||
|
||||
- Ubuntu 16.04 (64-bit)
|
||||
|
||||
If you are using a different distro, OS, or architecture the steps will be the same however there will be slight differences in the commands you need to run.
|
||||
|
||||
- This example requires Docker Engine 1.11 or newer to be installed on your machine.
|
||||
|
||||
- This examples requires that you have root or sudo access on your machine.
|
||||
|
||||
Steps
|
||||
=====
|
||||
|
||||
1. Create a directory that OPA can store policy definitions in:
|
||||
|
||||
mkdir -p policies
|
||||
|
||||
1. Download the latest version of OPA:
|
||||
|
||||
curl -L https://github.com/open-policy-agent/opa/releases/download/v0.1.0-rc3/opa_linux_amd64 > opa
|
||||
chmod u+x opa
|
||||
|
||||
1. Run OPA in server mode with logging enabled:
|
||||
|
||||
./opa run -s --alsologtostderr 1 --v 2 --policy-dir policies
|
||||
|
||||
OPA will run until it receives a signal to stop. Open another terminal to continue with the rest of the example.
|
||||
|
||||
1. Download the [open-policy-agent/docker-authz-plugin](https://github.com/open-policy-agent/docker-authz-plugin) executable from GitHub:
|
||||
|
||||
curl -L https://github.com/open-policy-agent/docker-authz-plugin/releases/download/v0.1.0-rc1/docker-authz-plugin_linux_amd64 > docker-authz-plugin
|
||||
chmod u+x docker-authz-plugin
|
||||
|
||||
The open-policy-agent/docker-authz-plugin repository hosts a small [Docker Authorization Plugin](https://docs.docker.com/engine/extend/plugins_authorization/).
|
||||
Docker's authorization plugin system allows an external process to receive all requests sent to the Docker daemon. The
|
||||
authorization plugin replies, instructing the Docker daemon to allow or reject the request.
|
||||
|
||||
1. Create an empty policy definition that will allow all requests:
|
||||
|
||||
cat >example.rego <<EOF
|
||||
package opa.example
|
||||
allow_request = true :- true
|
||||
EOF
|
||||
|
||||
This policy definition is about simple as it can be. The policy includes a single rule named allow_request that is defined
|
||||
to always be true. Once all of the components are running, we will come back and extend the policy.
|
||||
|
||||
1. Run the executable downloaded in the previous step and then open another terminal:
|
||||
|
||||
sudo ./docker-authz-plugin
|
||||
|
||||
> This step requires sudo access because the Docker plugin framework will attempt to update the Docker daemon configuration.
|
||||
If you run without sudo you may encounter a permission error.
|
||||
|
||||
1. <a name="step-6"></a>Update Docker's configuration to include the following command line argument:
|
||||
|
||||
--authorization-plugin=docker-authz-plugin
|
||||
|
||||
On Ubuntu 16.04 with systemd, this can be done as follows (requires root):
|
||||
|
||||
sudo mkdir -p /etc/systemd/system/docker.service.d
|
||||
sudo tee -a /etc/systemd/system/docker.service.d/override.conf > /dev/null <<EOF
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=/usr/bin/docker daemon -H fd:// --authorization-plugin=docker-authz-plugin
|
||||
EOF
|
||||
sudo systemctl daemon-reload
|
||||
sudo service docker restart
|
||||
|
||||
If you are using a different Linux distribution or you are not running systemd the step will be slightly different.
|
||||
|
||||
1. Run a simple Docker command to make sure everything is still working:
|
||||
|
||||
docker ps
|
||||
|
||||
If everything is setup correctly, the command should exit successfully. You can expect to see log messages
|
||||
from OPA and the plugin.
|
||||
|
||||
1. Test the policy definition is working by modifying it to **deny** all requests:
|
||||
|
||||
cat >example.rego <<EOF
|
||||
package opa.example
|
||||
allow_request = true :- false
|
||||
EOF
|
||||
|
||||
In OPA, rules defines the content of documents, e.g., objects, arrays, strings, booleans, etc.
|
||||
|
||||
In steps above, we created a rule named allow_request that defines a document that is the boolean value true.
|
||||
When all of the expressions on the body of the rule evaluate to true, we say the document is defined.
|
||||
If any of the expressions evaluate to false, we say the document is *undefined*.
|
||||
In this case, the document will always be undefined because the body of the rule is false.
|
||||
|
||||
docker ps
|
||||
|
||||
The output from the last command will be:
|
||||
|
||||
Error response from daemon: authorization denied by plugin docker-authz-plugin: request rejected by administrative policy
|
||||
|
||||
To learn more about how rules define the content of documents, see the [Data Model](/docs/arch.html#data-model) section in the architectural overview.
|
||||
|
||||
With this policy in place, users will not be able to run any Docker commands. Go ahead and try other commands such as `docker run` or `docker pull`,
|
||||
they will all be rejected. Let's change the policy so that it's a bit more useful.
|
||||
|
||||
1. Update the policy to reject requests with the unconfined [seccomp](https://en.wikipedia.org/wiki/Seccomp) profile:
|
||||
|
||||
cat >example.rego <<EOF
|
||||
package opa.example
|
||||
|
||||
import request as req
|
||||
|
||||
seccomp_unconfined :-
|
||||
# This expression asserts that the string on the right hand side exists
|
||||
# within the array SecurityOpt referenced on the left hand side.
|
||||
req.Body.HostConfig.SecurityOpt[_] = "seccomp:unconfined"
|
||||
|
||||
allow_request = true :- not seccomp_unconfined
|
||||
EOF
|
||||
|
||||
The plugin is watching the policy definition file for changes. Each time we change the file, the plugin reads the file and sends it to OPA. To manually send the policy to OPA, you can use the following API:
|
||||
|
||||
curl -X PUT --data-binary @example.rego \
|
||||
http://localhost:8181/v1/policies/example_policy
|
||||
|
||||
This API is idempotent so sending the policy multiple times is fine. Go ahead and try it yourself.
|
||||
|
||||
1. Test the policy is working by running a simple container:
|
||||
|
||||
docker run hello-world
|
||||
|
||||
Now try running the same container but disable seccomp (which should be prevented by the policy):
|
||||
|
||||
docker run --security-opt seccomp:unconfined hello-world
|
||||
|
||||
When Docker processes the run command it contacts the plugin to see if the request(s) should be allowed. The plugin takes the request and executes a query against OPA using the request as input data to the query. The same API call that the plugin makes can be executed using curl:
|
||||
|
||||
curl -v -G http://localhost:8181/v1/data/opa/example/allow_request \
|
||||
--data-urlencode \
|
||||
'global=request:{"Body":{"HostConfig":{"SecurityOpt":["seccomp:unconfined"]}}}'
|
||||
|
||||
Because the document generated by the allow_request rule is undefined in this case, OPA responds with a 404.
|
||||
|
||||
You can re-run the same query with the default seccomp profile and see that it succeeds:
|
||||
|
||||
curl -G http://localhost:8181/v1/data/opa/example/allow_request \
|
||||
--data-urlencode \
|
||||
'global=request:{"Body":{"HostConfig":{"SecurityOpt":["seccomp:default"]}}}'
|
||||
|
||||
Congratulations! You have successfully prevented containers from running without seccomp!
|
||||
|
||||
So far, the policy has been defined in terms of input data from the plugin. In many cases, it's necessary to write policies against multiple data sources.
|
||||
|
||||
The rest of the example shows how you can grant fine grained access to specific clients. To do so, we will insert fake user data into OPA to simulate an authentication system.
|
||||
|
||||
1. Create a configuration file to include an HTTP header in all of the requests sent to the Docker daemon. This header will identify the user:
|
||||
|
||||
mkdir -p ~/.docker
|
||||
|
||||
# Backup your existing Docker configuration, just in case. You can replace the configuration
|
||||
# which we generate below after you are done the example.
|
||||
cp ~/.docker/config.json ~/.docker/config.json~
|
||||
|
||||
cat >~/.docker/config.json <<EOF
|
||||
{
|
||||
"HttpHeaders": {
|
||||
"Authz-User": "bob"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
Currently, Docker does not provide a way to authenticate clients. In Docker 1.12, clients can be authenticated using TLS and there are plans to include other means of authentication. For the purpose of this example, we assume that an authentication system is place.
|
||||
|
||||
1. Add user data directly into OPA:
|
||||
|
||||
cat >users.json <<EOF
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/",
|
||||
"value": {
|
||||
"alice": {
|
||||
"readOnly": false
|
||||
},
|
||||
"bob": {
|
||||
"readOnly": true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
EOF
|
||||
|
||||
curl -X PATCH -d @users.json \
|
||||
http://localhost:8181/v1/data/users \
|
||||
-H "Content-Type: application/json"
|
||||
|
||||
This data represents information about users that could either come from an external system or be included in policy definitions.
|
||||
|
||||
To see that the user data has been added, we can query the Data API. This shows the properties associated with the user "alice":
|
||||
|
||||
curl http://localhost:8181/v1/data/users/alice
|
||||
|
||||
1. Update the policy definition to include basic user access controls:
|
||||
|
||||
cat >example.rego <<EOF
|
||||
package opa.example
|
||||
|
||||
import request as req
|
||||
import data.users
|
||||
|
||||
allow_request = true :- valid_user_role
|
||||
|
||||
# valid_user_role defines a document that is the boolean value true if this is
|
||||
# a write request and the user is allowed to perform writes.
|
||||
valid_user_role :-
|
||||
user_id = req.Headers["Authz-User"],
|
||||
user = users[user_id],
|
||||
user.readOnly = false
|
||||
|
||||
# valid_user_role is defined again here to handle read requests. When a rule
|
||||
# like this is defined multiple times, the rule definition must ensure that
|
||||
# only one instance evaluates successfully in a given query. If multiple
|
||||
# instances evaluated successfully, it indicates a conflict.
|
||||
valid_user_role :-
|
||||
user_id = req.Headers["Authz-User"],
|
||||
user = users[user_id],
|
||||
req.Method = "GET",
|
||||
user.readOnly = true
|
||||
EOF
|
||||
|
||||
In the new policy, the valid_user_role rules reference the "users" document created in the previous step.
|
||||
|
||||
1. Attempt to run a container. Because the configured user is "bob", the request is rejected:
|
||||
|
||||
docker run hello-world
|
||||
|
||||
1. Change the user to "alice" and re-run the container:
|
||||
|
||||
cat > ~/.docker/config.json <<EOF
|
||||
{
|
||||
"HttpHeaders": {
|
||||
"Authz-User": "alice"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
docker run hello-world
|
||||
|
||||
That's it!
|
||||
|
||||
Remember to undo the configuration changes from [Step 6](#step-6) and [Step 12](#step-12) after you are
|
||||
finished with the example.
|
||||
@@ -1,225 +0,0 @@
|
||||
---
|
||||
layout: docs
|
||||
title: REPL
|
||||
section: examples
|
||||
sort_order: 1001
|
||||
---
|
||||
|
||||
REPL
|
||||
----
|
||||
|
||||
This examples shows how to run OPA as an interactive shell or [REPL (read-eval-print loop)](https://en.wikipedia.org/wiki/Read–eval–print_loop).
|
||||
|
||||
REPLs are great for learning new languages and running quick experiments. You can take advantage of OPA's REPL to quickly
|
||||
run ad-hoc queries or prototype policies.
|
||||
|
||||
In this example, we will use the REPL to define rules to identify servers that violate a security policy: servers exposing (unencrypted) HTTP must not be connected to a public network.
|
||||
|
||||
Once you finish this example, you will be familiar with:
|
||||
|
||||
- Running OPA as an interactive shell/REPL.
|
||||
- Writing ad-hoc queries in [Rego](/docs/lang.html).
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
- This example requires that you have the latest version of OPA. You can download the latest version of OPA at [openpolicyagent.org](/index.html)
|
||||
|
||||
- This example assumes you have the OPA executable in your $PATH.
|
||||
|
||||
Steps
|
||||
=====
|
||||
|
||||
1. First, make sure you can run the REPL on your machine:
|
||||
|
||||
opa run
|
||||
|
||||
Without any data, you can experiment with simple expressions to get the hang of it:
|
||||
|
||||
> true
|
||||
true
|
||||
> 3.14
|
||||
3.14
|
||||
> ["hello", "world"]
|
||||
[
|
||||
"hello",
|
||||
"world"
|
||||
]
|
||||
|
||||
You can also test simple boolean expressions:
|
||||
|
||||
> true = false
|
||||
false
|
||||
> 3.14 > 3
|
||||
true
|
||||
> "hello" != "goodbye"
|
||||
true
|
||||
|
||||
Most REPLs let you define variables that you can reference later on. OPA allows you to do something similiar. For example, we can define a "pi" constant as follows:
|
||||
|
||||
> pi = 3.14
|
||||
|
||||
Once "pi" is defined, you query for the value and write expressions in terms of it:
|
||||
|
||||
> pi
|
||||
3.14
|
||||
> pi > 3
|
||||
true
|
||||
|
||||
One thing to watch out for in the REPL is that = is used both for assigning variables values and for testing the value of variables. For example p = q sometimes assigns p the value of q and sometimes checks if the values of p and q are the same. The REPL decides between assignment and test based on whether p already has a value or not. If p has a value, p = q is a test (returning true or false), and if p has no value p = q is an assignment. To unset a value for a variable, use the 'unset' command. (This ambiguity is only really an issue in the REPL--when writing policy the duality of = is actually beneficial.)
|
||||
|
||||
> pi = 3
|
||||
false
|
||||
> unset pi
|
||||
> pi = 3
|
||||
> pi
|
||||
3
|
||||
|
||||
In addition to running queries, the REPL also lets you define rules:
|
||||
|
||||
> p[x] :- a = [1,2,3,4], a[x]
|
||||
> p[x], x > 1
|
||||
+---+
|
||||
| x |
|
||||
+---+
|
||||
| 2 |
|
||||
| 3 |
|
||||
+---+
|
||||
|
||||
The rule above defines a set of values that are the indices of elements in the array "a".
|
||||
|
||||
When you enter expressions into the REPL, you are effectively running *queries* against OPA. The REPL output shows the values of variables in the expression that make the query **true**. If there is no set of variables that would make the query true, the REPL prints **false**. If there are no variables in the query and the query evaluates successfully, then the REPL just prints **true**.
|
||||
|
||||
Quit out of the REPL by pressing Control-C or typing "exit":
|
||||
|
||||
> exit
|
||||
Exiting
|
||||
|
||||
1. The REPL can also take policies and data as input. Let's define a bit of JSON data that will be used in the example:
|
||||
|
||||
cat >data.json <<EOF
|
||||
{
|
||||
"servers": [
|
||||
{"id": "s1", "name": "app", "protocols": ["https", "ssh"], "ports": ["p1", "p2", "p3"]},
|
||||
{"id": "s2", "name": "db", "protocols": ["mysql"], "ports": ["p3"]},
|
||||
{"id": "s3", "name": "cache", "protocols": ["memcache"], "ports": ["p3"]},
|
||||
{"id": "s4", "name": "dev", "protocols": ["http"], "ports": ["p1", "p2"]}
|
||||
],
|
||||
"networks": [
|
||||
{"id": "n1", "public": false},
|
||||
{"id": "n2", "public": false},
|
||||
{"id": "n3", "public": true}
|
||||
],
|
||||
"ports": [
|
||||
{"id": "p1", "networks": ["n1"]},
|
||||
{"id": "p2", "networks": ["n3"]},
|
||||
{"id": "p3", "networks": ["n2"]}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
Also, let's include a rule that defines a set of servers that are attached to public networks:
|
||||
|
||||
cat >example.rego <<EOF
|
||||
package opa.example
|
||||
|
||||
import data.servers
|
||||
import data.networks
|
||||
import data.ports
|
||||
|
||||
public_servers[s] :-
|
||||
s = servers[_],
|
||||
s.ports[_] = ports[i].id,
|
||||
ports[i].networks[_] = networks[j].id,
|
||||
networks[j].public = true
|
||||
EOF
|
||||
|
||||
1. Run the REPL with the two files as input:
|
||||
|
||||
opa run data.json example.rego
|
||||
|
||||
You can now run queries against the various documents:
|
||||
|
||||
> data.servers[_].id
|
||||
+--------------------+
|
||||
| data.servers[_].id |
|
||||
+--------------------+
|
||||
| "s1" |
|
||||
| "s2" |
|
||||
| "s3" |
|
||||
| "s4" |
|
||||
+--------------------+
|
||||
> data.opa.example.public_servers[x]
|
||||
+-------------------------------------------------------------------------------+
|
||||
| x |
|
||||
+-------------------------------------------------------------------------------+
|
||||
| {"id":"s1","name":"app","ports":["p1","p2","p3"],"protocols":["https","ssh"]} |
|
||||
| {"id":"s4","name":"dev","ports":["p1","p2"],"protocols":["http"]} |
|
||||
+-------------------------------------------------------------------------------+
|
||||
|
||||
One powerful thing about Rego and the REPL is that you can run queries using the same syntax that you would use to lookup values.
|
||||
|
||||
For example if `i` has value 0 then `data.servers[i]` returns the first value in the `data.servers` array:
|
||||
|
||||
> i = 0
|
||||
> data.servers[i]
|
||||
{
|
||||
"id": "s1",
|
||||
"name": "app",
|
||||
"ports": [
|
||||
"p1",
|
||||
"p2",
|
||||
"p3"
|
||||
],
|
||||
"protocols": [
|
||||
"https",
|
||||
"ssh"
|
||||
]
|
||||
}
|
||||
|
||||
That same expression `data.servers[i]` when `i` has no value defines a query that returns all the values of `i` and `data.servers[i]`:
|
||||
|
||||
> unset i
|
||||
> data.servers[i]
|
||||
+---+-------------------------------------------------------------------------------+
|
||||
| i | data.servers[i] |
|
||||
+---+-------------------------------------------------------------------------------+
|
||||
| 0 | {"id":"s1","name":"app","ports":["p1","p2","p3"],"protocols":["https","ssh"]} |
|
||||
| 1 | {"id":"s2","name":"db","ports":["p3"],"protocols":["mysql"]} |
|
||||
| 2 | {"id":"s3","name":"cache","ports":["p3"],"protocols":["memcache"]} |
|
||||
| 3 | {"id":"s4","name":"dev","ports":["p1","p2"],"protocols":["http"]} |
|
||||
+---+-------------------------------------------------------------------------------+
|
||||
|
||||
1. The REPL also understands the [Import and Package](/docs/lang.html#modules) directives.
|
||||
|
||||
> import data.servers
|
||||
> servers[i].ports[_] = "p2", servers[i].id = id
|
||||
+---+------+
|
||||
| i | id |
|
||||
+---+------+
|
||||
| 0 | "s1" |
|
||||
| 3 | "s4" |
|
||||
+---+------+
|
||||
> package opa.example
|
||||
> public_servers[x], x.protocols[_] = "http"
|
||||
+-------------------------------------------------------------------+
|
||||
| x |
|
||||
+-------------------------------------------------------------------+
|
||||
| {"id":"s4","name":"dev","ports":["p1","p2"],"protocols":["http"]} |
|
||||
+-------------------------------------------------------------------+
|
||||
|
||||
1. Finally, we can define a rule to identify servers in violation of our security policy:
|
||||
|
||||
> import data.servers
|
||||
> violations[s] :-
|
||||
s = servers[_],
|
||||
s.protocols[_] = "http",
|
||||
public_servers[s]
|
||||
> violations[server]
|
||||
+-------------------------------------------------------------------+
|
||||
| server |
|
||||
+-------------------------------------------------------------------+
|
||||
| {"id":"s4","name":"dev","ports":["p1","p2"],"protocols":["http"]} |
|
||||
+-------------------------------------------------------------------+
|
||||
|
||||
> The REPL will accept multi-line input. The REPL prompt will change to indicate that it's accepting multi-line input.
|
||||
@@ -1,48 +0,0 @@
|
||||
---
|
||||
layout: docs
|
||||
header_title: Docs
|
||||
title: Introduction
|
||||
section: docs
|
||||
show_in_header: true
|
||||
sort_order: 1
|
||||
---
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Policy defines expected behavior in response to specific events within an application.
|
||||
|
||||
There are many examples of functionality within applications which can benefit from rich policy control, e.g, API authorization, VM and container placement, auto-scaling, auto-healing, etc.
|
||||
|
||||
The approach to policy can vary greatly within and across applications. For example, policy is often written in natural language (English), stored in documents such as wiki pages, and enforced manually by the administrators of the the application.
|
||||
|
||||
When the cost of manually maintaining and enforcing policy is too high, policy is often defined in way that computers can understand: the application's implementation is updated to include the definition of policy and the means for enforcing it. Because most applications are implemented in **imperative languages** (such as Java or Python) the policy definition tends to focus on *how* the policy is enforced rather than *what* the expected behavior or state should be. This makes it hard for people unfamiliar with the application's implementation to know what the expected behavior or state should be. Furthermore, because policy is defined in the application's implementation, it is hard to adapt the policy without rebuilding, retesting, and redeploying the application. Deploy-time configuration may address this to some extent but is often incomplete or too course-grained.
|
||||
|
||||
A better approach is to define policy in a **declarative language** which can be understood by a computer. By using a declarative language, the policy definition can focus on *what* the expected behavior or state should be. This approach decouples policy from the application's implementation and lifecycle so that policy can be updated independently. When policy is defined this way, it can be readily reused in multiple systems. Finally, this approach to policy enables various features such as conflict detection, change tracking and access control independent from the application's source, sharing and re-use of policy, visualization, etc.
|
||||
|
||||
What is Policy Enabling?
|
||||
========================
|
||||
|
||||
Policy enabling an application decouples the policy implementation from the business logic so that administrators can define policy without changing the application while still keeping up with the size, complexity, and dynamic nature of modern applications.
|
||||
|
||||
Policy enabling an application involves providing policy statements to an engine and then integrating the application with the engine to provide answers to questions such as:
|
||||
|
||||
- Can a specific operation be performed?
|
||||
- What options are available for some operation?
|
||||
- What policy violations currently exist?
|
||||
|
||||
To answer questions like these, policies must be written in a declarative language and then compiled and executed by a policy engine that knows about the state of the world (which is relevant to policy).
|
||||
|
||||
What does OPA provide?
|
||||
======================
|
||||
|
||||
OPA provides an open source policy engine that simplifies the task of policy enabling applications.
|
||||
|
||||
OPA exposes several APIs to simplify integration:
|
||||
|
||||
- Query APIs with rich support for accessing stored data.
|
||||
- Simple CRUD APIs to manage policies and data.
|
||||
- Transactional APIs to operate on consistent snapshots of data.
|
||||
- Asynchronous APIs to register for notification when policy is violated.
|
||||
|
||||
OPA's policy engine supports a purpose built declarative language for policy. Developers do not have to design a policy language, build a compiler or interpreter, or implement other language analysis tools to policy enable their applications.
|
||||
@@ -1,89 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
---
|
||||
<div class="home">
|
||||
<p>
|
||||
The Open Policy Agent (OPA) is an open source project that helps <a class="page-link" href="/docs/intro.html">policy enable</a> your
|
||||
application. By integrating with OPA, your application's operators will be empowered to
|
||||
manage the size, complexity, and scale of modern deployments.
|
||||
</p>
|
||||
<h2>Get OPA</h2>
|
||||
Latest binary (Linux/64-bit):<br><br>
|
||||
<pre>
|
||||
curl -L https://github.com/open-policy-agent/opa/releases/download/v0.1.0-rc3/opa_linux_amd64 > opa
|
||||
chmod u+x opa
|
||||
./opa version
|
||||
</pre>
|
||||
Latest binary (Mac OSX/64-bit):<br><br>
|
||||
<pre>
|
||||
curl -L https://github.com/open-policy-agent/opa/releases/download/v0.1.0-rc3/opa_darwin_amd64 > opa
|
||||
chmod u+x opa
|
||||
./opa version
|
||||
</pre>
|
||||
From source (requires Go):<br><br>
|
||||
<pre>
|
||||
git clone https://github.com/open-policy-agent/opa.git $GOPATH/src/github.com/open-policy-agent/opa
|
||||
cd $GOPATH/src/github.com/open-policy-agent/opa
|
||||
make deps
|
||||
make
|
||||
./opa version
|
||||
</pre>
|
||||
<div class="features">
|
||||
<h2>Features</h2>
|
||||
<div class="features-grid">
|
||||
<div class="row feats">
|
||||
<div class="col feat1-1">
|
||||
<h3>Declarative Language</h3>
|
||||
<p>
|
||||
OPA features a purpose-built declarative language for describing the expected
|
||||
state of your application. Policies written in <a class="page-link" href="/docs/lang.html">Rego</a> are readily understood
|
||||
and easy to maintain.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col feat1-2">
|
||||
<h3>Zero Dependencies</h3>
|
||||
<p>
|
||||
There are zero deployment dependencies in OPA. Furthermore, OPA is designed
|
||||
around the fate-sharing philosophy for highly available systems.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col feat1-3">
|
||||
<h3>Language Agnostic</h3>
|
||||
<p>
|
||||
OPA exposes RESTful APIs for managing policy modules and state relevant to policy
|
||||
enforcement. The APIs produce and consume JSON data and are accessed via HTTP.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col feat2-1">
|
||||
<h3>Low Latency</h3>
|
||||
<p>
|
||||
OPA's policy engine is designed from scratch with latency sensitive applications in mind.
|
||||
OPA introduces as little latency as possible while still decoupling policy definitions
|
||||
from application business logic.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col feat2-2">
|
||||
<h3>Interactive Shell</h3>
|
||||
<p>
|
||||
OPA features an interactive shell that you can use to quickly experiment with
|
||||
queries and data sets.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col feat2-3">
|
||||
<h3>Embeddable</h3>
|
||||
<p>
|
||||
OPA is written in Go with the intention of being embeddable. Applications written
|
||||
in Go can use OPA as a library if needed.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="License">
|
||||
<h2>License</h2>
|
||||
<p>
|
||||
OPA is licensed under the Apache 2.0 open source license. OPA is under
|
||||
active development.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
.asset-cache
|
||||
.jekyll-metadata
|
||||
.sass-cache
|
||||
_site
|
||||
@@ -1,15 +1,17 @@
|
||||
# Site Updates
|
||||
|
||||
We use GitHub pages to host the website that includes all of the OPA documentation. In order
|
||||
to update the website, you need to have write permission on the open-policy-agent/opa repository.
|
||||
We use GitHub pages to host the website that includes all of the OPA documentation. In order to update the website, you need to have write permission on the open-policy-agent/opa repository.
|
||||
|
||||
You also need to have [Jekyll](http://jekyllrb.com) installed to build the site. If you are
|
||||
not sure how to install Jekyll, see the link for details. Assuming you have Ruby installed,
|
||||
all you should need to do is run:
|
||||
You also need to have [Jekyll](http://jekyllrb.com) installed to build the site. If you are not sure how to install Jekyll, see the link for details.
|
||||
|
||||
Assuming you have Ruby installed, all you should need to do is run:
|
||||
|
||||
```
|
||||
gem install --user-install jekyll
|
||||
gem install --user-install redcarpet
|
||||
gem install --user-install autoprefixer-rails
|
||||
gem install --user-install jekyll-assets
|
||||
gem install --user-install jekyll-contentblocks
|
||||
gem install --user-install jekyll-minifier
|
||||
```
|
||||
|
||||
To update the website perform the following steps:
|
||||
@@ -20,13 +22,12 @@ To update the website perform the following steps:
|
||||
git clone git@github.com:open-policy-agent/opa.git opa-site
|
||||
```
|
||||
|
||||
- Note: if you are preparing documentation for a specific release, checkout the release
|
||||
tag in this step as well.
|
||||
- Note: if you are preparing documentation for a specific release, checkout the release tag in this step as well.
|
||||
|
||||
1. Build the site content and save the output:
|
||||
|
||||
```
|
||||
cd opa-site/docs
|
||||
cd opa-site/site
|
||||
jekyll build .
|
||||
tar czvf ~/site.tar.gz -C _site .
|
||||
```
|
||||
|
After Width: | Height: | Size: 183 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 178 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" viewBox="0 0 250 250" fill="#FF9512" style="position:absolute;top:0;right:0"><path d="M0 0l115 115h15l12 27 108 108V0z" fill="#fff"/><path class="octo-arm" d="M128 109c-15-9-9-19-9-19 3-7 2-11 2-11-1-7 3-2 3-2 4 5 2 11 2 11-3 10 5 15 9 16" style="-webkit-transform-origin:130px 106px;transform-origin:130px 106px"/><path class="octo-body" d="M115 115s4 2 5 0l14-14c3-2 6-3 8-3-8-11-15-24 2-41 5-5 10-7 16-7 1-2 3-7 12-11 0 0 5 3 7 16 4 2 8 5 12 9s7 8 9 12c14 3 17 7 17 7-4 8-9 11-11 11 0 6-2 11-7 16-16 16-30 10-41 2 0 3-1 7-5 11l-12 11c-1 1 1 5 1 5z"/></svg>
|
||||
|
After Width: | Height: | Size: 623 B |
@@ -0,0 +1 @@
|
||||
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z" fill="#fff"/><path d="M0 0h24v24H0z" fill="none"/></svg>
|
||||
|
After Width: | Height: | Size: 192 B |
@@ -0,0 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><title>icon-github</title><g fill="none" fill-rule="evenodd"><path d="M0 0h16v16H0z"/><path d="M8 .43C3.713.43.24 3.906.24 8.193c0 3.428 2.222 6.337 5.306 7.363.388.07.53-.168.53-.374 0-.183-.007-.67-.01-1.32-2.16.47-2.614-1.04-2.614-1.04-.353-.895-.862-1.134-.862-1.134-.705-.48.053-.472.053-.472.78.055 1.19.8 1.19.8.69 1.186 1.815.843 2.257.645.07-.502.27-.843.493-1.037-1.723-.197-3.534-.862-3.534-3.836 0-.847.3-1.54.797-2.082-.08-.197-.347-.986.076-2.054 0 0 .652-.21 2.134.797.62-.173 1.282-.26 1.942-.262.66.003 1.322.09 1.942.26 1.482-1.003 2.132-.795 2.132-.795.423 1.067.157 1.856.077 2.053.498.542.8 1.235.8 2.082 0 2.98-1.815 3.637-3.544 3.83.28.24.527.712.527 1.436 0 1.04-.01 1.876-.01 2.13 0 .21.14.45.534.374 3.08-1.028 5.302-3.935 5.302-7.362C15.76 3.907 12.284.43 8 .43z" fill="#828282"/></g></svg>
|
||||
|
After Width: | Height: | Size: 900 B |
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Syntax highlighting styles
|
||||
*/
|
||||
.highlight {
|
||||
.c { color: #998; font-style: italic } // Comment
|
||||
.err { color: #a61717; background-color: #e3d2d2 } // Error
|
||||
.k { font-weight: bold } // Keyword
|
||||
.o { font-weight: bold } // Operator
|
||||
.cm { color: #998; font-style: italic } // Comment.Multiline
|
||||
.cp { color: #999; font-weight: bold } // Comment.Preproc
|
||||
.c1 { color: #998; font-style: italic } // Comment.Single
|
||||
.cs { color: #999; font-weight: bold; font-style: italic } // Comment.Special
|
||||
.gd { color: #000; background-color: #fdd } // Generic.Deleted
|
||||
.gd .x { color: #000; background-color: #faa } // Generic.Deleted.Specific
|
||||
.ge { font-style: italic } // Generic.Emph
|
||||
.gr { color: #a00 } // Generic.Error
|
||||
.gh { color: #999 } // Generic.Heading
|
||||
.gi { color: #000; background-color: #dfd } // Generic.Inserted
|
||||
.gi .x { color: #000; background-color: #afa } // Generic.Inserted.Specific
|
||||
.go { color: #888 } // Generic.Output
|
||||
.gp { color: #555 } // Generic.Prompt
|
||||
.gs { font-weight: bold } // Generic.Strong
|
||||
.gu { color: #aaa } // Generic.Subheading
|
||||
.gt { color: #a00 } // Generic.Traceback
|
||||
.kc { font-weight: bold } // Keyword.Constant
|
||||
.kd { font-weight: bold } // Keyword.Declaration
|
||||
.kp { font-weight: bold } // Keyword.Pseudo
|
||||
.kr { font-weight: bold } // Keyword.Reserved
|
||||
.kt { color: #458; font-weight: bold } // Keyword.Type
|
||||
.m { color: #099 } // Literal.Number
|
||||
.s { color: #d14 } // Literal.String
|
||||
.na { color: #008080 } // Name.Attribute
|
||||
.nb { color: #0086B3 } // Name.Builtin
|
||||
.nc { color: #458; font-weight: bold } // Name.Class
|
||||
.no { color: #008080 } // Name.Constant
|
||||
.ni { color: #800080 } // Name.Entity
|
||||
.ne { color: #900; font-weight: bold } // Name.Exception
|
||||
.nf { color: #900; font-weight: bold } // Name.Function
|
||||
.nn { color: #555 } // Name.Namespace
|
||||
.nt { color: #000080 } // Name.Tag
|
||||
.nv { color: #008080 } // Name.Variable
|
||||
.ow { font-weight: bold } // Operator.Word
|
||||
.w { color: #bbb } // Text.Whitespace
|
||||
.mf { color: #099 } // Literal.Number.Float
|
||||
.mh { color: #099 } // Literal.Number.Hex
|
||||
.mi { color: #099 } // Literal.Number.Integer
|
||||
.mo { color: #099 } // Literal.Number.Oct
|
||||
.sb { color: #d14 } // Literal.String.Backtick
|
||||
.sc { color: #d14 } // Literal.String.Char
|
||||
.sd { color: #d14 } // Literal.String.Doc
|
||||
.s2 { color: #d14 } // Literal.String.Double
|
||||
.se { color: #d14 } // Literal.String.Escape
|
||||
.sh { color: #d14 } // Literal.String.Heredoc
|
||||
.si { color: #d14 } // Literal.String.Interpol
|
||||
.sx { color: #d14 } // Literal.String.Other
|
||||
.sr { color: #009926 } // Literal.String.Regex
|
||||
.s1 { color: #d14 } // Literal.String.Single
|
||||
.ss { color: #990073 } // Literal.String.Symbol
|
||||
.bp { color: #999 } // Name.Builtin.Pseudo
|
||||
.vc { color: #008080 } // Name.Variable.Class
|
||||
.vg { color: #008080 } // Name.Variable.Global
|
||||
.vi { color: #008080 } // Name.Variable.Instance
|
||||
.il { color: #099 } // Literal.Number.Integer.Long
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
*,
|
||||
*:after,
|
||||
*:before {
|
||||
box-sizing: border-box;
|
||||
border: 0;
|
||||
font: inherit;
|
||||
font-size: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
*:after,
|
||||
*:before {
|
||||
content: none;
|
||||
}
|
||||
|
||||
address,
|
||||
article,
|
||||
aside,
|
||||
audio,
|
||||
body,
|
||||
blockquote,
|
||||
canvas,
|
||||
dd,
|
||||
details,
|
||||
dialog,
|
||||
div,
|
||||
dl,
|
||||
dt,
|
||||
fieldset,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
form,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
header,
|
||||
hgroup,
|
||||
hr,
|
||||
html,
|
||||
legend,
|
||||
li,
|
||||
main,
|
||||
menu,
|
||||
nav,
|
||||
noscript,
|
||||
ol,
|
||||
p,
|
||||
pre,
|
||||
section,
|
||||
summary,
|
||||
ul,
|
||||
video {
|
||||
display: block;
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
keygen,
|
||||
meter,
|
||||
progress,
|
||||
select,
|
||||
textarea, {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
html {
|
||||
text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
@@ -0,0 +1,563 @@
|
||||
@import "reset.scss";
|
||||
@import "highlight.scss";
|
||||
|
||||
@mixin clearfix {
|
||||
&:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
font-family: "Open Sans", "Helvetica Neue", "Liberation Sans", Arial, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
|
||||
&:link,
|
||||
&:visited {
|
||||
color: #09e;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #ff9512;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
code.highlighter-rouge {
|
||||
background-color: #f6f4f2;
|
||||
font-family: "Menlo", "DejaVu Mono", "Liberation Mono", "Courier New", monospace;
|
||||
font-size: 14px;
|
||||
margin: 0 2px;
|
||||
outline: solid 1px #ebe6e2;
|
||||
padding: 1px 2px;
|
||||
}
|
||||
|
||||
div.highlighter-rouge {
|
||||
background-color: #f6f4f2;
|
||||
border: solid 1px #ebe6e2;
|
||||
font-family: "Menlo", "DejaVu Mono", "Liberation Mono", "Courier New";
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
margin: 24px auto;
|
||||
max-width: 720px;
|
||||
padding: 24px;
|
||||
|
||||
& > .highlight {
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
|
||||
em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #000;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
margin: 40px 0 24px;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
margin: 20px 0 12px;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
margin: 12px auto;
|
||||
|
||||
& li {
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
display: list-item;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
margin: 0 0 0 60px;
|
||||
}
|
||||
}
|
||||
|
||||
ol li {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
ul li {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
p {
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-style: bold;
|
||||
}
|
||||
|
||||
.opa-nav-main {
|
||||
&--list {
|
||||
margin: 27px auto;
|
||||
max-width: 720px;
|
||||
text-align: right;
|
||||
@include clearfix;
|
||||
}
|
||||
|
||||
&--home-item {
|
||||
border-bottom: solid 2px transparent;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
|
||||
&--selected {
|
||||
border-bottom: solid 2px #444;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
&--item {
|
||||
border-bottom: solid 2px transparent;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
margin: 0 0 0 24px;
|
||||
|
||||
&--selected {
|
||||
border-bottom: solid 2px #444;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
margin: 0 0 0 24px;
|
||||
}
|
||||
}
|
||||
|
||||
&--link {
|
||||
text-decoration: none;
|
||||
|
||||
&:link,
|
||||
&:visited {
|
||||
color: rgba(0, 0, 0, 0.73);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #09e;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: #ff9512;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.opa-header {
|
||||
background-color: #ff9512;
|
||||
position: relative;
|
||||
@include clearfix;
|
||||
|
||||
&--fork-me {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
&--minor-title {
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
margin: 0 0 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&--title {
|
||||
color: #fff;
|
||||
font-size: 48px;
|
||||
line-height: 60px;
|
||||
margin: 0 0 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&--subtitle {
|
||||
color: rgba(255, 255, 255, 0.93);
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
margin: -10px 0 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&--text {
|
||||
color: rgba(255, 255, 255, 0.93);
|
||||
text-align: center;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&--link {
|
||||
border-bottom: dotted 1px;
|
||||
text-decoration: none;
|
||||
|
||||
&:link,
|
||||
&:visited {
|
||||
border-color: rgba(255, 255, 255, 0.93);
|
||||
color: rgba(255, 255, 255, 0.93);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: #fff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&:active {
|
||||
border-color: #444;
|
||||
color: #444;
|
||||
}
|
||||
}
|
||||
|
||||
&--download-list {
|
||||
margin: 48px auto 0;
|
||||
max-width: 720px;
|
||||
|
||||
& li {
|
||||
border: solid 2px rgba(255, 255, 255, 0.87);
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 130px;
|
||||
margin: 0 0 0 24px;
|
||||
position: relative;
|
||||
width: 224px;
|
||||
|
||||
&:first-child {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&::before {
|
||||
background-image: url(/assets/icon-download-white.svg);
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
content: "";
|
||||
display: block;
|
||||
height: 48px;
|
||||
left: 50%;
|
||||
opacity: 0.87;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 48px;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&--link {
|
||||
display: block;
|
||||
font-size: 20px;
|
||||
height: 100%;
|
||||
line-height: 72px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
|
||||
&:link,
|
||||
&:visited {
|
||||
color: rgba(255, 255, 255, 0.93);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: #444;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.opa-header--abstract {
|
||||
background-color: #ff9512;
|
||||
margin: 0 auto;
|
||||
max-width: 720px;
|
||||
position: relative;
|
||||
@include clearfix;
|
||||
|
||||
&--nav {
|
||||
bottom: 0;
|
||||
left: -10px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 224px;
|
||||
|
||||
&-list {
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
|
||||
&-item {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
line-height: 18px;
|
||||
padding: 0 0 0 10px;
|
||||
margin: 3px 0 15px;
|
||||
}
|
||||
|
||||
&-item--selected {
|
||||
border-left: solid 2px #fff;
|
||||
color: #fff;
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
line-height: 18px;
|
||||
padding: 0 0 0 8px;
|
||||
margin: 3px 0 15px;
|
||||
}
|
||||
|
||||
&-link {
|
||||
text-decoration: none;
|
||||
|
||||
&:link,
|
||||
&:visited {
|
||||
color: rgba(255, 255, 255, 0.80);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: #444;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > h1 {
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
margin: -4px 0 24px 248px;
|
||||
max-width: 472px;
|
||||
}
|
||||
|
||||
& > p {
|
||||
color: rgba(255, 255, 255, 0.93);
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
margin: 0 0 12px 248px;
|
||||
max-width: 472px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.opa-content {
|
||||
margin: 60px auto;
|
||||
max-width: 720px;
|
||||
@include clearfix;
|
||||
|
||||
.opa-header > & {
|
||||
margin: 0 auto 60px;
|
||||
padding: 60px 0 0;
|
||||
}
|
||||
|
||||
& + .opa-footer {
|
||||
margin: 80px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.opa-homepage--content {
|
||||
margin: 0 auto;
|
||||
max-width: 720px;
|
||||
}
|
||||
|
||||
.opa-homepage--section {
|
||||
&--experience,
|
||||
&--benefits,
|
||||
&--features {
|
||||
padding: 60px 0;
|
||||
}
|
||||
|
||||
&--experience {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
&--benefits {
|
||||
background-color: #9fdaed;
|
||||
}
|
||||
|
||||
&--features {
|
||||
background-color: #dded9f;
|
||||
}
|
||||
|
||||
&--title {
|
||||
color: #000;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
margin: 0 auto 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&--text {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&--infographic {
|
||||
margin: 60px auto 24px;
|
||||
max-width: 720px;
|
||||
}
|
||||
}
|
||||
|
||||
.opa-homepage--section--feature-list {
|
||||
max-width: 720px;
|
||||
margin: 16px auto 0;
|
||||
@include clearfix;
|
||||
|
||||
.opa-homepage--section--feature {
|
||||
float: left;
|
||||
max-width: 348px;
|
||||
|
||||
&:nth-child(2n+1) {
|
||||
margin-left: 0;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
&:nth-child(2n) {
|
||||
margin-left: 12px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&--title {
|
||||
color: #000;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
margin: 10px auto 10px;
|
||||
}
|
||||
|
||||
&--text {
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.opa-tip {
|
||||
background-color: rgba(255, 204, 0, 0.07);
|
||||
border: solid 1px rgba(255, 153, 0, 0.13);
|
||||
color: rgba(0, 0, 0, 0.73);
|
||||
font-size: 16px;
|
||||
font-style: italic;
|
||||
line-height: 24px;
|
||||
margin: 24px auto;
|
||||
max-width: 624px;
|
||||
padding: 12px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.opa-community--contact-list {
|
||||
margin: 48px auto 0;
|
||||
max-width: 720px;
|
||||
@include clearfix;
|
||||
|
||||
& li {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 116px;
|
||||
margin: 0 0 0 24px;
|
||||
position: relative;
|
||||
width: 224px;
|
||||
|
||||
&:first-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& + .opa-footer {
|
||||
margin: 80px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.opa-footer {
|
||||
background-color: #444;
|
||||
padding: 60px 0;
|
||||
|
||||
&--text {
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
margin: 0 auto 20px;
|
||||
max-width: 720px;
|
||||
}
|
||||
|
||||
&--github {
|
||||
&--ci {
|
||||
float: right;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&--icon {
|
||||
fill: rgba(255, 255, 255, 0.54);
|
||||
margin-right: 8px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
&--link {
|
||||
text-decoration: none;
|
||||
|
||||
&:link,
|
||||
&:visited {
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: #ff9512;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--link {
|
||||
text-decoration: none;
|
||||
|
||||
&:link,
|
||||
&:visited {
|
||||
color: #3cf;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: #ff9512;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.opa-keep-it-together {
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -8,16 +8,23 @@
|
||||
# Site settings
|
||||
title: Open Policy Agent
|
||||
email:
|
||||
description: >
|
||||
OPA: An open source project to policy enable any application.
|
||||
description: "OPA: An open source project to policy enable any application."
|
||||
baseurl: "" # the subpath of your site, e.g. /blog
|
||||
url: "http://openpolicyagent.org" # the base hostname & protocol for your site
|
||||
github_username: open-policy-agent
|
||||
github_repo: opa
|
||||
|
||||
# Build settings
|
||||
markdown: redcarpet
|
||||
redcarpet:
|
||||
extensions:
|
||||
- tables
|
||||
- no_intra_emphasis
|
||||
gems:
|
||||
- autoprefixer-rails
|
||||
- jekyll-assets
|
||||
- jekyll-contentblocks
|
||||
- jekyll-minifier
|
||||
assets:
|
||||
compress:
|
||||
css: true
|
||||
assets:
|
||||
- "*.png"
|
||||
- "*.svg"
|
||||
kramdown:
|
||||
auto_ids: false
|
||||
@@ -0,0 +1,9 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
|
||||
<title>{% if page.title %}{{ page.title | escape }} | OPA{% else %}{{ site.title | escape }}{% endif %}</title>
|
||||
<meta name="description" content="{{ site.description | strip }}">
|
||||
|
||||
{% stylesheet style.scss %}
|
||||
</head>
|
||||
@@ -0,0 +1,28 @@
|
||||
{% assign doc_what = false %}
|
||||
{% assign doc_work = false %}
|
||||
{% assign doc_rego = false %}
|
||||
|
||||
{% for page in site.pages %}
|
||||
{% case page.doc_id %}
|
||||
{% when 'WHAT_IS_POLICY_ENABLEMENT' %}
|
||||
{% assign doc_what = page %}
|
||||
{% when 'HOW_DOES_OPA_WORK' %}
|
||||
{% assign doc_work = page %}
|
||||
{% when 'HOW_DO_I_WRITE_POLICIES' %}
|
||||
{% assign doc_rego = page %}
|
||||
{% endcase %}
|
||||
{% endfor %}
|
||||
|
||||
<nav class="opa-header--abstract--nav">
|
||||
<ul class="opa-header--abstract--nav-list">
|
||||
<li class="opa-header--abstract--nav-item{% if page.doc_id == doc_what.doc_id %}--selected{% endif %}">
|
||||
{% if page.doc_id != doc_what.doc_id %}<a class="opa-header--abstract--nav-link" href="{{doc_what.url}}">{{ doc_what.title }}</a>{% else %}{{ doc_what.title }}{% endif %}
|
||||
</li>
|
||||
<li class="opa-header--abstract--nav-item{% if page.doc_id == doc_work.doc_id %}--selected{% endif %}">
|
||||
{% if page.doc_id != doc_work.doc_id %}<a class="opa-header--abstract--nav-link" href="{{doc_work.url}}">{{ doc_work.title }}</a>{% else %}{{ doc_work.title }}{% endif %}
|
||||
</li>
|
||||
<li class="opa-header--abstract--nav-item{% if page.doc_id == doc_rego.doc_id %}--selected{% endif %}">
|
||||
{% if page.doc_id != doc_rego.doc_id %}<a class="opa-header--abstract--nav-link" href="{{doc_rego.url}}">{{ doc_rego.title }}</a>{% else %}{{ doc_rego.title }}{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -0,0 +1,22 @@
|
||||
{% assign xmp_repl = false %}
|
||||
{% assign xmp_auth = false %}
|
||||
|
||||
{% for page in site.pages %}
|
||||
{% case page.xmp_id %}
|
||||
{% when 'WORKING_WITH_THE_OPA_REPL' %}
|
||||
{% assign xmp_repl = page %}
|
||||
{% when 'DOCKER_AUTHORIZATION' %}
|
||||
{% assign xmp_auth = page %}
|
||||
{% endcase %}
|
||||
{% endfor %}
|
||||
|
||||
<nav class="opa-header--abstract--nav">
|
||||
<ul class="opa-header--abstract--nav-list">
|
||||
<li class="opa-header--abstract--nav-item{% if page.xmp_id == xmp_repl.xmp_id %}--selected{% endif %}">
|
||||
{% if page.xmp_id != xmp_repl.xmp_id %}<a class="opa-header--abstract--nav-link" href="{{xmp_repl.url}}">{{ xmp_repl.title }}</a>{% else %}{{ xmp_repl.title }}{% endif %}
|
||||
</li>
|
||||
<li class="opa-header--abstract--nav-item{% if page.xmp_id == xmp_auth.xmp_id %}--selected{% endif %}">
|
||||
{% if page.xmp_id != xmp_auth.xmp_id %}<a class="opa-header--abstract--nav-link" href="{{xmp_auth.url}}">{{ xmp_auth.title }}</a>{% else %}{{ xmp_auth.title }}{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
{% include head.html %}
|
||||
<body>
|
||||
{% include nav-main.html %}
|
||||
|
||||
<header class="opa-header">
|
||||
<p class="opa-header--fork-me"><a href="https://github.com/open-policy-agent/opa">{% img '{{assets["github-corner-right.svg"].logical_path}}' alt:'Fork me on GitHub' %}</a></p>
|
||||
|
||||
<div class="opa-content">
|
||||
{% contentblock header %}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="opa-content">
|
||||
{% contentblock body %}
|
||||
</div>
|
||||
|
||||
{% include footer.html %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
{% include head.html %}
|
||||
<body>
|
||||
{% include nav-main.html %}
|
||||
|
||||
<header class="opa-header">
|
||||
<p class="opa-header--fork-me"><a href="https://github.com/open-policy-agent/opa">{% img '{{assets["github-corner-right.svg"].logical_path}}' alt:'Fork me on GitHub' %}</a></p>
|
||||
|
||||
<div class="opa-content">
|
||||
<div class="opa-header--abstract">
|
||||
{% include nav-documentation.html %}
|
||||
|
||||
{% contentblock header %}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="opa-content">
|
||||
{% contentblock body %}
|
||||
</div>
|
||||
|
||||
{% include footer.html %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
{% include head.html %}
|
||||
<body>
|
||||
{% include nav-main.html %}
|
||||
|
||||
<header class="opa-header">
|
||||
<p class="opa-header--fork-me"><a href="https://github.com/open-policy-agent/opa">{% img '{{assets["github-corner-right.svg"].logical_path}}' alt:'Fork me on GitHub' %}</a></p>
|
||||
|
||||
<div class="opa-content">
|
||||
<div class="opa-header--abstract">
|
||||
{% include nav-examples.html %}
|
||||
|
||||
{% contentblock header %}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="opa-content">
|
||||
{% contentblock body %}
|
||||
</div>
|
||||
|
||||
{% include footer.html %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
{% include head.html %}
|
||||
<body>
|
||||
{% include nav-main.html %}
|
||||
|
||||
<header class="opa-header">
|
||||
<div class="opa-content">
|
||||
<p class="opa-header--fork-me"><a href="https://github.com/open-policy-agent/opa">{% img '{{assets["github-corner-right.svg"].logical_path}}' alt:'Fork me on GitHub' %}</a></p>
|
||||
|
||||
{% contentblock header %}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="opa-homepage--section--experience">
|
||||
<div class="opa-homepage--content">
|
||||
{% contentblock experience %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="opa-homepage--section--benefits">
|
||||
<div class="opa-homepage--content">
|
||||
{% contentblock benefits %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="opa-homepage--section--features">
|
||||
<div class="opa-homepage--content">
|
||||
{% contentblock features %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% include footer.html %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
nav_id: MAIN_COMMUNITY
|
||||
layout: default
|
||||
|
||||
title: Community
|
||||
---
|
||||
|
||||
{% contentfor header %}
|
||||
|
||||
# Open Policy Agent
|
||||
{: .opa-header--title}
|
||||
|
||||
Under active development.
|
||||
{: .opa-header--subtitle}
|
||||
|
||||
Open Policy Agent (OPA) is an open source policy engine. Contributions are always welcome. <span class="opa-keep-it-together">For information about contributing, see [CONTRIBUTING.md](https://github.com/open-policy-agent/opa/blob/master/CONTRIBUTING.md){: .opa-header--link}.</span>
|
||||
{: .opa-header--text}
|
||||
|
||||
{% endcontentfor %}
|
||||
|
||||
{% contentfor body %}
|
||||
|
||||
* [{% img '{{assets["community-slack.svg"].logical_path}}' alt:'Slack: community chat' %}](https://openpolicyagent.slack.com/)
|
||||
* [{% img '{{assets["community-google-groups.svg"].logical_path}}' alt:'Google Groups: mailing list' %}](https://groups.google.com/forum/?hl=en#!forum/open-policy-agent)
|
||||
* [{% img '{{assets["community-github-issues.svg"].logical_path}}' alt:'GitHub Issues: bugs + features' %}](https://github.com/open-policy-agent/opa/issues)
|
||||
{: .opa-community--contact-list}
|
||||
|
||||
{% endcontentfor %}
|
||||
@@ -0,0 +1,512 @@
|
||||
---
|
||||
nav_id: MAIN_DOCUMENTATION
|
||||
doc_id: HOW_DOES_OPA_WORK
|
||||
layout: documentation
|
||||
|
||||
title: How Does OPA Work?
|
||||
---
|
||||
|
||||
{% contentfor header %}
|
||||
|
||||
# How Does OPA Work?
|
||||
|
||||
OPA is a full-featured policy engine that runs as a host-local daemon alongside your service. You can think of it as a concierge for your service who can answer detailed questions on behalf of your users to meet their specific needs.
|
||||
|
||||
{% endcontentfor %}
|
||||
|
||||
{% contentfor body %}
|
||||
|
||||
## Overview
|
||||
|
||||
OPA’s RESTful APIs use JSON over HTTP so you and your users can integrate OPA with any programming language. At a high level, integrating OPA into your service involves:
|
||||
|
||||
* Deploying OPA alongside your service
|
||||
* Pushing relevant data about your service’s state into OPA’s document store
|
||||
* Offloading some or all decision-making to OPA by querying it
|
||||
|
||||
When your service is integrated with OPA, your users will be able author and deploy custom policies that control the behavior of your service’s policy-enabled features. Furthermore, users can publish data to OPA that is not available to your service about their own deployment context.
|
||||
|
||||
In the future, both your service and its users will be able to register for, and react to, notifications triggered when OPA detects a policy-relevant change.
|
||||
|
||||
## Deployment
|
||||
|
||||
Unless you embed OPA as a Go library, you will deploy it alongside your service – either directly as an operating system daemon or inside a container. In this way, transactions will have low latency and availability will be determined through shared fate with your service.
|
||||
|
||||
When OPA starts for the first time, it will not contain any policies or data. Policies and data can be added, removed, and modified at any time. For example: by deployment automation software or your service as it is deployed, by your service during an upgrade, or by administrators as needed.
|
||||
|
||||
## Data and Policies
|
||||
|
||||
The primary unit of data in OPA is a document, which is similar to a JSON value. Documents typically correspond to single, self-contained objects and are capable of representing both primitive types (strings, numbers, booleans, and null) as well as structured types (objects, and arrays). Documents are created, read, updated, and deleted via OPA’s RESTful HTTP APIs.
|
||||
|
||||
{% img '{{assets["data-model-dependencies.svg"].logical_path}}' width:'720' %}
|
||||
|
||||
### Base Documents
|
||||
|
||||
So-called base documents contain static, structured data stored in memory and optionally saved to disk for resiliency. Your service will publish and update base documents in order to describe its current state, and your users can do the same to include relevant data about the state of their own deployment context.
|
||||
|
||||
Base documents are published and updated using OPA’s Data API. For example, the following request publishes a list of servers to OPA:
|
||||
|
||||
```http
|
||||
PATCH /v1/data/servers HTTP/1.1
|
||||
Content-Type: application/json-patch+json
|
||||
```
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "-",
|
||||
"value": {
|
||||
"id": "s1",
|
||||
"name": "app",
|
||||
"protocols": [
|
||||
"http",
|
||||
"https",
|
||||
"ssh"
|
||||
],
|
||||
"ports": [
|
||||
"p1",
|
||||
"p2",
|
||||
"p3"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"path": "-",
|
||||
"value": {
|
||||
"id": "s2",
|
||||
"name": "db",
|
||||
"protocols": [
|
||||
"mysql"
|
||||
],
|
||||
"ports": [
|
||||
"p3"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"path": "-",
|
||||
"value": {
|
||||
"id": "s3",
|
||||
"name": "cache",
|
||||
"protocols": [
|
||||
"memcache"
|
||||
],
|
||||
"ports": [
|
||||
"p3"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"path": "-",
|
||||
"value": {
|
||||
"id": "s4",
|
||||
"name": "dev",
|
||||
"protocols": [
|
||||
"http",
|
||||
"https",
|
||||
"ssh"
|
||||
],
|
||||
"ports": [
|
||||
"p1",
|
||||
"p2"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Policies
|
||||
|
||||
Policies are written using OPA’s purpose-built, declarative language Rego. Rego includes rich support for traversing nested documents and transforming data using syntax inspired by dictionary and array access in languages like Python and JSONPath. For detailed information about using Rego, see [How Do I Write Policies?](/documentation/how-do-i-write-policies).
|
||||
|
||||
Each Rego file defines a policy module using a collection of rules that describe the expected state of your service. Both your service and its users can publish and update policy modules using OPA’s Policy API. For example, the following request creates a policy with two rules (violations and public_servers) named “exempli-gratia”:
|
||||
|
||||
```http
|
||||
PUT https://example.com/v1/policies/exempli-gratia HTTP/1.1
|
||||
Content-Type: text/plain
|
||||
```
|
||||
|
||||
```ruby
|
||||
package opa.examples
|
||||
|
||||
import data.servers
|
||||
import data.networks
|
||||
import data.ports
|
||||
|
||||
violations[server] :-
|
||||
server = servers[_],
|
||||
server.protocols[_] = "http",
|
||||
public_servers[server] = true
|
||||
|
||||
public_servers[server] :-
|
||||
server = servers[_],
|
||||
server.ports[_] = ports[i].id,
|
||||
ports[i].networks[_] = networks[j].id,
|
||||
networks[j].public = true
|
||||
```
|
||||
|
||||
A policy file must contain a single package declaration, which defines the path to the policy module and its rules (for example, data.opa.examples.violations – see The data Document for more information about accessing nested documents). The policy name itself (in this case, “exempli-gratia”) is only used to identify policies for file management purposes; it is not used otherwise.
|
||||
|
||||
### Rules and Virtual Documents
|
||||
|
||||
In contrast to base documents, virtual documents embody the results of evaluating the rules included in policy modules. Virtual documents are computed when users publish new policy modules, update existing modules, run queries, and when any relevant base document is published or updated. Rules allow policy authors to write questions with yes-no answers (that is, predicates) and to generate structured values from raw data found in base documents as well as from intermediate data found in other virtual documents.
|
||||
|
||||
### The data Document
|
||||
|
||||
OPA nests all documents within a built-in root document named data. All documents, whether pushed by your service or computed by OPA as needed, are contained within the data document.
|
||||
|
||||
{% img '{{assets["data-model-logical.svg"].logical_path}}' width:'720' %}
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"servers": [...],
|
||||
"ports": [...],
|
||||
"networks": [...],
|
||||
"opa": {
|
||||
"examples": {
|
||||
"violations": [...],
|
||||
"public_servers": [...]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As a result, any document, base or virtual, can be accessed hierarchically starting from the root data node – either as an identifier:
|
||||
|
||||
```ruby
|
||||
import data.servers # Base document
|
||||
import data.opa.examples.violations # Virtual document
|
||||
```
|
||||
|
||||
or as a URI component in an HTTP request:
|
||||
|
||||
```http
|
||||
GET https://example.com/v1/data/servers HTTP/1.1
|
||||
```
|
||||
|
||||
```http
|
||||
GET https://example.com/v1/data/opa/examples/violations HTTP/1.1
|
||||
```
|
||||
|
||||
### Putting It All Together
|
||||
|
||||
Let’s take a look at some documents representing the state of a hypothetical service and a policy module that uses this data. The following documents describe a set of servers, the protocols they use, the ports they open, and the networks those ports are connected to.
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"servers": [
|
||||
{
|
||||
"id": "s1",
|
||||
"name": "app",
|
||||
"protocols": [
|
||||
"https",
|
||||
"ssh"
|
||||
],
|
||||
"ports": [
|
||||
"p1",
|
||||
"p2",
|
||||
"p3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "s2",
|
||||
"name": "db",
|
||||
"protocols": [
|
||||
"mysql"
|
||||
],
|
||||
"ports": [
|
||||
"p3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "s3",
|
||||
"name": "cache",
|
||||
"protocols": [
|
||||
"memcache",
|
||||
"http"
|
||||
],
|
||||
"ports": [
|
||||
"p3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "s4",
|
||||
"name": "dev",
|
||||
"protocols": [
|
||||
"http"
|
||||
],
|
||||
"ports": [
|
||||
"p1",
|
||||
"p2"
|
||||
]
|
||||
}
|
||||
],
|
||||
"networks": [
|
||||
{
|
||||
"id": "n1",
|
||||
"public": false
|
||||
},
|
||||
{
|
||||
"id": "n2",
|
||||
"public": false
|
||||
},
|
||||
{
|
||||
"id": "n3",
|
||||
"public": true
|
||||
}
|
||||
],
|
||||
"ports": [
|
||||
{
|
||||
"id": "p1",
|
||||
"networks": [
|
||||
"n1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "p2",
|
||||
"networks": [
|
||||
"n3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "p3",
|
||||
"networks": [
|
||||
"n2"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When the data is published, we can use OPA’s API to inspect base documents like servers:
|
||||
|
||||
```http
|
||||
GET https://example.com/v1/data/servers HTTP/1.1
|
||||
```
|
||||
|
||||
The response is an array of all servers:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "s1",
|
||||
"name": "app",
|
||||
"protocols": [
|
||||
"https",
|
||||
"ssh"
|
||||
],
|
||||
"ports": [
|
||||
"p1",
|
||||
"p2",
|
||||
"p3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "s2",
|
||||
"name": "db",
|
||||
"protocols": [
|
||||
"mysql"
|
||||
],
|
||||
"ports": [
|
||||
"p3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "s3",
|
||||
"name": "cache",
|
||||
"protocols": [
|
||||
"memcache",
|
||||
"http"
|
||||
],
|
||||
"ports": [
|
||||
"p3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "s4",
|
||||
"name": "dev",
|
||||
"protocols": [
|
||||
"http"
|
||||
],
|
||||
"ports": [
|
||||
"p1",
|
||||
"p2"
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Now let’s write a policy that enumerates servers that are connected to public networks and that are using HTTP. These servers are violating a business rule that states that all public servers must use HTTPS.
|
||||
|
||||
```ruby
|
||||
# This policy module belongs the opa.example package.
|
||||
package opa.examples
|
||||
|
||||
# Refer to data.servers as `servers`.
|
||||
import data.servers
|
||||
# Refer to the data.networks as `networks`.
|
||||
import data.networks
|
||||
# Refer to the data.ports as `ports`.
|
||||
import data.ports
|
||||
|
||||
# A server exists in the violations set if...
|
||||
violations[server] :-
|
||||
# ...the server exists
|
||||
server = servers[_],
|
||||
# ...and any of the server’s protocols is HTTP
|
||||
server.protocols[_] = "http",
|
||||
# ...and the server is public.
|
||||
public_servers[server] = true
|
||||
|
||||
# A server exists in the public_servers set if...
|
||||
public_servers[server] :-
|
||||
# ...the server exists
|
||||
server = servers[_],
|
||||
# ...and the server is connected to a port
|
||||
server.ports[_] = ports[i].id,
|
||||
# ...and the port is connected to a network
|
||||
ports[i].networks[_] = networks[j].id,
|
||||
# ...and the network is public.
|
||||
networks[j].public = true
|
||||
```
|
||||
|
||||
Note that:
|
||||
|
||||
* Rules consist of assertions about data stored in OPA. In this case, the assertions test for equality with, and membership of, values in the servers, networks, and ports documents.
|
||||
* Expressions can reference elements in a collection using the `[_]` and `[<variable>]` syntax. OPA knows to evaluate such queries by iterating over each element in the corresponding collection.
|
||||
* Assertions about elements in a collection are `true` if any of the elements match the expression, and are only `false` when none of the elements match. For example, `ports[i].networks[_] = networks[j].id` will be `true` whenever any element in `ports[i].networks` matches the id of any element in `networks`.
|
||||
* Expressions can reference nested documents. For `example,ports[i].networks[_]` refers to each network ID listed in each port document.
|
||||
* Expressions can reference virtual documents. For example, `public_servers[server] = true` matches only if `server` is in the list produced by the `public_servers` rule.
|
||||
|
||||
After publishing this policy module, data will include additional documents corresponding to the module’s package declaration (opa.examples) and the virtual documents its rules generate.
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"servers": [...],
|
||||
"networks": [...],
|
||||
"ports": [...],
|
||||
"opa": {
|
||||
"examples": {
|
||||
"violations": [
|
||||
{
|
||||
"id": "s4",
|
||||
"name": "dev",
|
||||
"protocols": [
|
||||
"http"
|
||||
],
|
||||
"ports": [
|
||||
"p1",
|
||||
"p2"
|
||||
]
|
||||
}
|
||||
],
|
||||
"public_servers": [
|
||||
{
|
||||
"id": "s1",
|
||||
"name": "app",
|
||||
"protocols": [
|
||||
"https",
|
||||
"ssh"
|
||||
],
|
||||
"ports": [
|
||||
"p1",
|
||||
"p2",
|
||||
"p3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "s4",
|
||||
"name": "dev",
|
||||
"protocols": [
|
||||
"http"
|
||||
],
|
||||
"ports": [
|
||||
"p1",
|
||||
"p2"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If we use OPA’s API to inspect the violations virtual document…
|
||||
|
||||
```http
|
||||
GET https://example.com/v1/data/opa/examples/violations HTTP/1.1
|
||||
```
|
||||
|
||||
…the response is the subset of the servers base document that use HTTP and are connected to a public network:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "s4",
|
||||
"name": "dev",
|
||||
"protocols": [
|
||||
"http"
|
||||
],
|
||||
"ports": [
|
||||
"p1",
|
||||
"p2"
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Future Features
|
||||
|
||||
OPA is under active development. The following features are planned but not yet implemented.
|
||||
|
||||
### Triggers
|
||||
|
||||
Your service and its users can register to be notified when the system exits the expected state so that violations can be remediated automatically.
|
||||
|
||||
Any rule can be used as the trigger for a notification. For example, let’s assume that you want to migrate containers to a new host if their current host shuts down. You need a rule to detect when there are containers assigned to hosts that are no longer running.
|
||||
|
||||
```ruby
|
||||
# All production containers are running if...
|
||||
containers_to_migrate[id] :-
|
||||
# ...the container exists
|
||||
container = containers[id],
|
||||
# ...and it is in production
|
||||
container.site.name = "prod",
|
||||
# ...and its host is running.
|
||||
container.host.state != "terminated"
|
||||
```
|
||||
|
||||
This rule produces a list of containers that should be migrated. You can register to observe this rule, and when the underlying data changes, OPA will re-evaluate it and trigger a notification. You can handle the event by re-deploying the containers in the resulting list to a running host.
|
||||
|
||||
### Transactions
|
||||
|
||||
OPA APIs support transactional operations. Either all of the operations within a transaction succeed or the whole transaction fails. For example, to deploy a new virtual machine, you might:
|
||||
|
||||
* Open a new transaction.
|
||||
* Query OPA for a list of hosts where the VM can be deployed.
|
||||
* Deploy the VM to host from the list.
|
||||
* Push data about the new deployment into OPA’s document store.
|
||||
* Close the transaction.
|
||||
|
||||
If any of steps 2–4 fail, so will the transaction.
|
||||
|
||||
### Debugging
|
||||
|
||||
To understand why queries return specific results, policy authors can obtain detailed explanations from OPA about its query processing.
|
||||
|
||||
{% endcontentfor %}
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
nav_id: MAIN_DOCUMENTATION
|
||||
doc_id: WHAT_IS_POLICY_ENABLEMENT
|
||||
layout: documentation
|
||||
|
||||
title: What is Policy Enablement?
|
||||
---
|
||||
|
||||
{% contentfor header %}
|
||||
|
||||
# What Is Policy Enablement?
|
||||
|
||||
A policy is a set of rules that governs the behavior of a service.
|
||||
|
||||
Policy-enablement empowers users to read, write, and manage these rules without needing specialized development or operational expertise.
|
||||
|
||||
When your users can implement policies without recompiling your source code, then your service is policy enabled.
|
||||
|
||||
{% endcontentfor %}
|
||||
|
||||
{% contentfor body %}
|
||||
|
||||
## Policies
|
||||
|
||||
All organizations have policies. Policies are essential to the long-term success of organizations because they encode important knowledge about how to comply with legal requirements, work within technical constraints, avoid repeating mistakes, and so on.
|
||||
|
||||
In their simplest form, policies can be applied manually based on rules that are written down or conventions that are unspoken but permeate an organization’s culture. Policies may also be enforced with application logic or statically configured at deploy time.
|
||||
|
||||
## Policy-Enabled Services
|
||||
|
||||
Policy-enabled services allow policies to be specified declaratively, updated at any time without recompiling or redeploying, and enforced automatically (which is especially valuable when decisions need to be made faster than humanly possible). They make deployments more adaptable to changing business requirements, improve the ability to discover violations and conflicts, increase the consistency of policy compliance, and mitigate the risk of human error.
|
||||
|
||||
A policy-enabled service is able to answer questions by comparing relevant input from its environment to policy statements written by administrators. For example, a cloud computing service could answer questions such as:
|
||||
|
||||
* Can I add compute capacity?
|
||||
* In what regions can I add compute capacity?
|
||||
* Which instances are currently running in the wrong region?
|
||||
|
||||
## Open Policy Agent
|
||||
|
||||
OPA is a lightweight, self-contained and extensible agent co-located with the your service. OPA simplifies the task of policy enabling your service by implementing a full-featured policy engine for you. It provides:
|
||||
|
||||
* Declarative policy authoring
|
||||
* Secure policy management
|
||||
* Interactive queries
|
||||
* Transactional data consistency
|
||||
* Asynchronous events
|
||||
|
||||
With OPA, you do not have to design a policy language, build a compiler or interpreter, or implement other language analysis tools to policy enable your service.
|
||||
|
||||
{% endcontentfor body %}
|
||||
@@ -0,0 +1,347 @@
|
||||
---
|
||||
sort_order: 1001
|
||||
nav_id: MAIN_EXAMPLES
|
||||
xmp_id: DOCKER_AUTHORIZATION
|
||||
layout: examples
|
||||
|
||||
title: Docker Authorization
|
||||
---
|
||||
|
||||
{% contentfor header %}
|
||||
# Docker Authorization
|
||||
|
||||
Docker’s out-of-the-box authorization model is all or nothing. But many users require finer-grained access control and Docker’s plugin infrastructure allows us to do so.
|
||||
|
||||
This is an excellent opportunity to see how to policy enable an existing service.
|
||||
|
||||
{% endcontentfor %}
|
||||
|
||||
{% contentfor body %}
|
||||
|
||||
## Goals
|
||||
|
||||
This example helps you get started with OPA and introduces you to core concepts in OPA, including Rego the language used to define policies.
|
||||
|
||||
> Policy enabling an application decouples the policy implementation from the business logic so that administrators can define policy without changing the application while still keeping up with the size, complexity, and dynamic nature of modern applications.
|
||||
{: .opa-tip}
|
||||
|
||||
Although there are a multitude of desirable access control policies, for demonstration purposes, we want to prevent the following:
|
||||
|
||||
* Containers with insecure configurations.
|
||||
* Users modifying the system without sufficient read+write access.
|
||||
|
||||
This example illustrates two key concepts:
|
||||
|
||||
1. OPA policy definition is decoupled from the implementation of the service (in this case Docker). The administrator is empowered to define and manage policies without requiring changes to any of the apps.
|
||||
2. Both the data relevant to policy and the policy definitions themselves can change rapidly.
|
||||
|
||||
Once you finish this example, you will be familiar with:
|
||||
|
||||
* Running OPA as a server/daemon.
|
||||
* Loading policy definitions and data via the REST APIs.
|
||||
* Querying data via the REST APIs.
|
||||
* The basics of [Rego](/documentation/how-do-i-write-policies/), OPA’s purpose-built policy language.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This example requires:
|
||||
|
||||
* Docker Engine 1.11 or newer
|
||||
* `root` or `sudo` access
|
||||
|
||||
The example has been tested on the following platforms:
|
||||
|
||||
* Ubuntu 16.04 (64-bit)
|
||||
|
||||
If you are using a different distro, OS, or architecture, the steps will be the same. However, there may be slight differences in the commands you need to run.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Create a directory for OPA policy defintions.
|
||||
|
||||
```shell
|
||||
$ mkdir -p policies
|
||||
```
|
||||
|
||||
### 2. Download the latest version of OPA.
|
||||
|
||||
```shell
|
||||
$ curl -L https://github.com/open-policy-agent/opa/releases/download/v0.1.0-rc3/opa_linux_amd64 > opa
|
||||
$ chmod u+x opa
|
||||
```
|
||||
|
||||
|
||||
### 3. Run OPA in server mode with logging enabled.
|
||||
|
||||
```shell
|
||||
$ opa run -s --alsologtostderr 1 --v 2 --policy-dir policies
|
||||
```
|
||||
|
||||
OPA will run until it receives a signal to stop. Open another terminal to continue with the rest of the example.
|
||||
|
||||
### 4. Download the [open-policy-agent/docker-authz-plugin](https://github.com/open-policy-agent/docker-authz-plugin) executable.
|
||||
|
||||
```shell
|
||||
$ curl -L https://github.com/open-policy-agent/docker-authz-plugin/releases/download/v0.1.0-rc1/docker-authz-plugin_linux_amd64 > docker-authz-plugin
|
||||
$ chmod u+x docker-authz-plugin
|
||||
```
|
||||
|
||||
The open-policy-agent/docker-authz-plugin repository hosts a small [Docker Authorization Plugin](https://docs.docker.com/engine/extend/plugins_authorization/). Docker's authorization plugin system allows an external process to receive all requests sent to the Docker daemon. The authorization plugin replies, instructing the Docker daemon to allow or reject the request.
|
||||
|
||||
### 5. Create an empty policy definition that will allow all requests.
|
||||
|
||||
```shell
|
||||
$ cat >example.rego <<EOF
|
||||
package opa.example
|
||||
allow_request = true :- true
|
||||
EOF
|
||||
```
|
||||
|
||||
This policy definition is about simple as it can be. It includes a single rule named `allow_request` that is defined to always be `true`. Once all of the components are running, we will come back and extend this policy.
|
||||
|
||||
### 6. Run the docker-authz-plugin and then open another terminal.
|
||||
|
||||
```shell
|
||||
$ sudo ./docker-authz-plugin
|
||||
```
|
||||
|
||||
> This step requires sudo access because the Docker plugin framework will attempt to update the Docker daemon configuration. If you run without sudo you may encounter a permission error.
|
||||
{: .opa-tip}
|
||||
|
||||
### <a name="reconfigure-docker"></a> 7. Reconfigure Docker.
|
||||
|
||||
Docker must include the following command-line argument:
|
||||
|
||||
```shell
|
||||
--authorization-plugin=docker-authz-plugin
|
||||
```
|
||||
|
||||
On Ubuntu 16.04 with systemd, this can be done as follows (requires root):
|
||||
|
||||
```shell
|
||||
$ sudo mkdir -p /etc/systemd/system/docker.service.d
|
||||
$ sudo tee -a /etc/systemd/system/docker.service.d/override.conf > /dev/null <<EOF
|
||||
[Service]
|
||||
ExecStart=/usr/bin/docker daemon -H fd:// --authorization-plugin=docker-authz-plugin
|
||||
EOF
|
||||
$ sudo systemctl daemon-reload
|
||||
$ sudo service docker restart
|
||||
```
|
||||
|
||||
If you are using a different Linux distribution or you are not running systemd, the process will be slightly different.
|
||||
|
||||
### 8. Run a simple Docker command to make sure everything is still working.
|
||||
|
||||
```shell
|
||||
$ docker ps
|
||||
```
|
||||
|
||||
If everything is setup correctly, the command should exit successfully. You can expect to see log messages from OPA and the plugin.
|
||||
|
||||
### 9. Test that the policy definition is working.
|
||||
|
||||
Let’s modify our policy to **deny** all requests:
|
||||
|
||||
```shell
|
||||
$ cat >example.rego <<EOF
|
||||
package opa.example
|
||||
allow_request = true :- false
|
||||
EOF
|
||||
```
|
||||
|
||||
In OPA, rules defines the content of documents (for example, objects, arrays, strings, booleans, and so on).
|
||||
|
||||
In steps above, we created a rule named `allow_request` that defines a document that is the boolean value `true`. When all of the expressions on the body of the rule evaluate to `true`, we say the document is defined. If any of the expressions evaluate to `false`, we say the document is *undefined*. In this case, the document will always be undefined because the body of the rule is `false`.
|
||||
|
||||
```shell
|
||||
$ docker ps
|
||||
```
|
||||
|
||||
The output should be:
|
||||
|
||||
```
|
||||
Error response from daemon: authorization denied by plugin docker-authz-plugin: request rejected by administrative policy
|
||||
```
|
||||
|
||||
To learn more about how rules define the content of documents, see: [How Does OPA Work?](/documentation/how-does-opa-work/)
|
||||
|
||||
With this policy in place, users will not be able to run any Docker commands. Go ahead and try other commands such as `docker run` or `docker pull`. They will all be rejected.
|
||||
|
||||
Now let's change the policy so that it's a bit more useful.
|
||||
|
||||
### 10. Update the policy to reject requests with the unconfined [seccomp](https://en.wikipedia.org/wiki/Seccomp) profile:
|
||||
|
||||
```shell
|
||||
$ cat >example.rego <<EOF
|
||||
package opa.example
|
||||
|
||||
import request as req
|
||||
|
||||
seccomp_unconfined :-
|
||||
# This expression asserts that the string on the right-hand side is equal
|
||||
# to an element in the array SecurityOpt referenced on the left-hand side.
|
||||
req.Body.HostConfig.SecurityOpt[_] = "seccomp:unconfined"
|
||||
|
||||
allow_request = true :- not seccomp_unconfined
|
||||
EOF
|
||||
```
|
||||
|
||||
The docker-authz-plugin is watching the policy definition file for changes. Each time we change the file, the plugin reads the file and sends it to OPA. To manually send the policy to OPA, you can use the following API:
|
||||
|
||||
```shell
|
||||
$ curl -X PUT --data-binary @example.rego http://localhost:8181/v1/policies/example_policy
|
||||
```
|
||||
|
||||
This API is idempotent so sending the policy multiple times is fine. Go ahead and try it yourself.
|
||||
|
||||
### 11. Test the policy is working by running a simple container:
|
||||
|
||||
```shell
|
||||
$ docker run hello-world
|
||||
```
|
||||
|
||||
Now try running the same container but disable seccomp (which should be prevented by the policy):
|
||||
|
||||
```shell
|
||||
$ docker run --security-opt seccomp:unconfined hello-world
|
||||
```
|
||||
|
||||
When Docker processes the run command, it contacts the plugin to see if the request should be allowed. The plugin takes the request and executes a query against OPA using the request as input data to the query. The same API call that the plugin makes can be executed using curl:
|
||||
|
||||
```shell
|
||||
$ curl -v -G http://localhost:8181/v1/data/opa/example/allow_request --data-urlencode 'global=request:{"Body":{"HostConfig":{"SecurityOpt":["seccomp:unconfined"]}}}'
|
||||
```
|
||||
|
||||
Because the document generated by the `allow_request` rule is undefined in this case, OPA responds with a 404.
|
||||
|
||||
You can re-run the same query with the default seccomp profile and see that it succeeds:
|
||||
|
||||
```shell
|
||||
$ curl -G http://localhost:8181/v1/data/opa/example/allow_request --data-urlencode 'global=request:{"Body":{"HostConfig":{"SecurityOpt":["seccomp:default"]}}}'
|
||||
```
|
||||
|
||||
Congratulations! You have successfully prevented containers from running without seccomp!
|
||||
|
||||
So far, the policy has been defined in terms of input data from the plugin. In many cases, it's necessary to write policies against multiple data sources.
|
||||
|
||||
The rest of the example shows how you can grant fine grained access to specific clients. To do so, we will insert fake user data into OPA to simulate an authentication system.
|
||||
|
||||
### <a name="identify-user"></a> 12. Identify the user in Docker requests.
|
||||
|
||||
> Back up your existing Docker configuration, just in case. You can replace your original configuration after you are done with the example.
|
||||
{: .opa-tip}
|
||||
|
||||
```shell
|
||||
$ mkdir -p ~/.docker
|
||||
$ cp ~/.docker/config.json ~/.docker/config.json~
|
||||
```
|
||||
|
||||
To identify the user, include an HTTP header in all of the requests sent to the Docker daemon:
|
||||
|
||||
```shell
|
||||
$ cat >~/.docker/config.json <<EOF
|
||||
{
|
||||
"HttpHeaders": {
|
||||
"Authz-User": "bob"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
Docker does not currently provide a way to authenticate clients. But in Docker 1.12, clients can be authenticated using TLS and there are plans to include other means of authentication. For the purpose of this example, we assume that an authentication system is place.
|
||||
|
||||
### 13. Add user data directly to OPA.
|
||||
|
||||
```shell
|
||||
$ cat >users.json <<EOF
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/",
|
||||
"value": {
|
||||
"alice": {
|
||||
"readOnly": false
|
||||
},
|
||||
"bob": {
|
||||
"readOnly": true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
EOF
|
||||
$ curl -X PATCH -d @users.json http://localhost:8181/v1/data/users -H "Content-Type: application/json"
|
||||
```
|
||||
|
||||
This data represents information about users that could either come from an external system or be included in policy definitions.
|
||||
|
||||
To see that the user data has been added, we can query the Data API. This shows the properties associated with the user "alice":
|
||||
|
||||
```shell
|
||||
$ curl http://localhost:8181/v1/data/users/alice
|
||||
```
|
||||
|
||||
### 14. Update the policy to include basic user access controls.
|
||||
|
||||
```shell
|
||||
$ cat >example.rego <<EOF
|
||||
package opa.example
|
||||
|
||||
import request as req
|
||||
import data.users
|
||||
|
||||
allow_request = true :- valid_user_role
|
||||
|
||||
# valid_user_role defines a document that is the boolean value true if this is
|
||||
# a write request and the user is allowed to perform writes.
|
||||
valid_user_role :-
|
||||
user_id = req.Headers["Authz-User"],
|
||||
user = users[user_id],
|
||||
user.readOnly = false
|
||||
|
||||
# valid_user_role is defined again here to handle read requests. When a rule
|
||||
# like this is defined multiple times, the rule definition must ensure that
|
||||
# only one instance evaluates successfully in a given query. If multiple
|
||||
# instances evaluated successfully, it indicates a conflict.
|
||||
valid_user_role :-
|
||||
user_id = req.Headers["Authz-User"],
|
||||
user = users[user_id],
|
||||
req.Method = "GET",
|
||||
user.readOnly = true
|
||||
EOF
|
||||
```
|
||||
|
||||
In the new policy, the valid_user_role rules reference the "users" document created in the previous step.
|
||||
|
||||
### 15. Attempt to run a container.
|
||||
|
||||
Because the configured user is `"bob"`, the request is rejected:
|
||||
|
||||
```shell
|
||||
$ docker run hello-world
|
||||
```
|
||||
|
||||
### 16. Change the user to "alice" and re-run the container.
|
||||
|
||||
```shell
|
||||
$ cat > ~/.docker/config.json <<EOF
|
||||
{
|
||||
"HttpHeaders": {
|
||||
"Authz-User": "alice"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
Because the configured user is `"alice"`, the request will succeed:
|
||||
|
||||
```shell
|
||||
$ docker run hello-world
|
||||
```
|
||||
|
||||
### 17. Restore your original configuration.
|
||||
|
||||
See: “[Reconfigure Docker.](#reconfigure-docker)” and “[Identify the user in Docker requests.](#identify-user)”.
|
||||
|
||||
That's it!
|
||||
|
||||
{% endcontentfor %}
|
||||
@@ -0,0 +1,270 @@
|
||||
---
|
||||
sort_order: 1000
|
||||
nav_id: MAIN_EXAMPLES
|
||||
xmp_id: WORKING_WITH_THE_OPA_REPL
|
||||
layout: examples
|
||||
|
||||
title: Working with the OPA REPL
|
||||
---
|
||||
|
||||
{% contentfor header %}
|
||||
|
||||
# Working with the OPA REPL
|
||||
|
||||
REPLs are great for learning new languages and running quick experiments. You can take advantage of OPA's REPL to quickly run ad-hoc queries or prototype policies.
|
||||
|
||||
{% endcontentfor %}
|
||||
|
||||
{% contentfor body %}
|
||||
|
||||
## OPA’s REPL
|
||||
|
||||
In this example, we will learn how to run OPA as an interactive shell or [REPL (read-eval-print loop)](https://en.wikipedia.org/wiki/Read–eval–print_loop). We will use the REPL to define rules to identify servers that violate the following security policy: “Servers that open an unencrypted HTTP port must not be connected to a public network.”
|
||||
|
||||
Once you finish this example, you will be familiar with:
|
||||
|
||||
* Running OPA as an interactive shell/REPL.
|
||||
* Writing ad-hoc queries in [Rego](/docs/lang.html).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* This example requires that you have the latest version of OPA. You can download the latest version of OPA at [openpolicyagent.org](/get-opa/).
|
||||
* This example assumes you have the OPA executable in your `$PATH`.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Make sure you can run the REPL on your machine.
|
||||
|
||||
```shell
|
||||
$ opa run
|
||||
```
|
||||
|
||||
Without any data, you can experiment with simple expressions to get the hang of it:
|
||||
|
||||
```ruby
|
||||
> true
|
||||
true
|
||||
> 3.14
|
||||
3.14
|
||||
> ["hello", "world"]
|
||||
[
|
||||
"hello",
|
||||
"world"
|
||||
]
|
||||
```
|
||||
|
||||
You can also test simple boolean expressions:
|
||||
|
||||
```ruby
|
||||
> true = false
|
||||
false
|
||||
> 3.14 > 3
|
||||
true
|
||||
> "hello" != "goodbye"
|
||||
true
|
||||
```
|
||||
|
||||
Most REPLs let you define variables that you can reference later on. OPA allows you to do something similar. For example, we can define a `pi` constant as follows:
|
||||
|
||||
```ruby
|
||||
> pi = 3.14
|
||||
```
|
||||
|
||||
Once "pi" is defined, you query for the value and write expressions in terms of it:
|
||||
|
||||
```ruby
|
||||
> pi
|
||||
3.14
|
||||
> pi > 3
|
||||
true
|
||||
```
|
||||
|
||||
One thing to watch out for in the REPL is that `=` is used both for assigning variables values and for testing the value of variables. For example `p = q` sometimes assigns `p` the value of `q` and sometimes checks if the values of `p` and `q` are the same. The REPL decides between assignment and test based on whether `p` already has a value or not. If `p` has a value, `p = q` is a test (returning `true` or `false`), and if `p` has no value `p = q` is an assignment. To unset a value for a variable, use the `unset` command. (This ambiguity is only really an issue in the REPL. When writing policy, the duality of `=` is actually beneficial.)
|
||||
|
||||
```ruby
|
||||
> pi = 3
|
||||
false
|
||||
> unset pi
|
||||
> pi = 3
|
||||
> pi
|
||||
3
|
||||
```
|
||||
|
||||
In addition to running queries, the REPL also lets you define rules:
|
||||
|
||||
```ruby
|
||||
> p[x] :- a = [1,2,3,4], a[x]
|
||||
> p[x], x > 1
|
||||
+---+
|
||||
| x |
|
||||
+---+
|
||||
| 2 |
|
||||
| 3 |
|
||||
+---+
|
||||
```
|
||||
|
||||
The rule above defines a set of values that are the indices of elements in the array `a`.
|
||||
|
||||
When you enter expressions into the OPA REPL, you are effectively running *queries*. The REPL output shows the values of variables in the expression that make the query `true`. If there is no set of variables that would make the query `true`, the REPL prints `false`. If there are no variables in the query and the query evaluates successfully, then the REPL just prints `true`.
|
||||
|
||||
Quit out of the REPL by pressing Control-C or typing `exit`:
|
||||
|
||||
```ruby
|
||||
> exit
|
||||
Exiting
|
||||
```
|
||||
|
||||
### 2. Create a data file and a policy module.
|
||||
|
||||
Let's define a bit of JSON data that will be used in the example:
|
||||
|
||||
```shell
|
||||
$ cat >data.json <<EOF
|
||||
{
|
||||
"servers": [
|
||||
{"id": "s1", "name": "app", "protocols": ["https", "ssh"], "ports": ["p1", "p2", "p3"]},
|
||||
{"id": "s2", "name": "db", "protocols": ["mysql"], "ports": ["p3"]},
|
||||
{"id": "s3", "name": "cache", "protocols": ["memcache"], "ports": ["p3"]},
|
||||
{"id": "s4", "name": "dev", "protocols": ["http"], "ports": ["p1", "p2"]}
|
||||
],
|
||||
"networks": [
|
||||
{"id": "n1", "public": false},
|
||||
{"id": "n2", "public": false},
|
||||
{"id": "n3", "public": true}
|
||||
],
|
||||
"ports": [
|
||||
{"id": "p1", "networks": ["n1"]},
|
||||
{"id": "p2", "networks": ["n3"]},
|
||||
{"id": "p3", "networks": ["n2"]}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
Also, let's include a rule that defines a set of servers that are attached to public networks:
|
||||
|
||||
```shell
|
||||
$ cat >example.rego <<EOF
|
||||
package opa.example
|
||||
|
||||
import data.servers
|
||||
import data.networks
|
||||
import data.ports
|
||||
|
||||
public_servers[s] :-
|
||||
s = servers[_],
|
||||
s.ports[_] = ports[i].id,
|
||||
ports[i].networks[_] = networks[j].id,
|
||||
networks[j].public = true
|
||||
EOF
|
||||
```
|
||||
|
||||
### 3. Run the REPL with the data file and policy module as input.
|
||||
|
||||
```shell
|
||||
$ opa run data.json example.rego
|
||||
```
|
||||
|
||||
You can now run queries against the various documents:
|
||||
|
||||
```ruby
|
||||
> data.servers[_].id
|
||||
+--------------------+
|
||||
| data.servers[_].id |
|
||||
+--------------------+
|
||||
| "s1" |
|
||||
| "s2" |
|
||||
| "s3" |
|
||||
| "s4" |
|
||||
+--------------------+
|
||||
> data.opa.example.public_servers[x]
|
||||
+-------------------------------------------------------------------------------+
|
||||
| x |
|
||||
+-------------------------------------------------------------------------------+
|
||||
| {"id":"s1","name":"app","ports":["p1","p2","p3"],"protocols":["https","ssh"]} |
|
||||
| {"id":"s4","name":"dev","ports":["p1","p2"],"protocols":["http"]} |
|
||||
+-------------------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
One powerful thing about Rego and the REPL is that you can run queries using the same syntax that you would use to lookup values.
|
||||
|
||||
For example if `i` has value `0` then `data.servers[i]` returns the first value in the `data.servers` array:
|
||||
|
||||
```ruby
|
||||
> i = 0
|
||||
> data.servers[i]
|
||||
{
|
||||
"id": "s1",
|
||||
"name": "app",
|
||||
"ports": [
|
||||
"p1",
|
||||
"p2",
|
||||
"p3"
|
||||
],
|
||||
"protocols": [
|
||||
"https",
|
||||
"ssh"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
That same expression `data.servers[i]` when `i` has no value defines a query that returns all the values of `i` and `data.servers[i]`:
|
||||
|
||||
```ruby
|
||||
> unset i
|
||||
> data.servers[i]
|
||||
+---+-------------------------------------------------------------------------------+
|
||||
| i | data.servers[i] |
|
||||
+---+-------------------------------------------------------------------------------+
|
||||
| 0 | {"id":"s1","name":"app","ports":["p1","p2","p3"],"protocols":["https","ssh"]} |
|
||||
| 1 | {"id":"s2","name":"db","ports":["p3"],"protocols":["mysql"]} |
|
||||
| 2 | {"id":"s3","name":"cache","ports":["p3"],"protocols":["memcache"]} |
|
||||
| 3 | {"id":"s4","name":"dev","ports":["p1","p2"],"protocols":["http"]} |
|
||||
+---+-------------------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
### 4. Import and export documents.
|
||||
|
||||
The REPL also understands the [Import and Package](/documentation/how-do-i-write-policies/#modules) directives.
|
||||
|
||||
```ruby
|
||||
> import data.servers
|
||||
> servers[i].ports[_] = "p2", servers[i].id = id
|
||||
+---+------+
|
||||
| i | id |
|
||||
+---+------+
|
||||
| 0 | "s1" |
|
||||
| 3 | "s4" |
|
||||
+---+------+
|
||||
```
|
||||
|
||||
```ruby
|
||||
> package opa.example
|
||||
> public_servers[x], x.protocols[_] = "http"
|
||||
+-------------------------------------------------------------------+
|
||||
| x |
|
||||
+-------------------------------------------------------------------+
|
||||
| {"id":"s4","name":"dev","ports":["p1","p2"],"protocols":["http"]} |
|
||||
+-------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
### 5. Define a rule to identify servers in violation of our security policy.
|
||||
|
||||
```ruby
|
||||
> import data.servers
|
||||
> violations[s] :-
|
||||
s = servers[_],
|
||||
s.protocols[_] = "http",
|
||||
public_servers[s]
|
||||
> violations[server]
|
||||
+-------------------------------------------------------------------+
|
||||
| server |
|
||||
+-------------------------------------------------------------------+
|
||||
| {"id":"s4","name":"dev","ports":["p1","p2"],"protocols":["http"]} |
|
||||
+-------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
> The REPL accepts multi-line input and will change appearance when it detects multi-line input.
|
||||
{: .opa-tip}
|
||||
|
||||
{% endcontentfor %}
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
nav_id: MAIN_GET_OPA
|
||||
layout: default
|
||||
|
||||
title: Get Open Policy Agent
|
||||
---
|
||||
|
||||
{% contentfor header %}
|
||||
|
||||
# Get Open Policy Agent
|
||||
{: .opa-header--minor-title}
|
||||
|
||||
OPA is available as binary releases for 64-bit Linux and Mac OS X or as Golang source code.
|
||||
{: .opa-header--text}
|
||||
|
||||
* [64-bit Linux](https://github.com/open-policy-agent/opa/releases/download/v0.1.0-rc3/opa_linux_amd64){: .opa-header--download-list--link}
|
||||
* [64-bit Mac OS X](https://github.com/open-policy-agent/opa/releases/download/v0.1.0-rc3/opa_darwin_amd64){: .opa-header--download-list--link}
|
||||
* [Go Source](https://github.com/open-policy-agent/opa/releases/download/v0.1.0-rc3/opa0.1rc3.src.tar.gz){: .opa-header--download-list--link}
|
||||
{: .opa-header--download-list}
|
||||
|
||||
{% endcontentfor %}
|
||||
|
||||
{% contentfor body %}
|
||||
|
||||
## 64-bit Linux
|
||||
|
||||
```shell
|
||||
$ curl -L https://github.com/open-policy-agent/opa/releases/download/v0.1.0-rc3/opa_linux_amd64 > opa
|
||||
$ chmod u+x opa
|
||||
$ ./opa version
|
||||
```
|
||||
|
||||
## 64-bit Mac OS X
|
||||
|
||||
```shell
|
||||
$ curl -L https://github.com/open-policy-agent/opa/releases/download/v0.1.0-rc3/opa_darwin_amd64 > opa
|
||||
$ chmod u+x opa
|
||||
$ ./opa version
|
||||
```
|
||||
|
||||
## Go Source
|
||||
|
||||
```shell
|
||||
$ git clone https://github.com/open-policy-agent/opa.git $GOPATH/src/github.com/open-policy-agent/opa
|
||||
$ cd $GOPATH/src/github.com/open-policy-agent/opa
|
||||
$ make deps
|
||||
$ make
|
||||
$ ./opa version
|
||||
```
|
||||
|
||||
{% endcontentfor %}
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
nav_id: MAIN_HOME
|
||||
layout: homepage
|
||||
|
||||
title: Open Policy Agent
|
||||
---
|
||||
|
||||
{% contentfor header %}
|
||||
|
||||
# Open Policy Agent
|
||||
{: .opa-header--title}
|
||||
|
||||
Decouple governance logic from application logic.
|
||||
{: .opa-header--subtitle}
|
||||
|
||||
Open Policy Agent (OPA) simplifies the task of policy enabling your service. OPA provides an extensible framework for declarative policy authoring, secure policy management, interactive queries, transactional data consistency, asynchronous events, and more.
|
||||
{: .opa-header--text}
|
||||
|
||||
So you can focus on other things.
|
||||
{: .opa-header--text}
|
||||
|
||||
{% endcontentfor %}
|
||||
|
||||
{% contentfor experience %}
|
||||
|
||||
## Deliver the experience you want your users to have.
|
||||
{: .opa-homepage--section--title}
|
||||
|
||||
Your users need deployments to comply with legal requirements, technical constraints, and their own team conventions. OPA policies are decoupled from your code so users can create and manage policies without modifying your service.
|
||||
{: .opa-homepage--section--text}
|
||||
|
||||
{% img '{{assets["experience.svg"].logical_path}}' alt:'With OPA, admins define how your service should behave using a declarative language. Attempts to use your service are posed as questions that OPA answers.' %}
|
||||
{: .opa-homepage--section--infographic}
|
||||
|
||||
{% endcontentfor %}
|
||||
|
||||
{% contentfor benefits %}
|
||||
|
||||
## Don’t roll your own policy engine.
|
||||
{: .opa-homepage--section--title}
|
||||
|
||||
Do you really have time and resources to devote to designing, implementing, testing, and maintaining a policy engine?
|
||||
{: .opa-homepage--section--text}
|
||||
|
||||
{% img '{{assets["benefits.svg"].logical_path}}' alt:'Without OPA, you need to implement policy management for your service from scratch. Required components must be carefully designed, implemented, and tested to ensure correct behavior and a positive user experience. That’s a lot of work. But OPA already includes everything you need in order to policy enable any service.' %}
|
||||
{: .opa-homepage--section--infographic}
|
||||
|
||||
{% endcontentfor %}
|
||||
|
||||
{% contentfor features %}
|
||||
|
||||
## Is OPA right for you?
|
||||
{: .opa-homepage--section--title}
|
||||
|
||||
OPA is purpose-built for modern deployment environments. OPA is…
|
||||
{: .opa-homepage--section--text}
|
||||
|
||||
<div class="opa-homepage--section--feature-list">
|
||||
<div class="opa-homepage--section--feature" markdown="1">
|
||||
|
||||
### Decoupled
|
||||
{: .opa-homepage--section--feature--title}
|
||||
|
||||
Users don’t change your source code. Instead, they write policies in an easy-to-use, declarative language developed especially for OPA.
|
||||
{: .opa-homepage--section--feature--text}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="opa-homepage--section--feature" markdown="1">
|
||||
|
||||
### Easy to Deploy
|
||||
{: .opa-homepage--section--feature--title}
|
||||
|
||||
OPA has zero deployment dependencies. It runs as a daemon side-by-side with your service and shares its fate for the purposes of high availabilty.
|
||||
{: .opa-homepage--section--feature--text}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="opa-homepage--section--feature" markdown="1">
|
||||
|
||||
### Compatible
|
||||
{: .opa-homepage--section--feature--title}
|
||||
|
||||
OPA’s RESTful APIs use JSON over HTTP so you can integrate OPA with your service no matter which programming language you use.
|
||||
{: .opa-homepage--section--feature--text}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="opa-homepage--section--feature" markdown="1">
|
||||
|
||||
### Responsive
|
||||
{: .opa-homepage--section--feature--title}
|
||||
|
||||
OPA is designed from scratch with latency-sensitive applications in mind, enforcing policies with minimal performance impact.
|
||||
{: .opa-homepage--section--feature--text}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="opa-homepage--section--feature" markdown="1">
|
||||
|
||||
### Interactive
|
||||
{: .opa-homepage--section--feature--title}
|
||||
|
||||
Anyone can use OPA’s interactive shell to quickly experiment with queries and data sets.
|
||||
{: .opa-homepage--section--feature--text}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="opa-homepage--section--feature" markdown="1">
|
||||
|
||||
### Embeddable
|
||||
{: .opa-homepage--section--feature--title}
|
||||
|
||||
Anyone can use OPA’s interactive shell to quickly experiment with queries and data sets.
|
||||
{: .opa-homepage--section--feature--text}
|
||||
|
||||
{% endcontentfor %}
|
||||