Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions api/envoy/config/filter/http/rbac/v2/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
load("//bazel:api_build_system.bzl", "api_proto_library")

licenses(["notice"]) # Apache 2

api_proto_library(
name = "rbac",
srcs = ["rbac.proto"],
deps = ["//envoy/config/rbac/v2alpha:rbac"],
)
29 changes: 29 additions & 0 deletions api/envoy/config/filter/http/rbac/v2/rbac.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
syntax = "proto3";

package envoy.config.filter.http.rbac.v2;
option go_package = "v2";

import "envoy/config/rbac/v2alpha/rbac.proto";

import "validate/validate.proto";
import "gogoproto/gogo.proto";

// [#protodoc-title: RBAC]
// Role-Based Access Control :ref:`configuration overview <config_http_filters_rbac>`.

message RBAC {
// Specify the RBAC rules to be applied globally
config.rbac.v2alpha.RBAC rules = 1 [(validate.rules).message.required = true];
}

message RBACPerRoute {
oneof override {
option (validate.required) = true;

// Disable the filter for this particular vhost or route.
bool disabled = 1 [(validate.rules).bool.const = true];

// Override the global configuration of the filter with this new config.
RBAC rbac = 2 [(validate.rules).message.required = true];
}
}
3 changes: 3 additions & 0 deletions api/envoy/config/rbac/v2alpha/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ load("//bazel:api_build_system.bzl", "api_proto_library", "api_go_proto_library"
api_proto_library(
name = "rbac",
srcs = ["rbac.proto"],
visibility = ["//visibility:public"],
deps = [
"//envoy/api/v2/core:address",
"//envoy/api/v2/route",
"//envoy/type:string_match",
],
)
Expand All @@ -16,6 +18,7 @@ api_go_proto_library(
proto = ":rbac",
deps = [
"//envoy/api/v2/core:address_go_proto",
"//envoy/api/v2/route:route_go_proto",
"//envoy/type:string_match_go_proto",
],
)
142 changes: 64 additions & 78 deletions api/envoy/config/rbac/v2alpha/rbac.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ syntax = "proto3";

import "validate/validate.proto";
import "envoy/api/v2/core/address.proto";
import "envoy/type/string_match.proto";
import "envoy/api/v2/route/route.proto";

package envoy.config.rbac.v2alpha;
option go_package = "v2alpha";
Expand All @@ -16,30 +16,36 @@ option go_package = "v2alpha";
// matching policy is found (suppose the `action` is ALLOW).
//
// Here is an example of RBAC configuration. It has two policies:
//
// * Service account "cluster.local/ns/default/sa/admin" has full access (empty permission entry
// means full access) to the service.
//
// * Any user (empty principal entry means any user) can read ("GET") the service at paths with
// prefix "/products" or suffix "/reviews" when request header "version" set to either "v1" or
// "v2".
//
// .. code-block:: yaml
//
// action: ALLOW
// policies:
// "service-admin":
// permissions:
// -
// - any: true
// principals:
// authenticated:
// name: "cluster.local/ns/default/sa/admin"
// - authenticated: { name: "cluster.local/ns/default/sa/admin" }
// - authenticated: { name: "cluster.local/ns/default/sa/superuser" }
// "product-viewer":
// permissions:
// - paths: [prefix: "/products", suffix: "/reviews"]
// methods: ["GET"]
// conditions:
// - header:
// key: "version"
// values: [simple: "v1", simple: "v2"]
// - and_rules:
// rules:
// - header: { name: ":method", exact_match: "GET" }
// - header: { name: ":path", regex_match: "/products(/.*)?" }
// - or_rules:
// rules:
// - destination_port: 80
// - destination_port: 443
// principals:
// -
// - any: true
//
message RBAC {
// Should we do white-list or black-list style access control.
Expand Down Expand Up @@ -68,90 +74,70 @@ message Policy {
repeated Principal principals = 2 [(validate.rules).repeated .min_items = 1];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that there is space between "repeated" and ".min_items"? Should it be removed?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The clang-format rules doesn't distinguish between the option name and the repeated decorator for a field. Unfortunately this has to remain but it's fortunately compatible with protoc

}

// Specifies how to match an entry in a map.
message MapEntryMatch {
// The key to select an entry from the map.
string key = 1;
// Permission defines a permission to access the service.
message Permission {

// A list of matched values.
repeated envoy.type.StringMatch values = 2;
}
message Set {
repeated Permission rules = 1 [(validate.rules).repeated .min_items = 1];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per offline convo can we add some more docs/explanation of what the AND/OR behavior is of this repeated lists? I think that would be helpful.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, these are just wrappers since you can't have a repeated field in a oneof. Adding a comment to specify that the application is dependent on whether it's in and_rules/ids vs or_rules/ids

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(But more comments here fine too)

}

// Specifies how to match IP addresses.
message IpMatch {
// IP addresses in CIDR notation.
repeated envoy.api.v2.core.CidrRange cidrs = 1;
}
oneof rule {
option (validate.required) = true;

// Specifies how to match ports.
message PortMatch {
// Port numbers.
repeated uint32 ports = 1;
}
// A set of rules that all must match in order to define the action.
Set and_rules = 1;

// Permission defines a permission to access the service.
message Permission {
// Optional. A list of HTTP paths or gRPC methods.
// gRPC methods must be presented as fully-qualified name in the form of
// packageName.serviceName/methodName.
// If this field is unset, it applies to any path.
repeated envoy.type.StringMatch paths = 1;

// Required. A list of HTTP methods (e.g., "GET", "POST").
// If this field is unset, it applies to any method.
repeated string methods = 2;

// Definition of a custom condition.
message Condition {
oneof condition_spec {
// Header match. This matches to the "request.http.headers" field in
// ":ref: `AttributeContext <envoy_api_msg_service.auth.v2alpha.AttributeContext>`.
// The map key is the header name. The header specifies how the service is accessed.
MapEntryMatch header = 1;

// Destination IP addresses.
IpMatch destination_ips = 2;

// Destination ports.
PortMatch destination_ports = 3;
}
}
// A set of rules where at least one must match in order to define the action.
Set or_rules = 2;

// Optional. Custom conditions.
repeated Condition conditions = 3;
// When any is set, it matches any action.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On this field (and the same one on principal) can we add a bit more docs explaining the usage (effectively that it's explicit and a policy needs to have at least one permission and principal).

bool any = 3 [(validate.rules).bool.const = true];

// A header (or psuedo-header such as :path or :method) on the incoming HTTP request.
envoy.api.v2.route.HeaderMatcher header = 4;

// A CIDR block that describes the destination IP.
envoy.api.v2.core.CidrRange destination_ip = 5;

// A port number that describes the destination port connecting to.
uint32 destination_port = 6 [(validate.rules).uint32.lte = 65535];
}
}

// Principal defines an identity or a group of identities.
message Principal {

message Set {
repeated Principal ids = 1 [(validate.rules).repeated .min_items = 1];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space between "repeated" and ".min_items"?

}

// Authentication attributes for principal. These could be filled out inside RBAC filter.
// Or if an authentication filter is used, they can be provided by the authentication filter.
message Authenticated {
// Optional. The name of the principal. This matches to the "source.principal" field in
// ":ref: `AttributeContext <envoy_api_msg_service.auth.v2alpha.AttributeContext>`.
// If unset, it applies to any user.
// The name of the principal. If set, the URI SAN is used from the certificate, otherwise the
// subject field is used. If unset, it applies to any user that is authenticated.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the "subject field"? Is "unset" of this field equivalent to "any" field set to "true"?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same behavior as in ext authz / AttributeContext, it uses the URI SAN if present, otherwise, it uses the subject field off the certificate.

if a principal has authenticated set with its name unset it means that the request must be authenticated, but does not care who the subject is.

string name = 1;
}

// Optional. Authenticated attributes that identify the principal.
Authenticated authenticated = 1;
oneof identifier {
option (validate.required) = true;

// Definition of a custom attribute to identify the principal.
message Attribute {
oneof attribute_spec {
// Source service name. This matches to the "source.service" field in
// ":ref: `AttributeContext <envoy_api_msg_service.auth.v2alpha.AttributeContext>`.
string service = 1;
// A set of identifiers that all must match in order to define the principal.
Set and_ids = 1;

// Source IP addresses.
IpMatch source_ips = 2;
// A set of identifiers at least one must match in order to define the principal.
Set or_ids = 2;

// Header match. This matches to the "request.http.headers" field in
// ":ref: `AttributeContext <envoy_api_msg_service.auth.v2alpha.AttributeContext>`.
// The map "key" is the header name. The header identifies the client.
MapEntryMatch header = 3;
}
}
// When any is set, it matches any principal.
bool any = 3 [(validate.rules).bool.const = true];

// Authenticated attributes that identify the principal.
Authenticated authenticated = 4;

// Optional. Custom attributes that identify the principal.
repeated Attribute attributes = 2;
// A CIDR block that describes the source IP.
envoy.api.v2.core.CidrRange source_ip = 5;

// A header (or psuedo-header such as :path or :method) on the incoming HTTP request.
envoy.api.v2.route.HeaderMatcher header = 6;
}
}